wkgeo-cli 0.2.2 → 0.2.4
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 +32 -23
- package/SKILL.md +43 -32
- package/lib/commands/auth.mjs +52 -59
- package/lib/commands/billing.mjs +36 -11
- package/lib/device.mjs +24 -4
- package/lib/login.mjs +372 -78
- package/lib/page.mjs +51 -54
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -87,7 +87,7 @@ cd cli && node scripts/build-exe.mjs --sea # 跑 Node SEA 的离线两步(
|
|
|
87
87
|
|
|
88
88
|
### 版本与升级策略
|
|
89
89
|
|
|
90
|
-
* 当前版本 **`0.
|
|
90
|
+
* 当前版本 **`0.2.3`**(2026-10-21:CI/非交互环境不再自动打开浏览器;稳定后升 `1.0.0`)。
|
|
91
91
|
* **同一个版本号不能覆盖**:改了代码必须 `npm version patch|minor|major` 再发布。
|
|
92
92
|
* AI 端统一用 **`npx -y wkgeo-cli@latest`** → 每次自动拿最新版;
|
|
93
93
|
**全局安装的用户不会自动升级**(要手动 `npm i -g wkgeo-cli@latest`)。
|
|
@@ -135,7 +135,7 @@ wkgeo login
|
|
|
135
135
|
|
|
136
136
|
会发生什么(**用户实际会看到的东西**):
|
|
137
137
|
|
|
138
|
-
1.
|
|
138
|
+
1. 终端打印一条授权网址(**人在终端时**会尽力自动打开浏览器;**CI/管道/AI 子进程只输出链接**,见下);
|
|
139
139
|
2. 浏览器里是**我们自己的登录页**:支持「账密登录 / 手机号登录 / 邮箱登录」三个页签
|
|
140
140
|
(手机号/邮箱是**验证码登录**,验证码由平台下发到已绑定的手机/邮箱);
|
|
141
141
|
3. 登录后进入**同意页**,显示应用名「官方 CLI」和将获得的权限,点「同意」;
|
|
@@ -155,8 +155,8 @@ wkgeo login
|
|
|
155
155
|
| 选项 | 说明 |
|
|
156
156
|
| --- | --- |
|
|
157
157
|
| `--device` | 用**设备码模式**(RFC 8628):不需要本地回调地址/端口;本地读不了回调时的兜底(见 §3.2) |
|
|
158
|
-
| `--no-browser` |
|
|
159
|
-
| `--open-browser` | 强制尝试打开系统浏览器(⚠️
|
|
158
|
+
| `--no-browser` | **不打开浏览器**(优先级最高,压过 `--open-browser`)。⚠️ 非交互/CI 环境本来就不会打开,这个开关主要用于终端/脚本显式表态 |
|
|
159
|
+
| `--open-browser` | 强制尝试打开系统浏览器(⚠️ **只在人类终端生效**;CI/非交互环境仍不打开;保留显式语义与兼容) |
|
|
160
160
|
| `--scope "site:read site:write billing:self"` | 申请的能力范围(默认就是这三个,够建站+下单+支付) |
|
|
161
161
|
| `--client-id <id>` | 换一个 OAuth 客户端(默认 `wkgeo-cli`) |
|
|
162
162
|
| `--callback-port <n>` | 本地回调端口(默认 53682;被占用会自动退到随机端口) |
|
|
@@ -172,20 +172,25 @@ wkgeo login
|
|
|
172
172
|
> `/admin/oauth/authorize`(页面显示「授权请求不完整」)。
|
|
173
173
|
> 全部失败才提示,且**只 warn**(`⚠️ 浏览器没能自动打开…` 后面紧跟**完整 URL** 供复制),**绝不中断**授权流程。
|
|
174
174
|
>
|
|
175
|
-
> ★
|
|
176
|
-
>
|
|
177
|
-
>
|
|
178
|
-
> *
|
|
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"` **已弃用**);
|
|
179
188
|
> * 打开成功 → `✅ 已为你打开浏览器,请在那里完成授权。`;打开失败 → ⚠️ 一段**含完整 URL** 的醒目提示
|
|
180
189
|
> (AI 场景再多一句"客户端支持内置浏览器也可以在那里打开"的**可选**提示,不是主路径);
|
|
181
|
-
> *
|
|
182
|
-
>
|
|
183
|
-
> * `--json` 的 `open_browser.skipped_reason` 现在只有 `"no_browser"` 或 `null` 两种
|
|
184
|
-
> (2026-10-01 那版的 `"non_tty_agent_mode"` **已弃用**);
|
|
185
|
-
> * 为什么走传统路线:**稳定**(各平台都有成熟的系统启动器)+ **`window.close()` 在独立浏览器里生效**
|
|
186
|
-
> (授权页/收银台完成后的那一屏能自己关掉);
|
|
190
|
+
> * 非交互时 `paste_text` 是「请打开以下链接完成授权:<url>」**而不是**「已为你打开浏览器」——
|
|
191
|
+
> 屏幕上什么都没弹,说"已打开"会让用户以为链接坏了;
|
|
187
192
|
> * ⚠️ **不弹浏览器 ≠ 失败**、**打开失败也 ≠ 失败**:地址照旧完整打印、流程照旧等回调/照旧返回 `pay_url`,
|
|
188
|
-
>
|
|
193
|
+
> 命令**绝不会**因此失败(回调 / 设备码的判定逻辑也完全不受影响)。
|
|
189
194
|
|
|
190
195
|
> ⚠️ 回调**只监听 `127.0.0.1`**(绝不 `0.0.0.0`);`state` 每次随机并在本地回调时**强校验**
|
|
191
196
|
> (服务端只负责原样回传,防 CSRF 是客户端自己的责任)。
|
|
@@ -410,7 +415,7 @@ wkgeo login --device-continue --json
|
|
|
410
415
|
| `import` | 老站导入(**默认 dry-run**;走本机运维脚本,需运维权限) | `--url`(必填)、`--apply`、`--site`、`--status`、`--out`、`--max-pages`、`--delay`、`--include`、`--exclude`、`--overwrite`、`--no-probe` |
|
|
411
416
|
| `wallet` | 余额 + 最近流水 | `--limit`、`--entitlements` |
|
|
412
417
|
| `wallet txns` | 钱包流水(普通用户自己的那份) | `--limit` |
|
|
413
|
-
| `wallet recharge` | **充值下单**:返 `code_url` + **`pay_url`(自家收银台页面,带订单支付令牌 `token`;★
|
|
418
|
+
| `wallet recharge` | **充值下单**:返 `code_url` + **`pay_url`(自家收银台页面,带订单支付令牌 `token`;★ 人类终端默认自动打开,CI/非交互只输出链接;**支付成功后页面会自己关掉**)**(**不会自动扣款**) | `--amount-cents`(必填,单位分)、`--no-browser`、`--open-browser`、`--dry-run` |
|
|
414
419
|
| `products list` | 能买什么(找 `--product` 该填什么) | — |
|
|
415
420
|
| `orders create` | 下单(**余额够就当场用钱包付清**) | `--product <代码>` + `--units <份数>`,或 `--quota <键>` + `--amount <数量>`;`--allow-wechat` |
|
|
416
421
|
| `orders check` | 查支付结果(微信侧已付则就地履约)+ 最新余额 | `<订单ID>` |
|
|
@@ -606,7 +611,7 @@ wkgeo publish # ③ 看状态(见下)
|
|
|
606
611
|
| --- | --- | --- |
|
|
607
612
|
| 1 | `wkgeo wallet`(记下当前余额,例如 ¥100.00) | 打印余额与流水 |
|
|
608
613
|
| 2 | `wkgeo wallet recharge --amount-cents 1000 --dry-run` | 只打印"将创建 ¥10.00 订单",**不产生任何订单** |
|
|
609
|
-
| 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/非交互只输出链接,不弹) |
|
|
610
615
|
| 4 | 在**收银台页面**用手机微信扫码支付 ¥10.00(人工) | 页面自动检测到支付成功;**支付成功后页面会自动尝试关闭**(关不掉就手动关掉即可);`pay_url` 里的订单号在任何设备/浏览器打开都能看到同一笔单 |
|
|
611
616
|
| 5 | `wkgeo orders check <order_id>` | `paid:true` + **最新余额 = 原余额 + 1000 分** |
|
|
612
617
|
| 6 | `wkgeo wallet txns` | 出现一条 `kind=recharge`、金额 `+¥10.00` 的流水 |
|
|
@@ -615,12 +620,14 @@ wkgeo publish # ③ 看状态(见下)
|
|
|
615
620
|
> **为什么是"打开我们网站的收银台"而不是"在终端画二维码"**:
|
|
616
621
|
> 老板明确否掉了"本地生成一个二维码页面让别人扫"的做法 —— 充值必须走**我们网上的充值页面**。
|
|
617
622
|
> 终端二维码只在"浏览器确实打不开"时作为**兜底**出现(文案会写明这一点)。
|
|
618
|
-
> `--no-browser`
|
|
619
|
-
> ★
|
|
620
|
-
>
|
|
621
|
-
>
|
|
622
|
-
>
|
|
623
|
-
>
|
|
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` 保留,但只在终端里生效(非交互仍不弹)。
|
|
624
631
|
>
|
|
625
632
|
> ★ **`pay_url` 上的 `token=`(2026-10-02 新增)**:服务端在**下单那一刻**把"这一单是谁的"
|
|
626
633
|
> 签成一把**只对这一单有效**的令牌(`createRechargeOrder` 返回的 `pay_token`),
|
|
@@ -728,6 +735,8 @@ node tencent/functions/_tests/oauth-device-live-e2e.js # 结
|
|
|
728
735
|
| `WKONGAI_LOGIN_TIMEOUT_MS` | `300000` | 等待授权超时(⚠️ 变量名里带 `MS`,所以这里填**毫秒**;命令行 `--timeout` 填的是**秒**) |
|
|
729
736
|
| `WKONGAI_CALLBACK_FIRST_WAIT_MS` | 未设置 | ★ **可选**的"首轮等待"(毫秒,**默认关闭**):只有显式设了它(或命令行 `--callback-first-wait`),这段时间没等到回调才会自动改用设备码继续(**本地回调监听会同时保留**);`0` 关闭(等价于 `--callback-first-wait 0`)。⚠️ **未设置时一律不启用**(0.2.2 起:等待超时不再自动改设备码;老板 2026-10-21:「默认的就是那个传统的授权方式」) |
|
|
730
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 |
|
|
731
740
|
| `NO_COLOR` | — | 设了就不上色 |
|
|
732
741
|
|
|
733
742
|
---
|
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、不设上限** —— 然后扫码支付;**不需要登录会员中心**。
|
|
@@ -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` |
|
|
@@ -232,17 +232,21 @@ npx -y wkgeo-cli@latest login --json --timeout 500ms # 也可以显式写毫
|
|
|
232
232
|
> 不要自己换算成毫秒。非法值(`--timeout 十分钟` / `--timeout 0`)直接报用法错误(退出码 `2`),
|
|
233
233
|
> 不会静默变成默认值。
|
|
234
234
|
|
|
235
|
-
> 🌐 **自动打开浏览器**(⚠️
|
|
236
|
-
>
|
|
237
|
-
>
|
|
238
|
-
>
|
|
239
|
-
>
|
|
240
|
-
>
|
|
241
|
-
>
|
|
242
|
-
>
|
|
243
|
-
>
|
|
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 里写它也不弹)。
|
|
244
248
|
> 所以 AI **不需要**自己判断"该不该开浏览器",但**永远要**把 `authorization_url` 原文转述给用户
|
|
245
|
-
>
|
|
249
|
+
> (CI/非交互环境里,这一步就是**唯一**的交付方式)。
|
|
246
250
|
|
|
247
251
|
**AI 的正确做法**:
|
|
248
252
|
|
|
@@ -250,6 +254,8 @@ npx -y wkgeo-cli@latest login --json --timeout 500ms # 也可以显式写毫
|
|
|
250
254
|
2. 读到 `stage:"awaiting_callback"` 后:
|
|
251
255
|
* **先看 `open_browser`**:`opened:true` = CLI 已经把系统浏览器弹出来了 → 把 `paste_text`
|
|
252
256
|
**原样转述给用户**("已为你打开浏览器,请在那里完成授权…")即可,**不要**自己再开一遍;
|
|
257
|
+
* `skipped_reason:"non_interactive"`(CI / 管道 / AI 子进程)→ **CLI 没弹、也不该弹**:
|
|
258
|
+
把 `authorization_url` / `paste_text` 清楚地转给有浏览器的用户;本机收不到回调时改用 `--device`;
|
|
253
259
|
* `opened:false`(打开失败)→ 把那段**含完整地址**的提示念给用户,或**可选**地
|
|
254
260
|
在你自己客户端的内置浏览器里打开 `authorization_url`(见 §5.5,这只是备选);
|
|
255
261
|
* `skipped_reason:"no_browser"` → 用户/调用方明确不要弹窗,那就**只**把完整网址清楚地给用户;
|
|
@@ -412,17 +418,17 @@ npx -y wkgeo-cli@latest login --device-continue --json # 用户说"点好
|
|
|
412
418
|
|
|
413
419
|
### 5.5 打开授权页 / 收银台(**给 AI 端**)
|
|
414
420
|
|
|
415
|
-
> ★ **2026-10
|
|
416
|
-
>
|
|
417
|
-
>
|
|
418
|
-
> 而是**看 `open_browser` 字段 + 把 `paste_text` 念给用户**:
|
|
421
|
+
> ★ **2026-10-21 起:人类终端会自动弹系统浏览器;CI/非交互环境只输出链接**(老板真机反馈:
|
|
422
|
+
> 「别人在 CI 里执行 `wkgeo-cli` 的 login,默认弹出了系统默认浏览器」)。所以你的第一动作**不是**
|
|
423
|
+
> 去开内置浏览器,而是**看 `open_browser` 字段 + 把 `paste_text` 念给用户**:
|
|
419
424
|
|
|
420
425
|
| `open_browser` | 你该做什么 |
|
|
421
426
|
| --- | --- |
|
|
422
427
|
| `opened: true` | ✅ CLI 已经把浏览器弹出来了 → 把 `paste_text` **原样**念给用户即可,**不要**再自己开一遍 |
|
|
423
|
-
| `
|
|
428
|
+
| `skipped_reason: "non_interactive"` | 🖥️ 这是 CI / 管道 / AI 子进程:CLI **没弹**(也不该弹)→ 把 `authorization_url` / `paste_text` 清楚地交给有浏览器的用户;本机收不到回调就用 `login --device` |
|
|
429
|
+
| `opened: false`(`attempted: true`) | ⚠️ 真的尝试过但没能自动打开 → 把那段**含完整地址**的提示念给用户(或可选地在你的内置浏览器里打开,见下) |
|
|
424
430
|
| `skipped_reason: "no_browser"` | 调用方/用户明确不要弹窗 → **只**把完整网址清楚地给用户,不要自己开浏览器 |
|
|
425
|
-
| `skipped_reason: null` | 真的尝试过打开(成没成看 `opened`)——
|
|
431
|
+
| `skipped_reason: null` | 真的尝试过打开(成没成看 `opened`)—— 人类终端的默认情况 |
|
|
426
432
|
|
|
427
433
|
**兜底手段(可选,**只在 CLI 没打开成功**时用)**——按 ① → ② → ③ 判断:
|
|
428
434
|
|
|
@@ -432,8 +438,9 @@ npx -y wkgeo-cli@latest login --device-continue --json # 用户说"点好
|
|
|
432
438
|
③ 都不行 → 把**完整地址**交给用户复制(说清动作是"复制到浏览器地址栏打开")
|
|
433
439
|
```
|
|
434
440
|
|
|
435
|
-
* ⚠️
|
|
436
|
-
|
|
441
|
+
* ⚠️ **这只是备选**:人类终端里 CLI 已经自动弹系统浏览器了,**不要**再叠一层"请在右侧…"的话术 ——
|
|
442
|
+
那会让用户不知道该看哪个窗口。**CI / 非交互环境**里 CLI 不会弹(`skipped_reason:"non_interactive"`),
|
|
443
|
+
这时把链接清楚地交给用户才是主路径。
|
|
437
444
|
* ① **原生就支持**:直接打开,**不需要装任何东西**;已经原生支持"应用内侧边栏"的客户端
|
|
438
445
|
(DSH 自带"点链接进右栏"),**让用户点一下链接**也算数。
|
|
439
446
|
* ② **不支持 → 装桥接插件**(DSH 的 `dsh-browser-open`):
|
|
@@ -461,18 +468,21 @@ Set-Content -LiteralPath "$env:USERPROFILE\.dsh\browser-open.txt" -Value "<完
|
|
|
461
468
|
|
|
462
469
|
| 运行场景 | CLI 会不会自动弹**系统**浏览器 |
|
|
463
470
|
| --- | --- |
|
|
464
|
-
|
|
|
465
|
-
|
|
|
466
|
-
| 加了 `--
|
|
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` | ✅ 只在**终端**里会(⚠️ 压不过非交互判定;别依赖它去"开启") |
|
|
467
475
|
|
|
468
476
|
* **`--json` 里的结构化字段 `open_browser`**:`{attempted, opened, tried, url, skipped_reason}`
|
|
469
|
-
(结构与取值口径**没有变**)—— `skipped_reason`
|
|
477
|
+
(结构与取值口径**没有变**)—— `skipped_reason` 有三种取值:
|
|
470
478
|
* `null` = 真的尝试过打开(成没成看 `opened`);
|
|
479
|
+
* `"non_interactive"` = CI / 管道 / AI 子进程,**没弹**(也不该弹);
|
|
471
480
|
* `"no_browser"` = 调用方/用户明确要求别打开。
|
|
472
481
|
* ⚠️ 历史取值 `"non_tty_agent_mode"`(2026-10-01 那版"AI 场景默认不弹")**已弃用,不会再出现**
|
|
473
482
|
—— 看到它说明你在读旧文档/旧版本的输出。
|
|
474
483
|
* 同一条事件/数据里还有 **`paste_text`**(`login` 的 `awaiting_callback` 事件、`wallet recharge` 的 `data`):
|
|
475
|
-
|
|
484
|
+
**原样念给用户**,不要自己改写。⚠️ 非交互时它是「请打开以下链接完成授权:<url>」,
|
|
485
|
+
**不会**说"已为你打开浏览器"(因为确实没弹)——照念即可,别自己补一句"已打开"。
|
|
476
486
|
* ⚠️ **不弹浏览器 ≠ 失败**,**打开失败也 ≠ 失败**:流程照旧等回调、照旧返回 `pay_url`;
|
|
477
487
|
CLI **绝不会**因为"没打开浏览器"而让命令失败 —— 你只要把地址送到用户眼前就对了。
|
|
478
488
|
|
|
@@ -552,9 +562,10 @@ npx -y wkgeo-cli@latest wallet recharge --amount-cents 10000 # 生
|
|
|
552
562
|
# (老板:"以那个 token 为基础来进行支付,而不是还要他用用户名那一套")。
|
|
553
563
|
# ⚠️ 老服务端没有这个字段时 `pay_url` 就只有 `order_id`,那条路仍然要求网页登录。
|
|
554
564
|
# ⚠️ **原样打开 data.pay_url**,不要自己拆参/重拼(尾斜杠与编码都有讲究)。
|
|
555
|
-
# ★ 打开方式见 §5.5
|
|
565
|
+
# ★ 打开方式见 §5.5:**人类终端**里 CLI 会尝试弹系统浏览器;**CI/非交互**只输出链接,
|
|
556
566
|
# 你只要看 data.open_browser:opened:true → 把 data.paste_text(已含完整地址 +
|
|
557
|
-
# "已为你打开浏览器…")原样念给用户即可;
|
|
567
|
+
# "已为你打开浏览器…")原样念给用户即可;skipped_reason:"non_interactive" → CLI 没弹,
|
|
568
|
+
# 把 data.pay_url 交给用户;opened:false → 念那段**含完整地址**的提示,
|
|
558
569
|
# 或可选地在自己的内置浏览器里打开 data.pay_url。
|
|
559
570
|
# ⚠️ `data.open_browser.skipped_reason = "no_browser"` = 调用方明确要求别弹(那就只给地址)。
|
|
560
571
|
# ★ **支付成功后那个页面会自己关掉**(收银台会尝试 `window.close()`):
|
|
@@ -572,10 +583,10 @@ npx -y wkgeo-cli@latest orders check <order_id> # 用
|
|
|
572
583
|
**原样打开**即可,**不要**把 `token` 单独摘出来展示、更不要放进对话正文里当"验证码"念给用户;
|
|
573
584
|
* 它**只**授权"读这一单的支付参数":读不到钱包、读不到订单列表、不能下单、不能取消,
|
|
574
585
|
而且 30 分钟(= 订单有效期)后自动失效(失效时页面会明确说"支付链接已过期,请重新下单")。
|
|
575
|
-
⚠️
|
|
576
|
-
⚠️ `--open-browser`
|
|
577
|
-
⚠️ 打开 `pay_url` 的方式见 **§5.5
|
|
578
|
-
|
|
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
|
+
只有它**没打开成功**(或非交互环境)时,才考虑可选地在客户端**内置浏览器 / 侧边面板**里打开。
|
|
579
590
|
|
|
580
591
|
#### 7.2.1 另一种:**让用户自己选金额**(`wallet recharge-link`,独立充值页)
|
|
581
592
|
|
|
@@ -592,7 +603,7 @@ npx -y wkgeo-cli@latest wallet recharge-link # 默认不弹浏览器
|
|
|
592
603
|
# ★ 有效期只有 10 分钟、且**用掉即失效**(一次性):过期/已用过时页面会明确提示"请重新获取",
|
|
593
604
|
# 这时**重新跑一次本命令**拿一条新链接即可(不要反复让用户刷新旧链接)。
|
|
594
605
|
# ★ 默认**不**打开浏览器(这条命令通常跑在服务器/容器里,且交接物是链接);
|
|
595
|
-
#
|
|
606
|
+
# 确实要在**人类终端**里弹出时加 --open-browser(CI/非交互仍不弹);--no-browser 优先级最高。
|
|
596
607
|
# ★ ticket 只是"建一笔充值单"的窄凭证:⛔ 不要把它当登录凭据、不要拿它去调别的 action。
|
|
597
608
|
npx -y wkgeo-cli@latest orders check <order_id> # 用户说付完了再查(order_id 从他那句"付完了"对应的单拿)
|
|
598
609
|
```
|
package/lib/commands/auth.mjs
CHANGED
|
@@ -70,10 +70,11 @@ export function resolveAutoDevice(flags = {}, env = {}) {
|
|
|
70
70
|
* ⇒ 本函数**不再**按"有没有代理"给默认值:用户没显式指定时**一律返回 0**。
|
|
71
71
|
* 也就是说 `wkgeo login` 默认会一路等回调到 `--timeout`,**不会**因为等太久自动改设备码。
|
|
72
72
|
*
|
|
73
|
-
*
|
|
73
|
+
* 设备码只在这两种情况出现(见 loginCommand.run):
|
|
74
74
|
* 1. 用户显式 `--device` / `--no-wait` / `--device-continue`;
|
|
75
|
-
* 2. 检测到系统代理且判定回调不可用(`--no-auto-device`
|
|
76
|
-
*
|
|
75
|
+
* 2. 检测到系统代理且判定回调不可用(`--no-auto-device` 可关)。
|
|
76
|
+
* ⚠️ 0.2.4 起:**本地回调端口起不来不再自动改设备码**(老板反馈"服务没起来却换了模式"),
|
|
77
|
+
* 而是如实报 `kind:"listen"` 并在 hint 里明确建议用户显式加 `--device`。
|
|
77
78
|
*
|
|
78
79
|
* ## 保留的显式开关(向后兼容,不参与默认判断)
|
|
79
80
|
* `--callback-first-wait <时长>`(默认单位**秒**;`0` / `off` / `none` = 关闭)
|
|
@@ -192,9 +193,7 @@ function bothPathsFailedError(deviceError, reason, { loginTimeoutMs }) {
|
|
|
192
193
|
const callbackSide =
|
|
193
194
|
reason === 'callback_timeout'
|
|
194
195
|
? `等待授权超时(${loginTimeoutMs}ms,本机回调监听当时仍然开着)`
|
|
195
|
-
:
|
|
196
|
-
? '本地回调端口起不来(根本没起监听)'
|
|
197
|
-
: '首轮等待没收到回调(本机回调监听当时仍然开着)';
|
|
196
|
+
: '首轮等待没收到回调(本机回调监听当时仍然开着)';
|
|
198
197
|
return new CliError(`本地回调没成功,改用设备码也失败了:${deviceError.message}`, {
|
|
199
198
|
kind: deviceError.kind || 'device_authorization_failed',
|
|
200
199
|
code: deviceError.code === undefined ? null : deviceError.code,
|
|
@@ -216,28 +215,28 @@ const TERMINAL_DEVICE_KINDS = new Set(['denied', 'expired', 'not_implemented', '
|
|
|
216
215
|
|
|
217
216
|
export const loginCommand = {
|
|
218
217
|
name: 'login',
|
|
219
|
-
summary: '浏览器授权一次,把 30
|
|
218
|
+
summary: '浏览器授权一次,把 30 天令牌存到本地(默认本地回调模式;设备码仅在显式 --device / 检测到系统代理时使用)',
|
|
220
219
|
usage:
|
|
221
220
|
'wkgeo login [--scope "site:read site:write billing:self"] [--no-browser] [--open-browser] [--client-id <id>] [--callback-port <n>] [--timeout <秒>]\n' +
|
|
222
221
|
'wkgeo login --device # 设备码模式:不需要回调地址/监听端口,给用户一个短码(兜底路径)\n' +
|
|
223
222
|
'wkgeo login --device --no-wait # ★ 只申请短码就退出(AI 端友好),稍后用下面这条领令牌\n' +
|
|
224
223
|
'wkgeo login --device-continue # 用上次保存的设备码换票、写完凭据就完成登录\n' +
|
|
225
|
-
'wkgeo login --no-auto-device # 检测到系统代理也不自动改设备码(强制回调;CLI
|
|
224
|
+
'wkgeo login --no-auto-device # 检测到系统代理也不自动改设备码(强制回调;CLI 会警告)\n' +
|
|
226
225
|
'wkgeo login --open-browser # 强制尝试打开系统浏览器(⚠️ 现在与默认行为等价,保留只为显式语义与兼容)\n' +
|
|
227
226
|
'wkgeo login --callback-first-wait 60 # ★ 可选:显式启用"首轮等待 60 秒没回调就并行改设备码"(默认关闭;0/off = 关闭)\n' +
|
|
228
227
|
'wkgeo login --timeout 600 # 等待授权 600 秒(默认单位是秒,不是毫秒;也支持 600s / 500ms)',
|
|
229
228
|
flags: {
|
|
230
|
-
browser: { type: 'boolean', desc: '是否自动打开浏览器(★
|
|
229
|
+
browser: { type: 'boolean', desc: '是否自动打开浏览器(★ 默认:人类终端打开;CI/管道/AI 子进程等非交互环境不打开;--no-browser 关闭)' },
|
|
231
230
|
/**
|
|
232
|
-
* ★
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
* 1.
|
|
231
|
+
* ★ 2026-10-21(老板反馈:CI 里弹了浏览器):默认**只在人类终端**自动打开;
|
|
232
|
+
* 非交互/CI 环境(stdout 非 TTY / CI 环境变量 / `WKONGAI_CLI_NO_BROWSER=1`)**一律不打开**。
|
|
233
|
+
* 本开关保留只为两件事:
|
|
234
|
+
* 1. **显式语义**(终端里明确表达"我就是要弹");
|
|
236
235
|
* 2. **兼容**既有脚本(不许删)。
|
|
237
|
-
*
|
|
236
|
+
* ⚠️ 它**压不过**非交互判定(CI 里写它也不弹);与 `--no-browser` 同时出现时 `--no-browser` 优先。
|
|
238
237
|
*/
|
|
239
|
-
'open-browser': { type: 'boolean', desc: '强制尝试打开系统浏览器(⚠️
|
|
240
|
-
device: { type: 'boolean', desc: '用设备码模式(RFC 8628
|
|
238
|
+
'open-browser': { type: 'boolean', desc: '强制尝试打开系统浏览器(⚠️ 仅在人类终端生效;CI/非交互环境仍不打开)' },
|
|
239
|
+
device: { type: 'boolean', desc: '用设备码模式(RFC 8628)代替本地回调:不需要任何回调地址,对代理/防火墙免疫(显式兜底;CLI 不会在端口失败时自动切换过来)' },
|
|
241
240
|
/**
|
|
242
241
|
* ★ 老板 2026-10-02 要的"AI 端友好路径"。
|
|
243
242
|
* 为什么做成"申请完就退出"而不是"申请完继续挂着轮询"(后者靠 JSONL 事件流也能做):
|
|
@@ -250,7 +249,7 @@ export const loginCommand = {
|
|
|
250
249
|
'device-continue': { type: 'boolean', desc: '继续上一次 --no-wait/被中断的设备码登录(读取本地保存的设备码并轮询到出结果)' },
|
|
251
250
|
'auto-device': {
|
|
252
251
|
type: 'boolean',
|
|
253
|
-
desc: '检测到系统代理时自动改用设备码(**默认开**;--no-auto-device 或 WKONGAI_AUTO_DEVICE=0 关闭,关闭时会警告"代理可能吃掉回调"
|
|
252
|
+
desc: '检测到系统代理时自动改用设备码(**默认开**;--no-auto-device 或 WKONGAI_AUTO_DEVICE=0 关闭,关闭时会警告"代理可能吃掉回调")',
|
|
254
253
|
},
|
|
255
254
|
/**
|
|
256
255
|
* ★ 可选自愈(默认**关闭**,0.2.2 起)。
|
|
@@ -345,7 +344,7 @@ export const loginCommand = {
|
|
|
345
344
|
*/
|
|
346
345
|
const proxyDetected = !!(proxyInfo && proxyInfo.detected);
|
|
347
346
|
let useDevice = wantsDevice;
|
|
348
|
-
/** 为什么会走设备码:proxy_detected /
|
|
347
|
+
/** 为什么会走设备码:proxy_detected / callback_first_wait(仅显式开启时) */
|
|
349
348
|
let downgradeReason = null;
|
|
350
349
|
if (!useDevice && autoDevice && proxyDetected) {
|
|
351
350
|
useDevice = true;
|
|
@@ -374,20 +373,23 @@ export const loginCommand = {
|
|
|
374
373
|
/** 真 spawn 的注入点(单测验证"AI 场景一次都不 spawn 浏览器";生产不传 = 真 spawn) */
|
|
375
374
|
spawnImpl: ctx.spawnImpl,
|
|
376
375
|
/**
|
|
377
|
-
* ★★
|
|
376
|
+
* ★★ 「要不要替用户弹**系统**浏览器」的输入(2026-10-21 起含 CI/非交互检测):
|
|
378
377
|
* * `isTTY` —— stdout 是不是终端(由 runtime 从 stdout.isTTY 读出,命令层原样透传);
|
|
379
378
|
* * `json` —— 是不是 JSON 输出(`--json` 或非 TTY 自动 JSON);
|
|
380
|
-
* * `forceBrowser` —— 用户写了 `--open-browser
|
|
381
|
-
* * `noBrowser` —— 用户写了 `--no-browser
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
379
|
+
* * `forceBrowser` —— 用户写了 `--open-browser`(只在终端里保留显式语义);
|
|
380
|
+
* * `noBrowser` —— 用户写了 `--no-browser`(**最高优先级**的"不打开"开关);
|
|
381
|
+
* * `env` —— CI 环境变量判定(`CI` / `GITHUB_ACTIONS`… / `WKONGAI_CLI_NO_BROWSER=1`)。
|
|
382
|
+
* 全部进 `resolveBrowserIntent()`(login.mjs 里的**唯一裁决点**):
|
|
383
|
+
* **人在终端默认打开**;`--no-browser` 与**非交互/CI**都不打开(`attempted:false`);
|
|
384
|
+
* 非交互时还会打印"把链接转给有浏览器的用户 / 改用 `--device`"的提示。
|
|
385
385
|
* ⚠️ 回调模式与设备码模式都吃这一份 deps —— 所以两条授权路径的行为天然一致。
|
|
386
386
|
*/
|
|
387
387
|
isTTY: ctx.isTTY === true,
|
|
388
388
|
json: ctx.out.json === true,
|
|
389
389
|
forceBrowser: flags.openBrowser === true,
|
|
390
390
|
noBrowser: flags.browser === false,
|
|
391
|
+
/** ★ CI / 非交互判定用(纯函数在 login.mjs 的 detectNonInteractive) */
|
|
392
|
+
env: ctx.env || {},
|
|
391
393
|
platform: ctx.platform,
|
|
392
394
|
/**
|
|
393
395
|
* ★ 可选首轮等待(见上面 `callbackFirstWaitMs`)。
|
|
@@ -411,7 +413,7 @@ export const loginCommand = {
|
|
|
411
413
|
*
|
|
412
414
|
* 老板原话:「默认的也不应该是设备授权,默认的就是那个传统的授权方式(浏览器打开授权页 +
|
|
413
415
|
* 本地 localhost 回调)。设备码是本地读不了/代理时才用的。」
|
|
414
|
-
* ⇒ 这里明说"本次就是回调、等多久都不会自动改设备码"
|
|
416
|
+
* ⇒ 这里明说"本次就是回调、等多久都不会自动改设备码",同时列出**唯一会走设备码**的两种情况,
|
|
415
417
|
* 并给出"主动提前切换"的显式开关,免得用户以为 CLI 背着他改道。
|
|
416
418
|
* ⚠️ 只在"回调模式 + 没检测到代理 + 没显式开首轮等待"时打:其它情况一律不打(正常环境不要噪音)。
|
|
417
419
|
*/
|
|
@@ -420,10 +422,12 @@ export const loginCommand = {
|
|
|
420
422
|
`ℹ️ 未检测到系统代理:本次使用浏览器回调授权(默认),会一直等你完成授权(最多 ${Math.round(config.loginTimeoutMs / 1000)} 秒)。`,
|
|
421
423
|
);
|
|
422
424
|
log(
|
|
423
|
-
' 不会因为"首轮等待/等待超时"到点就自动改用设备码;只有显式 `--device
|
|
424
|
-
'
|
|
425
|
+
' 不会因为"首轮等待/等待超时"到点就自动改用设备码;只有显式 `--device` 或检测到系统代理时才会用设备码' +
|
|
426
|
+
'(`--no-auto-device` 可关掉代理这一种自动切换)。',
|
|
427
|
+
);
|
|
428
|
+
log(
|
|
429
|
+
' (本地回调端口起不来会**如实报错**并建议 `--device`,不会静默换模式;想主动提前切换:`--callback-first-wait <秒>`。)',
|
|
425
430
|
);
|
|
426
|
-
log(' (想主动提前切换:`--callback-first-wait <秒>`;想直接走设备码:`--device`。)');
|
|
427
431
|
}
|
|
428
432
|
|
|
429
433
|
/**
|
|
@@ -663,13 +667,21 @@ export const loginCommand = {
|
|
|
663
667
|
handoff,
|
|
664
668
|
onAuthorizationUrl: (url, extra) => {
|
|
665
669
|
if (jsonMode) {
|
|
670
|
+
/**
|
|
671
|
+
* ★ 非交互(CI / 管道 / AI 子进程)时**不会**自动打开浏览器 ⇒ 给 AI/脚本的
|
|
672
|
+
* `user_action_required` 必须换成"把链接转给有浏览器的人 / 改用设备码",
|
|
673
|
+
* 否则调用方会以为"浏览器已经弹出来了",只让用户干等。
|
|
674
|
+
*/
|
|
675
|
+
const nonInteractive = !!(extra && extra.open_browser && extra.open_browser.skipped_reason === 'non_interactive');
|
|
666
676
|
out.event({
|
|
667
677
|
ok: true,
|
|
668
678
|
command: 'login',
|
|
669
679
|
mode: 'callback',
|
|
670
680
|
stage: 'awaiting_callback',
|
|
671
681
|
authorization_url: url,
|
|
672
|
-
user_action_required:
|
|
682
|
+
user_action_required: nonInteractive
|
|
683
|
+
? '本机是非交互环境(CI/管道/AI 子进程),不会自动打开浏览器:请把 authorization_url 转给有浏览器的用户打开;若本机无法接收回调,改用 `wkgeo login --device`(设备码模式)'
|
|
684
|
+
: '请让用户在浏览器中打开上面的 authorization_url,登录并点「同意」',
|
|
673
685
|
proxy_warning: proxyPayload,
|
|
674
686
|
...extra,
|
|
675
687
|
});
|
|
@@ -683,35 +695,18 @@ export const loginCommand = {
|
|
|
683
695
|
/** 设备码那条路也失败了 ⇒ 优先报"两条路都不通"(哪怕回调那边先抛了自己的错) */
|
|
684
696
|
if (deviceSide.combinedError) throw deviceSide.combinedError;
|
|
685
697
|
/**
|
|
686
|
-
* ★★ 0.2.
|
|
687
|
-
*
|
|
688
|
-
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
691
|
-
*
|
|
692
|
-
*
|
|
693
|
-
*
|
|
698
|
+
* ★★ 0.2.4(2026-10-21 老板真机反馈):**bind 失败不再静默改走设备码**。
|
|
699
|
+
*
|
|
700
|
+
* 旧行为(0.2.2~0.2.3):本地回调端口起不来 → 命令层自动申请设备码继续授权。
|
|
701
|
+
* 现场问题是:用户明明要的是"本地服务 + 浏览器回调",CLI 却在他不知情时换了
|
|
702
|
+
* 另一套授权协议(设备码),127.0.0.1 回调页始终没有出现,看起来就是"本地服务
|
|
703
|
+
* 没启动、浏览器也没东西可打开"。
|
|
704
|
+
*
|
|
705
|
+
* 新口径:**如实失败**,把 `--device` 作为明确的下一步建议交给用户/调用方
|
|
706
|
+
* (`runLogin` 抛的 `kind:"listen"` 错误 hint 里已经写了)。
|
|
707
|
+
* 设备码只在这两种情况下出现:用户显式 `--device` / 检测到系统代理。
|
|
694
708
|
*/
|
|
695
|
-
|
|
696
|
-
if (!canSwitch) throw e;
|
|
697
|
-
log('');
|
|
698
|
-
log(`⚠️ 本地回调端口起不来(${e.message}):这是"本地读不了回调"的真实失败,改用【设备码模式】继续授权。`);
|
|
699
|
-
log('ℹ️ 设备码不需要本地监听(常见原因是端口被安全软件/防火墙拦);授权页照旧点「同意」即可。');
|
|
700
|
-
let device;
|
|
701
|
-
try {
|
|
702
|
-
device = await requestDeviceCode({ config, deps: loginDeps });
|
|
703
|
-
} catch (e2) {
|
|
704
|
-
throw bothPathsFailedError(e2, 'callback_listen_failed', {
|
|
705
|
-
loginTimeoutMs: config.loginTimeoutMs,
|
|
706
|
-
});
|
|
707
|
-
}
|
|
708
|
-
savePending(device);
|
|
709
|
-
announceDeviceCode({ config, log, deps: deviceDeps({ no_wait: false, proxy_warning: proxyPayload }), device });
|
|
710
|
-
// 这一次再失败就如实抛出(不无限重试:两条路都失败,用户该看到真原因了)
|
|
711
|
-
result = await pollDeviceToken({ config, log, deps: loginDeps, device });
|
|
712
|
-
deletePendingDevice(pendingFile);
|
|
713
|
-
useDevice = true;
|
|
714
|
-
downgradeReason = 'callback_listen_failed';
|
|
709
|
+
throw e;
|
|
715
710
|
}
|
|
716
711
|
|
|
717
712
|
if (deviceSide.reason) {
|
|
@@ -781,8 +776,6 @@ export const loginCommand = {
|
|
|
781
776
|
out.info(' (本次因"检测到系统代理"自动改用了设备码模式:不再依赖 127.0.0.1 回调,绕过列表写了什么也不作依据。)');
|
|
782
777
|
if (downgradeReason === 'callback_first_wait')
|
|
783
778
|
out.info(' (已按显式 --callback-first-wait:首轮等待没收到回调,已自动改用设备码模式完成登录。)');
|
|
784
|
-
if (downgradeReason === 'callback_listen_failed')
|
|
785
|
-
out.info(' (本地回调端口起不来,已自动改用设备码模式完成登录。)');
|
|
786
779
|
}
|
|
787
780
|
return data;
|
|
788
781
|
},
|