wkgeo-cli 0.2.0
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 +681 -0
- package/SKILL.md +598 -0
- package/lib/api.mjs +178 -0
- package/lib/args.mjs +196 -0
- package/lib/cli.mjs +134 -0
- package/lib/commands/apps.mjs +116 -0
- package/lib/commands/auth.mjs +974 -0
- package/lib/commands/billing.mjs +421 -0
- package/lib/commands/index.mjs +160 -0
- package/lib/commands/orders.mjs +444 -0
- package/lib/commands/sites.mjs +140 -0
- package/lib/commands/skill.mjs +119 -0
- package/lib/config.mjs +260 -0
- package/lib/credentials.mjs +160 -0
- package/lib/device-store.mjs +132 -0
- package/lib/device.mjs +427 -0
- package/lib/errors.mjs +268 -0
- package/lib/format.mjs +204 -0
- package/lib/http.mjs +71 -0
- package/lib/index.mjs +39 -0
- package/lib/login.mjs +1332 -0
- package/lib/page.mjs +396 -0
- package/lib/pkce.mjs +48 -0
- package/lib/proxy.mjs +454 -0
- package/lib/qr.mjs +31 -0
- package/lib/runtime.mjs +287 -0
- package/package.json +51 -0
- package/wkgeo.mjs +17 -0
package/SKILL.md
ADDED
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: wkgeo-cli
|
|
3
|
+
description: "Use this skill when the user wants to 建站 / 创建网站 / 下单 / 购买加量包 / 钱包支付 / 充值 / 查余额 / 查钱包流水 / 查订单 / 查我已授权的应用 on the 云站(悟空AI,wkgeo.com)platform from the command line or as an AI agent. Covers installing the official CLI (npx -y wkgeo-cli@latest or npm i -g wkgeo-cli), one-time OAuth login (browser callback or device code), local credential storage, machine-readable JSON output and its error codes (40101 expired / 40102 revoked / 429 rate limited / 403 scope). Do NOT use for other platforms, for admin/ops actions requiring step-up (refund, price change, permission change), or for real WeChat payments (the CLI never charges money)."
|
|
4
|
+
description_zh: 云站平台官方命令行客户端:授权登录一次,之后建站 / 下单 / 钱包余额支付 / 充值 / 查余额 / 查订单都不再需要登录。
|
|
5
|
+
description_en: Official CLI for the wkgeo Cloud Site platform — one-time OAuth login, then create sites, place orders, pay from wallet balance, and check balance/orders without logging in again.
|
|
6
|
+
version: 0.2.0
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# 云站 CLI(`wkgeo-cli`)· 给 AI 端的操作手册
|
|
10
|
+
|
|
11
|
+
> 这份文档是给**AI/自动化**用的:目标是用最少的往返,把用户的"建站/下单/支付/查余额"意图
|
|
12
|
+
> 变成一串**确定性的命令调用**,并在需要用户动手(浏览器点同意)时**正确地停下来**。
|
|
13
|
+
>
|
|
14
|
+
> 用户版文档在 `README.md`;本文件与 npm 包一起分发(`npx` 装完就能读到)。
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 1. 何时使用
|
|
19
|
+
|
|
20
|
+
**用**:
|
|
21
|
+
|
|
22
|
+
* 用户说"**帮我建个站**"/"创建网站"/"开一个站点" → `sites create`
|
|
23
|
+
* 用户说"**帮我下单**"/"买加量包"/"扩容空间"/"买流量" → `products list` → `orders create`
|
|
24
|
+
* 用户说"**用钱包付**"/"余额支付"/"帮我付钱" → `orders create`(余额够会**当场付清**)+ `wallet` 复核
|
|
25
|
+
* 用户说"**充值**"/"充 100 块" → `wallet recharge`(下单后**自动打开我们网站的收银台页面**,用户在那里扫码支付)
|
|
26
|
+
* 用户说"**查余额**"/"我的钱包"/"账单流水" → `wallet` / `wallet txns`
|
|
27
|
+
* 用户说"**我的订单**"/"订单状态" → `orders list` / `orders get` / `orders check`
|
|
28
|
+
* 用户说"**我授权过哪些应用**"/"取消某个应用的授权" → `apps list` / `apps revoke`
|
|
29
|
+
* 需要确认"现在是谁在操作" → `whoami`
|
|
30
|
+
* **你自己需要这份手册**(换机器、上下文被截断、想确认某个参数)→ `skill`(把本文件原文打出来,见 §2)
|
|
31
|
+
|
|
32
|
+
**不要用**:
|
|
33
|
+
|
|
34
|
+
* ❌ **退款 / 关单 / 改价 / 改权限 / 调整余额**:这些是"敏感动作",需要 5 分钟内重新输密码
|
|
35
|
+
(step-up)。OAuth 令牌**永远过不了**(会返回 403/428)→ 让用户去网页后台做。
|
|
36
|
+
* ❌ **代替用户完成微信支付**:CLI 不会发起任何真实扣款,也不会模拟"支付成功"。
|
|
37
|
+
它只能创建待支付订单并把支付链接交给用户。
|
|
38
|
+
* ❌ 其他平台的网站/订单(本 CLI 只服务 `wkgeo.com` 这一套站点与账号体系)。
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 2. 安装与调用
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
# 首选(AI 端):不安装、免确认,永远拿最新版
|
|
46
|
+
npx -y wkgeo-cli@latest <命令>
|
|
47
|
+
|
|
48
|
+
# 或者全局安装后用短命令
|
|
49
|
+
npm i -g wkgeo-cli && wkgeo <命令>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
* 要求:Node ≥ 20(不需要 npm install 依赖,包内**零第三方依赖**)。
|
|
53
|
+
* 同机重复调用时 `npx` 会复用缓存,不再下载。
|
|
54
|
+
* **若某个命令的参数不确定,先跑 `npx -y wkgeo-cli@latest <命令> --help`**(帮助与实际解析同源)。
|
|
55
|
+
|
|
56
|
+
### ⭐ 自取手册:`wkgeo skill`
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx -y wkgeo-cli@latest skill # 默认:把本文件(SKILL.md)**原文**打到 stdout
|
|
60
|
+
npx -y wkgeo-cli@latest skill --json # 需要结构化:data.markdown / data.path / data.bytes / data.lines
|
|
61
|
+
npx -y wkgeo-cli@latest skill > SKILL.md # 直接落一份到本地,离线也能读
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
* 它读的是**包里那一份**(路径相对 CLI 模块解析,**与 cwd 无关**),所以拿到的永远和正在运行的这个版本一致 ——
|
|
65
|
+
不用再去 `node_modules` 里猜 npx 的缓存目录。
|
|
66
|
+
* ⚠️ `skill` **不受"非终端自动 JSON"的通用约定约束**:默认**永远**是纯文本原文(管道里也是),
|
|
67
|
+
只有显式 `--json` 才包成 `{"ok":true,"command":"skill","data":{…}}`。这样 `> SKILL.md` 得到的是一份能直接读的文档,
|
|
68
|
+
而不是一坨被转义的 JSON。
|
|
69
|
+
* ⚠️ 若报「**当前安装包里没有手册文件(SKILL.md)**」(`error.kind = "missing_skill"`,退出码 `1`):
|
|
70
|
+
说明跑的是**被裁剪的包**或只拷了 `lib/` 的开发目录(正常 npm 安装包里一定有这份文件)。
|
|
71
|
+
换官方包 `npx -y wkgeo-cli@latest skill` 即可;这不是命令用错了。
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 3. 输出契约(**关键**)
|
|
76
|
+
|
|
77
|
+
1. **非终端环境(管道 / AI 子进程)默认输出 JSON**;人类交互时自动是人类可读表格。
|
|
78
|
+
要强制人类可读加 `--text`,要强制 JSON 加 `--json`。
|
|
79
|
+
2. 除 `login` 与 `skill` 外,**stdout 只有一个 JSON 对象**:
|
|
80
|
+
|
|
81
|
+
```jsonc
|
|
82
|
+
// 成功
|
|
83
|
+
{ "ok": true, "command": "wallet", "data": { … }, "meta": { "base": "…", "client_id": "wkgeo-cli",
|
|
84
|
+
"credentials_file": "…", "duration_ms": 412 } }
|
|
85
|
+
// 失败(HTTP 仍是 200,错误在业务 code 里 —— 所以**不能**用 HTTP 状态判断成败)
|
|
86
|
+
{ "ok": false, "command": "wallet", "error": { "kind": "expired", "code": 40101,
|
|
87
|
+
"message": "访问令牌已过期,请重新授权",
|
|
88
|
+
"hint": "运行 `wkgeo login` 重新授权(浏览器里通常还有登录态,点一下同意即可)",
|
|
89
|
+
"retry_after": null }, "meta": { … } }
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
3. `login` 例外:它是长跑命令,JSON 模式下输出 **JSONL 事件流**(见 §5)。
|
|
93
|
+
另一个例外是 `skill`:它默认**永远**打**纯文本手册原文**(连管道里也不套信封)——
|
|
94
|
+
它本身就是给人/AI 读的 markdown,套 JSON 只会更难用;要结构化就显式 `--json`
|
|
95
|
+
(`{"ok":true,"command":"skill","data":{"path":…,"markdown":…,"bytes":…,"lines":…}}`)。
|
|
96
|
+
4. **退出码**:`0` 成功 | `1` 失败 | `2` 用法错误 | `3` 需要(重新)授权 | `4` 被限流。
|
|
97
|
+
脚本里优先用退出码分支,用 `error.kind` 细分。
|
|
98
|
+
5. **任何输出里都不会有令牌**(`--verbose` 也只给掩码)—— 不要试图从输出里找 token。
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 4. 命令表
|
|
103
|
+
|
|
104
|
+
| 命令 | 作用 | 关键参数 |
|
|
105
|
+
| --- | --- | --- |
|
|
106
|
+
| `login` | 授权一次(令牌存本地 30 天) | `--device`、`--no-wait`、`--device-continue`、`--no-auto-device`、`--callback-first-wait <秒>`、`--no-browser`、`--open-browser`、`--scope`、`--timeout <秒>` |
|
|
107
|
+
| `whoami` | 当前是谁(账号标识) | `--verbose` |
|
|
108
|
+
| `logout` | 退出(删本地凭据 + 服务端吊销) | `--local-only` |
|
|
109
|
+
| `config` | 生效配置 / 登录状态 | — |
|
|
110
|
+
| `sites list` | 我的站点 | `--limit` `--offset` |
|
|
111
|
+
| `sites create` | 建站(**建在令牌所属用户名下**) | `--name`(必填)、`--customer`、`--template` |
|
|
112
|
+
| `sites delete` | 删站(软删进回收站) | `<站点ID>` |
|
|
113
|
+
| `wallet` | 余额 + 最近流水 | `--limit` `--entitlements` |
|
|
114
|
+
| `wallet txns` | 钱包流水 | `--limit` |
|
|
115
|
+
| `wallet recharge` | 充值下单,**并打开我们网站的收银台页面**扫码支付(**不扣款**) | `--amount-cents`(分,必填)、`--no-browser`、`--open-browser`、`--dry-run` |
|
|
116
|
+
| `products list` | 能买什么 | — |
|
|
117
|
+
| `orders create` | 下单(余额够**当场付清**) | `--product <代码>` `--units <份数>`(或 `--quota <键>` `--amount <数量>`)`--allow-wechat` |
|
|
118
|
+
| `orders check` | 查支付结果 + 最新余额 | `<订单ID>` |
|
|
119
|
+
| `orders pay` | 同 `orders check`(别名) | `<订单ID>` |
|
|
120
|
+
| `orders get` | 订单详情(不触发微信查询) | `<订单ID>` |
|
|
121
|
+
| `orders list` | 我的订单 | `--limit` `--offset` `--status` |
|
|
122
|
+
| `apps list` | 我授权过的应用 | — |
|
|
123
|
+
| `apps revoke` | 一键吊销某应用授权(令牌立即失效) | `<client_id>` |
|
|
124
|
+
| `skill` | **打印给 AI 端的操作手册**(本文件原文,随包分发) | `--json`(默认纯文本原文;不受"非终端自动 JSON"影响) |
|
|
125
|
+
|
|
126
|
+
### 示例:成功
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
$ npx -y wkgeo-cli@latest orders create --product storage_gb --units 1
|
|
130
|
+
{
|
|
131
|
+
"ok": true,
|
|
132
|
+
"command": "orders create",
|
|
133
|
+
"data": {
|
|
134
|
+
"order_id": "95fe2dfc-73ca-4d86-80b3-0bfbb0c36f54",
|
|
135
|
+
"out_trade_no": "Q1759217340123abc",
|
|
136
|
+
"paid": true,
|
|
137
|
+
"amount_cents": 100,
|
|
138
|
+
"amount_text": "¥1.00",
|
|
139
|
+
"wallet_paid_cents": 100,
|
|
140
|
+
"balance_before_cents": 10000,
|
|
141
|
+
"balance_after_cents": 9900,
|
|
142
|
+
"product": { "code": "storage_gb", "name": "空间容量", "unit": "GB", "unit_price_cents": 100, "quota_multiplier": 1024 },
|
|
143
|
+
"quota": { "quota_key": "storage_mb", "quota_amount": 1024, "units": 1 },
|
|
144
|
+
"message": "余额已足够,订单已直接完成"
|
|
145
|
+
},
|
|
146
|
+
"meta": { … }
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**读法**:`data.paid === true` 表示**已经用钱包付清了**;`data.order_id` 是订单号;`data.balance_after_cents` 可用来向用户汇报新余额。
|
|
151
|
+
|
|
152
|
+
### 示例:失败(余额不足,CLI **拒绝**下单)
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
$ npx -y wkgeo-cli@latest orders create --product storage_gb --units 10
|
|
156
|
+
{
|
|
157
|
+
"ok": false,
|
|
158
|
+
"command": "orders create",
|
|
159
|
+
"error": {
|
|
160
|
+
"kind": "insufficient_balance",
|
|
161
|
+
"code": "insufficient_balance",
|
|
162
|
+
"message": "钱包余额不足:本单需要 ¥10.00,当前余额 ¥1.00",
|
|
163
|
+
"hint": "CLI **不会**发起真实微信支付。请先在网页充够余额再下单;如果确实要用微信扫码,加 --allow-wechat 创建\"待支付\"订单(CLI 只打印支付链接,不扣款,也不替你确认支付)",
|
|
164
|
+
"retry_after": null
|
|
165
|
+
},
|
|
166
|
+
"meta": { … }
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
**读法**:不要重试、不要加 `--allow-wechat` 硬来 —— 先请用户充值(`wallet recharge`)。
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 5. 授权流程(**必须让用户动手的那一步**)
|
|
175
|
+
|
|
176
|
+
### 5.1 本地回调模式(默认)
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
npx -y wkgeo-cli@latest login --json
|
|
180
|
+
# 需要更长的等待时间:--timeout 的单位是**秒**
|
|
181
|
+
npx -y wkgeo-cli@latest login --json --timeout 600 # 600 秒 = 10 分钟
|
|
182
|
+
npx -y wkgeo-cli@latest login --json --timeout 500ms # 也可以显式写毫秒
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
它会**阻塞**(默认最多 5 分钟)并输出 JSONL:
|
|
186
|
+
|
|
187
|
+
```jsonc
|
|
188
|
+
// 第一行 —— 拿到它就应该**立刻**把网址交给用户(不要等命令结束!)
|
|
189
|
+
// ⚠️ `authorize/` 的**尾斜杠不能丢**:它是托管侧 302(会丢掉全部 query)的分界线
|
|
190
|
+
{"ok":true,"command":"login","mode":"callback","stage":"awaiting_callback",
|
|
191
|
+
"authorization_url":"https://oauth.wkgeo.com/oauth/authorize/?client_id=wkgeo-cli&…",
|
|
192
|
+
"redirect_uri":"http://127.0.0.1:53682/callback","scope":"site:read site:write billing:self",
|
|
193
|
+
"timeout_ms":300000,
|
|
194
|
+
"user_action_required":"请让用户在浏览器中打开上面的 authorization_url,登录并点「同意」"}
|
|
195
|
+
// 最后一行 —— 成功
|
|
196
|
+
{"ok":true,"command":"login","stage":"done","data":{"account":{"user_id":"…","email":"…"},
|
|
197
|
+
"expires_at":"2026-10-30T…","credentials_file":"C:\\Users\\…\\.wkongai\\credentials.json"}}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
> ⏱️ **`--timeout` 的单位是秒**(默认 300 秒):`--timeout 600` = 600 秒 = 10 分钟;
|
|
201
|
+
> 也支持显式后缀 `--timeout 600s` / `--timeout 500ms`。这个单位以前是**毫秒**,
|
|
202
|
+
> 于是 `--timeout 600` 变成 600 毫秒(0.8 秒就超时失败)—— 所以**要更长等待就直接写秒数**,
|
|
203
|
+
> 不要自己换算成毫秒。非法值(`--timeout 十分钟` / `--timeout 0`)直接报用法错误(退出码 `2`),
|
|
204
|
+
> 不会静默变成默认值。
|
|
205
|
+
|
|
206
|
+
> 🌐 **自动打开浏览器**(⚠️ **本次有改动,务必看清楚**,详见 §5.5):
|
|
207
|
+
> ★ **默认总是尝试打开系统浏览器**(老板最终拍板走**传统路线**:「还是返回以前那种打开跳到一个浏览器……
|
|
208
|
+
> 大家也都是那个的……还是走传统的路吧」)——**人在终端**与 **AI 场景**(非 TTY / `--json`)**都一样**;
|
|
209
|
+
> 实现是**多策略依次尝试**(每条都看退出码/错误/超时,失败静默试下一条):
|
|
210
|
+
> * 成功 → `✅ 已为你打开浏览器,请在那里完成授权。`(紧跟着一句"授权成功后页面会自动尝试关闭");
|
|
211
|
+
> * 全部失败 → `⚠️ 浏览器没能自动打开,请手动复制这个地址到浏览器完成授权:` + **紧跟一行完整 URL**
|
|
212
|
+
> (**绝不中断**授权;AI 场景再多一句"客户端支持内置浏览器也可以在那里打开"的**可选**提示);
|
|
213
|
+
> * `--no-browser` → **完全不尝试打开**(唯一开关,`open_browser.skipped_reason = "no_browser"`);
|
|
214
|
+
> * `--open-browser` → 保留(现在与默认行为等价,只为显式语义与兼容)。
|
|
215
|
+
> 所以 AI **不需要**自己判断"该不该开浏览器",但**永远要**把 `authorization_url` 原文转述给用户
|
|
216
|
+
> (万一没弹出来,他要能手动打开)。
|
|
217
|
+
|
|
218
|
+
**AI 的正确做法**:
|
|
219
|
+
|
|
220
|
+
1. 用后台/长超时方式启动 `login --json`(它会等最多 5 分钟);
|
|
221
|
+
2. 读到 `stage:"awaiting_callback"` 后:
|
|
222
|
+
* **先看 `open_browser`**:`opened:true` = CLI 已经把系统浏览器弹出来了 → 把 `paste_text`
|
|
223
|
+
**原样转述给用户**("已为你打开浏览器,请在那里完成授权…")即可,**不要**自己再开一遍;
|
|
224
|
+
* `opened:false`(打开失败)→ 把那段**含完整地址**的提示念给用户,或**可选**地
|
|
225
|
+
在你自己客户端的内置浏览器里打开 `authorization_url`(见 §5.5,这只是备选);
|
|
226
|
+
* `skipped_reason:"no_browser"` → 用户/调用方明确不要弹窗,那就**只**把完整网址清楚地给用户;
|
|
227
|
+
3. **等**到 `stage:"done"` 再继续下一步命令;
|
|
228
|
+
4. 用户说"我不想点"时不要重试授权 —— 只能等他点(或者用 `--device`,见 5.3)。
|
|
229
|
+
|
|
230
|
+
**用户会看到什么**:登录页(账密 / 手机号验证码 / 邮箱验证码三个页签)→ 同意页
|
|
231
|
+
(应用名「官方 CLI」+ 将获得的能力)→ 点「同意」→ 浏览器显示
|
|
232
|
+
**「✅ 授权成功,请回到终端(客户端)继续操作,本页面可以关闭了」** → 终端打印
|
|
233
|
+
「✅ 授权成功,已登录:<账号>」。
|
|
234
|
+
|
|
235
|
+
### 5.2 令牌放在哪、能用多久
|
|
236
|
+
|
|
237
|
+
* 文件:`~/.wkongai/credentials.json`(Windows `%USERPROFILE%\.wkongai\credentials.json`),权限 `0600`。
|
|
238
|
+
* 有效期 **30 天**(2592000 秒);**不会自动刷新** —— 过期就再 `login` 一次。
|
|
239
|
+
* 用户也可以在网页「我已授权的应用」里一键吊销(吊销后 CLI 会收到 40102)。
|
|
240
|
+
* 🔒 不要把这个文件内容读出来放进任何输出/日志/第三方请求里。
|
|
241
|
+
|
|
242
|
+
### 5.3 设备码模式(`login --device`)—— **代理/远程机器的首选**
|
|
243
|
+
|
|
244
|
+
**什么时候用它**(不用问用户,直接选它就行):
|
|
245
|
+
|
|
246
|
+
* CLI 跑在远程服务器 / 容器 / CI 里(本地回调打不到用户机器);
|
|
247
|
+
* ⭐ **用户开着代理/VPN**(CLI 会自动检测,见 §5.4)—— 回调可能被代理接管;
|
|
248
|
+
* 用户机器有防火墙/安全软件拦本地监听;
|
|
249
|
+
* 用户觉得"复制长网址"麻烦(设备码页面短码已预填,点一下就完)。
|
|
250
|
+
|
|
251
|
+
**两条用法**:
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
# ① 交互式终端 / 你有长超时能力:一口气跑完(阻塞,最多 expires_in 秒)
|
|
255
|
+
npx -y wkgeo-cli@latest login --device --json
|
|
256
|
+
# → {"stage":"awaiting_user","user_code":"ABCD-1234",
|
|
257
|
+
# "verification_uri":"https://oauth.wkgeo.com/oauth/device/",
|
|
258
|
+
# "verification_uri_complete":"…/?user_code=ABCD-1234","open_url":"…",
|
|
259
|
+
# "expires_in":600,"interval":5,"paste_text":"请打开 … 并完成「同意授权」(短码:ABCD-1234)。",
|
|
260
|
+
# "user_action_required":"请让用户访问 … 并输入短码 ABCD-1234"}
|
|
261
|
+
# → 最后一行 {"stage":"done","data":{…}}
|
|
262
|
+
|
|
263
|
+
# ② ★ Agent 推荐:拆成两步,**不需要长驻进程**
|
|
264
|
+
npx -y wkgeo-cli@latest login --device --no-wait --json # 申请完立刻退出
|
|
265
|
+
# → 一条事件:no_wait:true + resume_command:"wkgeo login --device-continue"
|
|
266
|
+
# + pending_file + expires_at + next_step(照着 next_step 说话即可)
|
|
267
|
+
# (把 paste_text 原样念给用户,让他在**自己的浏览器**里点「同意」)
|
|
268
|
+
npx -y wkgeo-cli@latest login --device-continue --json # 用户说"点好了"之后再领票
|
|
269
|
+
# → 最后一行 {"stage":"done","data":{…凭据已写入…}}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
**②里那一行事件 = AI 端要用的全部信息**(都在这条 `stage:"awaiting_user"` 事件上):
|
|
273
|
+
|
|
274
|
+
| 字段 | 含义 | AI 端怎么用 |
|
|
275
|
+
| --- | --- | --- |
|
|
276
|
+
| `user_code` | 短码(形如 `ABCD-1234`) | 只有当用户要手输时才念;优先给下面的完整网址 |
|
|
277
|
+
| `verification_uri` | 确认页地址 | 网址兜底(一般不单独用它) |
|
|
278
|
+
| `verification_uri_complete` | 带 `?user_code=` 的地址(短码已预填) | **优先**把这个给用户,他打开点「同意」就完 |
|
|
279
|
+
| `paste_text` | CLI 已经写好的一句话(含网址 + 短码 + 要做什么) | **原样念给用户**,不要自己改写 |
|
|
280
|
+
| `expires_in` | 短码有效期(秒,默认 600 = 10 分钟) | 过期了重新 `--device --no-wait` 申请,别硬续 |
|
|
281
|
+
| `interval` | 服务端建议的轮询间隔(秒) | 只影响 `--device-continue` 内部,AI 不用管 |
|
|
282
|
+
| `resume_command` | 下一步该跑的命令(`wkgeo login --device-continue`) | 用户说"点好了"之后再跑它 |
|
|
283
|
+
| `pending_file` | 短码落盘的位置(本地状态) | 排障用;**换机器/换容器就续不上**(状态在本机) |
|
|
284
|
+
| `next_step` | CLI 写好的"下一步怎么做" | 照着它说话,别自己组织语言 |
|
|
285
|
+
|
|
286
|
+
(另有 `open_url` = CLI 实际会打开的地址、`user_action_required` = 直接可用的用户话术、`no_wait` / `expires_at` / `proxy_warning` 等诊断字段。)
|
|
287
|
+
|
|
288
|
+
**AI 端的正确做法**:
|
|
289
|
+
|
|
290
|
+
1. 优先用 `--device --no-wait --json`(一轮工具调用就返回,不会把用户/自己晾住);
|
|
291
|
+
2. 把事件里的 `paste_text`(或 `user_action_required`)**原样转述给用户**——不要自己改写、
|
|
292
|
+
不要漏掉短码或网址;
|
|
293
|
+
3. 用户回"点好了"之后跑 `login --device-continue --json`,看到 `stage:"done"` 才算登录成功;
|
|
294
|
+
4. 用户迟迟没点:设备码有 `expires_in`(默认 600 秒)有效期,过期就重新申请一次,
|
|
295
|
+
**不要**反复跑 `--device-continue`(那只会得到 `kind:"expired"`);
|
|
296
|
+
5. 🔴 **不要试图替用户确认**(见下面的安全边界)。
|
|
297
|
+
|
|
298
|
+
> 🔴 **安全边界(务必理解,别越线)**:用户在确认页点「同意」这一步是
|
|
299
|
+
> `POST /api/oauth/device_confirm`,它**要求用户自己的平台登录会话**。
|
|
300
|
+
> AI 端**没有、也不该有**用户的浏览器会话,所以:
|
|
301
|
+
> * "用户把短码发到 AI 对话框"之后,AI 能做的是**把网址/短码清楚地转达、引导他自己去点**;
|
|
302
|
+
> * **不能**替用户确认(也没有任何命令能代点 —— CLI 这边根本没有这个动作)。
|
|
303
|
+
>
|
|
304
|
+
> ✅ 这条路径**已上线并端到端真跑通过**(2026-10-02,含真 CLI 子进程
|
|
305
|
+
> `awaiting_user → done`、重放防护、以及 `--no-wait` + `--device-continue` 全流程)。
|
|
306
|
+
|
|
307
|
+
### 5.4 代理 / VPN 环境下怎么办(`127.0.0.1` 被代理接管)
|
|
308
|
+
|
|
309
|
+
**现象**:回调模式下用户点了「同意」,浏览器报"无法访问此网站"或没反应,
|
|
310
|
+
`login` 一直等到超时(`kind:"timeout"`)。
|
|
311
|
+
|
|
312
|
+
**机理**:回调要求**浏览器**访问 `http://127.0.0.1:<端口>/callback?code=…`;
|
|
313
|
+
全局/系统代理若没把回环地址排除,这条请求会被代理接管 → CLI 收不到 code。
|
|
314
|
+
|
|
315
|
+
**CLI 已经自己处理好了**(2026-10-02 第二版加固,**默认开**):
|
|
316
|
+
|
|
317
|
+
* ★ **只要检测到"生效中的系统代理"就直接改用设备码模式**(事件流里第一行就是
|
|
318
|
+
`stage:"awaiting_user"`,而不是 `awaiting_callback`);`data.auto_device.reason = "proxy_detected"`。
|
|
319
|
+
⚠️ **判定不看代理绕过列表**:真机上出现过"绕过列表里明明写了 `127.*;localhost`
|
|
320
|
+
却仍被代理接管"(用户点了【同意授权】,CLI 一直拿不到令牌;
|
|
321
|
+
证据 `curl -x http://127.0.0.1:7890 http://127.0.0.1:1/x` → 502)。
|
|
322
|
+
`proxy_warning.loopback_bypassed` / `risk` 现在**只是诊断字段**,不再代表"回调没问题";
|
|
323
|
+
`proxy_warning.loopback_bypass_trusted` 恒为 `false`。
|
|
324
|
+
* ★ **回调模式首轮等待**(★ **只在检测到系统代理时才启用**,默认 **180 秒**;
|
|
325
|
+
`--callback-first-wait <秒>` / `WKONGAI_CALLBACK_FIRST_WAIT_MS` 可显式指定,
|
|
326
|
+
`0`/`off` 关闭)没等到回调 → **自动改用设备码继续**(`reason: "callback_first_wait"`),
|
|
327
|
+
用户不用再干等整段 `--timeout`;
|
|
328
|
+
⚠️ **2026-10-03 修**:以前它**无条件**挂在 60 秒上,于是"用户读完授权页再点同意"经常超时,
|
|
329
|
+
明明没开代理也被切成设备码(老板真机原话:「现在变成设备码了,我们因为没有开任何VPN,
|
|
330
|
+
所以这个是不对的。」)。现在:**没有代理 → 不启用**(老实等满 `--timeout`),
|
|
331
|
+
用户显式指定时才照旧生效;`data.auto_device.reason == "callback_first_wait"`
|
|
332
|
+
**只可能**出现在有代理的机器上。
|
|
333
|
+
⚠️ **切换时不再关掉本地回调监听**(同一次修复):两条路**并存**,哪条先成功就用哪条 ——
|
|
334
|
+
用户之后才点的那次【同意授权】照样能完成登录;旧逻辑在切换那一刻关了监听,用户随后点同意
|
|
335
|
+
只会看到 **ERR_CONNECTION_REFUSED**(真机截图见过的"无法访问此网站")。
|
|
336
|
+
若设备码先成功,之后到达的回调会拿到一张「本次登录已经完成」的页面(不是错误页);
|
|
337
|
+
若回调先成功,设备码轮询会被停掉并说明(`ℹ️ 本地回调已经拿到令牌:设备码那条路已停止…`)。
|
|
338
|
+
* 整段回调**超时**、或本地回环端口**起不来** → 同样自动改用设备码继续
|
|
339
|
+
(`reason: "callback_timeout"` / `"callback_listen_failed"`),**不会**直接失败退出;
|
|
340
|
+
* 换票失败 / 用户点了拒绝 → **不降级**(授权已经走到下一步了,别让用户白做一遍);
|
|
341
|
+
* 上述情况下,`meta.proxy_warning` / `data.proxy_warning` 都会告诉你原因与建议动作:
|
|
342
|
+
|
|
343
|
+
```jsonc
|
|
344
|
+
"proxy_warning": {
|
|
345
|
+
// ★ callback_may_fail 现在等价于 detected(与绕过列表无关)
|
|
346
|
+
"detected": true, "callback_may_fail": true, "loopback_bypass_trusted": false,
|
|
347
|
+
"risk": false, "loopback_bypassed": true, // 仅诊断
|
|
348
|
+
"proxy": "127.0.0.1:7890", "source": "windows-registry",
|
|
349
|
+
"bypass": ["localhost", "127.*", "<local>"], "actions": [
|
|
350
|
+
{"type":"use_device_code","label":"改用设备码模式(不需要本地回调,推荐)","command":"wkgeo login --device"},
|
|
351
|
+
{"type":"bypass_loopback","label":"把 127.0.0.1,localhost 加进代理绕过列表(想继续用浏览器回调模式时;注意它不保证生效)"},
|
|
352
|
+
{"type":"disable_proxy","label":"临时关闭代理后重试"}
|
|
353
|
+
]
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
**AI 端该怎么说**:
|
|
358
|
+
|
|
359
|
+
* 看到 `proxy_warning.callback_may_fail: true`:**不要**叫用户去改代理设置(很多用户不懂),
|
|
360
|
+
直接按设备码流程走 —— CLI 通常已经自动切过去了;
|
|
361
|
+
只有当用户明确说"我就想用浏览器回调"时,才把 `actions` 里第 2/3 条念给他;
|
|
362
|
+
* 看到 `auto_device.applied: true`:可以在汇报里加一句
|
|
363
|
+
「检测到你开着代理,已自动改用设备码模式(不需要本地回调)」——如实说明,别隐瞒;
|
|
364
|
+
* 想让 CLI **不要**自动改道(例如你确认回调一定通):加 `--no-auto-device`,或设
|
|
365
|
+
`WKONGAI_AUTO_DEVICE=0`。⚠️ 此时 CLI 会**打印警告**说"若代理接管 127.0.0.1,
|
|
366
|
+
点了【同意授权】也收不到结果",并且**不会**再用首轮等待自愈 —— 尊重用户的选择,
|
|
367
|
+
但要**把这个风险如实转述**给他;
|
|
368
|
+
* 排障:`npx -y wkgeo-cli@latest config --json` 里有 `data.proxy` 与 `meta.proxy_warning`,
|
|
369
|
+
是"用户机器上是不是代理挡了回调"的**唯一权威判断**(只读检测,不会改用户任何设置)。
|
|
370
|
+
|
|
371
|
+
### 5.5 打开授权页 / 收银台(**给 AI 端**)
|
|
372
|
+
|
|
373
|
+
> ★ **2026-10 老板最终拍板走传统路线**(原话:「还是返回以前那种**打开跳到一个浏览器**,然后打开授权
|
|
374
|
+
> 那个吧,**大家也都是那个的**……我们在右侧这个……能力还不会太完善……**还是走传统的路吧**」):
|
|
375
|
+
> **CLI 默认就会尝试弹系统浏览器**(人在终端与 AI 场景都一样)。所以你的第一动作**不是**去开内置浏览器,
|
|
376
|
+
> 而是**看 `open_browser` 字段 + 把 `paste_text` 念给用户**:
|
|
377
|
+
|
|
378
|
+
| `open_browser` | 你该做什么 |
|
|
379
|
+
| --- | --- |
|
|
380
|
+
| `opened: true` | ✅ CLI 已经把浏览器弹出来了 → 把 `paste_text` **原样**念给用户即可,**不要**再自己开一遍 |
|
|
381
|
+
| `opened: false`(`attempted: true`) | ⚠️ 没能自动打开 → 把那段**含完整地址**的提示念给用户(或可选地在你的内置浏览器里打开,见下) |
|
|
382
|
+
| `skipped_reason: "no_browser"` | 调用方/用户明确不要弹窗 → **只**把完整网址清楚地给用户,不要自己开浏览器 |
|
|
383
|
+
| `skipped_reason: null` | 真的尝试过打开(成没成看 `opened`)—— 这是默认情况 |
|
|
384
|
+
|
|
385
|
+
**兜底手段(可选,**只在 CLI 没打开成功**时用)**——按 ① → ② → ③ 判断:
|
|
386
|
+
|
|
387
|
+
```
|
|
388
|
+
① 客户端【原生】支持"在内置浏览器 / 侧边面板打开 URL" → 直接用 ✔(一句话,不装任何东西)
|
|
389
|
+
② 客户端不支持(DSH 就是)→ 装桥接插件 → 装完就能在【右侧】打开 ✔
|
|
390
|
+
③ 都不行 → 把**完整地址**交给用户复制(说清动作是"复制到浏览器地址栏打开")
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
* ⚠️ **这只是备选**:CLI 已经默认弹系统浏览器了,**不要**再叠一层"请在右侧…"的话术 ——
|
|
394
|
+
那会让用户不知道该看哪个窗口(老板要的就是"回到以前那种,打开跳到一个浏览器")。
|
|
395
|
+
* ① **原生就支持**:直接打开,**不需要装任何东西**;已经原生支持"应用内侧边栏"的客户端
|
|
396
|
+
(DSH 自带"点链接进右栏"),**让用户点一下链接**也算数。
|
|
397
|
+
* ② **不支持 → 装桥接插件**(DSH 的 `dsh-browser-open`):
|
|
398
|
+
* 装:`dsh plugin --profile desktop add <本地插件路径>`
|
|
399
|
+
* 自检:`curl -s http://127.0.0.1:19387/dsh-browser-open/next` —— 返回 `{"url":""}` = 通道可用 ✔
|
|
400
|
+
* 回滚:`dsh plugin --profile desktop remove dsh-browser-open`
|
|
401
|
+
* ③ 把**完整地址**清楚地给用户,并说明"复制到浏览器地址栏打开",不要只说"我把链接发给你了"。
|
|
402
|
+
* ✅ 传统路线的两个好处(老板为什么这么定):**稳定**(各平台都有成熟的系统启动器);
|
|
403
|
+
**`window.close()` 在独立浏览器里生效** —— 授权页/收银台完成后的那一屏能自己关掉。
|
|
404
|
+
* ★ **页面完成之后会自己关掉**(2026-10-02 老板):授权成功 / 支付成功那一屏会尝试
|
|
405
|
+
`window.close()`,所以**那个页面自己消失是正常的**,不要因此以为出了错。
|
|
406
|
+
⚠️ 浏览器只允许脚本关闭**由脚本打开的**窗口,本页是导航过去的 ⇒ **关不掉也是常态**
|
|
407
|
+
(页面上有「关闭本页」按钮兜底)。两种情况都**不需要**让用户去找那个页面;
|
|
408
|
+
没关掉就让他手动关掉即可。`paste_text` / 提示语里已经带了这句话,照念就行。
|
|
409
|
+
* **DSH 专用的一行触发**(**仅 DSH,且 `dsh-browser-open` 插件已装**,**只在 CLI 没能自动打开时**才用):
|
|
410
|
+
|
|
411
|
+
```powershell
|
|
412
|
+
Set-Content -LiteralPath "$env:USERPROFILE\.dsh\browser-open.txt" -Value "<完整网址>" -NoNewline -Encoding utf8
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
约 **1.2 秒**后在**右侧内置浏览器**打开,收件文件会被**自动删除**(删掉 = 客户端真的执行了)。
|
|
416
|
+
原理 / 三层验证 / 回滚见 [`docs/AI客户端-内置浏览器打开.md`](../docs/AI客户端-内置浏览器打开.md)。
|
|
417
|
+
|
|
418
|
+
**CLI 这边的行为(你不需要自己判断,照着字段走就行)**:
|
|
419
|
+
|
|
420
|
+
| 运行场景 | CLI 会不会自动弹**系统**浏览器 |
|
|
421
|
+
| --- | --- |
|
|
422
|
+
| **默认(任何场景,含非 TTY / `--json`)** | ✅ **会尝试打开**(老板最终拍板走传统路线) |
|
|
423
|
+
| 加了 `--no-browser` | ❌ **完全不尝试打开**(**唯一**开关,**优先级最高**,压过 `--open-browser`) |
|
|
424
|
+
| 加了 `--open-browser` | ✅ 会(⚠️ 现在与默认行为**等价**,保留只为显式语义与兼容,**别删也别依赖它去"开启"**) |
|
|
425
|
+
|
|
426
|
+
* **`--json` 里的结构化字段 `open_browser`**:`{attempted, opened, tried, url, skipped_reason}`
|
|
427
|
+
(结构与取值口径**没有变**)—— `skipped_reason` 只有两种取值:
|
|
428
|
+
* `null` = 真的尝试过打开(成没成看 `opened`);
|
|
429
|
+
* `"no_browser"` = 调用方/用户明确要求别打开。
|
|
430
|
+
* ⚠️ 历史取值 `"non_tty_agent_mode"`(2026-10-01 那版"AI 场景默认不弹")**已弃用,不会再出现**
|
|
431
|
+
—— 看到它说明你在读旧文档/旧版本的输出。
|
|
432
|
+
* 同一条事件/数据里还有 **`paste_text`**(`login` 的 `awaiting_callback` 事件、`wallet recharge` 的 `data`):
|
|
433
|
+
**原样念给用户**,不要自己改写(里面已经含完整网址 + "已为你打开浏览器…"的口径)。
|
|
434
|
+
* ⚠️ **不弹浏览器 ≠ 失败**,**打开失败也 ≠ 失败**:流程照旧等回调、照旧返回 `pay_url`;
|
|
435
|
+
CLI **绝不会**因为"没打开浏览器"而让命令失败 —— 你只要把地址送到用户眼前就对了。
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## 6. 错误码 → 该做什么
|
|
440
|
+
|
|
441
|
+
| `error.kind` / `code` | 含义 | **AI 应该做的事** |
|
|
442
|
+
| --- | --- | --- |
|
|
443
|
+
| `expired` / `40101` | 令牌已过期 | 让用户重新授权:跑 `login`(**不需要**重新输密码注册;浏览器里通常还有登录态) |
|
|
444
|
+
| `revoked` / `40102` | 授权已被吊销 | 同上重新 `login`;并提醒用户"如果这不是你做的,去「我已授权的应用」看看" |
|
|
445
|
+
| `unauthenticated` / `401` | 旧版:过期/被吊销/未登录**分不出来** | 一律重新 `login`(CLI 的 `hint` 会说明这点) |
|
|
446
|
+
| `rate_limited` / `429` | 调用过于频繁(每令牌 60/分、写操作 20/分) | **等 `retry_after` 秒后原样重试**。⚠️ **绝对不要**重新授权 —— 限流和授权无关,重授权只会白跑一遍浏览器 |
|
|
447
|
+
| `forbidden` / `403` | scope 不足,或动作需要二次验证 | scope 不足 → `login --scope "site:read site:write billing:self"` 重新授权;**动钱/改权限类动作 → 明确告诉用户"这个只能你在网页后台做"** |
|
|
448
|
+
| `step_up` / `428` | 需要 5 分钟内重新输密码 | 同上:**AI 做不了**,让用户去网页后台 |
|
|
449
|
+
| `insufficient_balance` | 余额不足 | 先 `wallet` 确认,再 `wallet recharge --amount-cents N` 让用户扫码;**不要**加 `--allow-wechat` 绕过 |
|
|
450
|
+
| `not_found` / `404` | 订单/站点/产品不存在 | 用 `orders list` / `products list` 重新确认 ID |
|
|
451
|
+
| `bad_request` / `400` | 参数不对 | 按 `message` 修正参数;别硬改着重试 |
|
|
452
|
+
| `not_deployed` | 该接口服务端还没发布 | 告诉用户"这是服务端缺口",不要反复重试 |
|
|
453
|
+
| `missing_skill` | `skill` 在本包里找不到 `SKILL.md`(包被裁剪/只拷了 `lib/`) | 换官方包 `npx -y wkgeo-cli@latest skill`;**不是**命令用错了,别让用户去排查 |
|
|
454
|
+
| `server` / `5xx` | 服务端错误 | 保留原文告知用户,可稍后重试 |
|
|
455
|
+
| `network` | 连不上/超时 | 检查网络后重试;`message` 里是原始错误(如 `getaddrinfo ENOTFOUND`) |
|
|
456
|
+
|
|
457
|
+
**通用规则**:先看 `error.hint` —— 那是 CLI 写好的"下一步";`message` 是服务端原文,
|
|
458
|
+
**原样转述给用户**,不要改写成"操作失败了"。
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## 7. 典型工作流
|
|
463
|
+
|
|
464
|
+
### 7.1 用户说"帮我建个站并付钱"
|
|
465
|
+
|
|
466
|
+
```bash
|
|
467
|
+
# ① 先确认登录状态(没登录会 exit 3)
|
|
468
|
+
npx -y wkgeo-cli@latest whoami
|
|
469
|
+
# exit 3 → 走 §5 的授权流程,等用户点完「同意」再继续
|
|
470
|
+
|
|
471
|
+
# ② 建站(名字由用户给;返回站点 id)
|
|
472
|
+
npx -y wkgeo-cli@latest sites create --name "我的新站"
|
|
473
|
+
# 读 data.site.id / data.site.owner_user_id(应等于 whoami 的 user_id)
|
|
474
|
+
# ⚠️ 若报 400「无法确定所属客户」:这个账号必须显式指定客户 →
|
|
475
|
+
# 让用户提供客户 ID,再加 --customer <id>(README §10 有说明)
|
|
476
|
+
|
|
477
|
+
# ③ 看能买什么 + 余额够不够
|
|
478
|
+
npx -y wkgeo-cli@latest products list
|
|
479
|
+
npx -y wkgeo-cli@latest wallet
|
|
480
|
+
|
|
481
|
+
# ④ 下单(余额够就当场付清;不够会 exit 1 且 kind=insufficient_balance)
|
|
482
|
+
npx -y wkgeo-cli@latest orders create --product storage_gb --units 1
|
|
483
|
+
# 读 data.paid / data.order_id / data.balance_after_cents
|
|
484
|
+
|
|
485
|
+
# ⑤ 复核 + 汇报
|
|
486
|
+
npx -y wkgeo-cli@latest wallet txns --limit 3
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
**每一步都必须停下来检查 `ok`**;失败时按 §6 处理,**不要**把一串命令一次性盲发。
|
|
490
|
+
|
|
491
|
+
### 7.2 用户说"帮我充值 100 块"
|
|
492
|
+
|
|
493
|
+
```bash
|
|
494
|
+
npx -y wkgeo-cli@latest wallet recharge --dry-run --amount-cents 10000 # 先彩排,确认金额
|
|
495
|
+
npx -y wkgeo-cli@latest wallet recharge --amount-cents 10000 # 生成待支付订单,并把收银台地址交出来(不扣款)
|
|
496
|
+
# ★ 读 data.pay_url —— 这是**我们网站的收银台页面**(https://pay.wkgeo.com/pay/?order_id=…&token=…)。
|
|
497
|
+
# ⚠️ **不要把 data.code_url 当成主流程丢给用户自己想办法**(那是微信内部支付链接,
|
|
498
|
+
# 手机直接扫不了;终端二维码只是"网页打不开"时的兜底)。
|
|
499
|
+
# ★ `token=` 是服务端在下单那一刻签发的**订单支付令牌**(只对这一单有效):
|
|
500
|
+
# 带上它,收银台在**没有网页登录态**的环境里(新开/无痕窗口、客户端内置浏览器等)
|
|
501
|
+
# 也能读这一单并支付
|
|
502
|
+
# (老板:"以那个 token 为基础来进行支付,而不是还要他用用户名那一套")。
|
|
503
|
+
# ⚠️ 老服务端没有这个字段时 `pay_url` 就只有 `order_id`,那条路仍然要求网页登录。
|
|
504
|
+
# ⚠️ **原样打开 data.pay_url**,不要自己拆参/重拼(尾斜杠与编码都有讲究)。
|
|
505
|
+
# ★ 打开方式见 §5.5:★ **CLI 默认就会尝试弹系统浏览器**(老板最终拍板走传统路线),
|
|
506
|
+
# 你只要看 data.open_browser:opened:true → 把 data.paste_text(已含完整地址 +
|
|
507
|
+
# "已为你打开浏览器…")原样念给用户即可;opened:false → 念那段**含完整地址**的提示,
|
|
508
|
+
# 或可选地在自己的内置浏览器里打开 data.pay_url。
|
|
509
|
+
# ⚠️ `data.open_browser.skipped_reason = "no_browser"` = 调用方明确要求别弹(那就只给地址)。
|
|
510
|
+
# ★ **支付成功后那个页面会自己关掉**(收银台会尝试 `window.close()`):
|
|
511
|
+
# 关掉了是正常的,**不要**让用户去找那个页面、也不要催他"点返回";
|
|
512
|
+
# 没关掉就让他手动关掉即可。用户回报"付完了"之后再执行下面那条查单。
|
|
513
|
+
npx -y wkgeo-cli@latest orders check <order_id> # 用户说付完了再查
|
|
514
|
+
# 读 data.paid / data.balance_text
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
⚠️ **AI 永远不要替用户"确认支付成功"**:只能查。查不到就是没付。
|
|
518
|
+
⚠️ `wallet recharge` 的 `--json` 里 `order_id` / `code_url` / `pay_url` **三个都在**:
|
|
519
|
+
自动化用 `pay_url`(打开自家收银台),不要把 `code_url` 当成"让用户扫码"的入口。
|
|
520
|
+
⚠️ `pay_url` 上可能带 `token=`(**订单支付令牌**,见上面 §7.2 的说明)。
|
|
521
|
+
* 它的存在就是为了让"**没有网页登录态**"的窗口(新开/无痕窗口、客户端内置浏览器等)也能付这一单 ——
|
|
522
|
+
**原样打开**即可,**不要**把 `token` 单独摘出来展示、更不要放进对话正文里当"验证码"念给用户;
|
|
523
|
+
* 它**只**授权"读这一单的支付参数":读不到钱包、读不到订单列表、不能下单、不能取消,
|
|
524
|
+
而且 30 分钟(= 订单有效期)后自动失效(失效时页面会明确说"支付链接已过期,请重新下单")。
|
|
525
|
+
⚠️ 无桌面环境(容器/CI)加 `--no-browser`:**完全不尝试**打开浏览器(`open_browser.skipped_reason = "no_browser"`),`pay_url` 照常输出。
|
|
526
|
+
⚠️ `--open-browser` **保留但已与默认行为等价**(默认就会尝试弹系统浏览器);想"不弹"只能用 `--no-browser`。
|
|
527
|
+
⚠️ 打开 `pay_url` 的方式见 **§5.5**:CLI **默认就会尝试弹系统浏览器**(老板最终拍板走传统路线);
|
|
528
|
+
只有它**没打开成功**时,才考虑可选地在客户端**内置浏览器 / 侧边面板**里打开。
|
|
529
|
+
|
|
530
|
+
### 7.3 用户说"查一下我的余额和订单"
|
|
531
|
+
|
|
532
|
+
```bash
|
|
533
|
+
npx -y wkgeo-cli@latest wallet
|
|
534
|
+
npx -y wkgeo-cli@latest wallet txns --limit 20
|
|
535
|
+
npx -y wkgeo-cli@latest orders list --limit 10
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### 7.4 用户说"取消某个应用的授权"
|
|
539
|
+
|
|
540
|
+
```bash
|
|
541
|
+
npx -y wkgeo-cli@latest apps list # 找到应用名与 client_id
|
|
542
|
+
npx -y wkgeo-cli@latest apps revoke <client_id> # 该应用所有令牌立即失效
|
|
543
|
+
# 若吊销的是 CLI 自己(client_id=wkgeo-cli),本地凭据随之失效 → 需要重新 login
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
**⭐ 对用户的话术(老板明确要求,务必照做)**:
|
|
547
|
+
|
|
548
|
+
* 用户想"取消授权 / 解绑 / 不想让你再访问我的账号"时,**让他直接跟 AI 说一句就行**
|
|
549
|
+
("帮我取消 XX 的授权")—— AI 侧执行 `apps list` → `apps revoke`,**不要**让用户自己去敲命令、
|
|
550
|
+
也不要把他指去后台找入口(他多半找不到,也看不懂);
|
|
551
|
+
* ⚠️ **用户可见的文案里不要出现终端命令、`client_id`、`apps revoke` 这类技术字眼**。
|
|
552
|
+
用户不需要知道我们是怎么做的,只需要知道**结果**;
|
|
553
|
+
* ✅ 这么说:「已经帮你把「XX 助手」的授权取消了,它现在不能再访问你的云站账号。」
|
|
554
|
+
* ❌ 别这么说:「请运行 `wkgeo apps revoke wkgeo-cli`」/「你的 client_id 是 wkgeo-cli」/
|
|
555
|
+
「我去调一下 revoke 接口」;
|
|
556
|
+
* 若取消的正是**当前这个 AI 所用的应用**(`apps list` 里该行 `is_current_cli: true`,
|
|
557
|
+
或 `client_id` 等于 `meta.client_id`):要**额外多说一句**「这会让我的登录失效,
|
|
558
|
+
如果你还想让我继续帮你操作,需要重新授权一次」—— 否则用户下次发现"AI 忽然不能用了"会困惑。
|
|
559
|
+
|
|
560
|
+
---
|
|
561
|
+
|
|
562
|
+
## 8. 安全(**必须遵守**)
|
|
563
|
+
|
|
564
|
+
1. **绝不打印/转述令牌**:不要 `cat ~/.wkongai/credentials.json`,不要把它放进任何请求体或日志。
|
|
565
|
+
只需要账号标识时用 `whoami`(CLI 保证只输出账号标识)。
|
|
566
|
+
2. **凭据文件等同密码**:不要复制到别处、不要上传、不要提交到仓库。
|
|
567
|
+
3. **回调只监听 `127.0.0.1`**(不要改 `--callback-host` 绑 `0.0.0.0`,那会把授权码暴露给同网段)。
|
|
568
|
+
4. **不要绕过安全边界**:不要为了"退款/改价/提权"去想办法伪造 step-up;
|
|
569
|
+
那些动作**只能由人在网页后台完成**。
|
|
570
|
+
5. **不要发起真实扣款**:`login`/`wallet recharge` 只创建订单/链接;
|
|
571
|
+
所有"付款"都必须由用户在**我们网站的收银台页面**用微信完成。自动化测试里**绝不**走真钱路径。
|
|
572
|
+
6. **收银台是我们自己的页面**:`wallet recharge` 的正确交接物是 `pay_url`
|
|
573
|
+
(`https://pay.wkgeo.com/pay/?order_id=…`),不是 `code_url`。
|
|
574
|
+
不要在本地自己画一个二维码页面让用户扫 —— 那是被明确否掉的做法。
|
|
575
|
+
7. **限流是保护,不是故障**:429 时按 `retry_after` 退避;**不要**用重新登录或换令牌来"绕过"限流。
|
|
576
|
+
8. 遇到 `client_secret`、`authorization_code`、`access_token` 之类的字段:
|
|
577
|
+
它们是凭据,**不要**记录到对话/文件里。
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
## 9. 常见坑速查
|
|
582
|
+
|
|
583
|
+
| 现象 | 原因 | 处理 |
|
|
584
|
+
| --- | --- | --- |
|
|
585
|
+
| `whoami` 返回 exit 3 | 还没登录 / 令牌过期 / 被吊销 | 跑 `login`,把网址给用户点同意 |
|
|
586
|
+
| 浏览器授权后显示"无法访问此网站" | 那一刻本机没有 CLI 在监听(login 被 Ctrl+C 或已退出);**或代理接管了 127.0.0.1** | 重新 `login`(开着代理时 CLI 会自动改设备码);并**保持它在前台**直到点完「同意」 |
|
|
587
|
+
| `login` 等到超时(`kind:"timeout"`) | 用户没点完,**或代理把回调吃掉了** | 看 `hint` 与 `meta.proxy_warning`:只要检测到代理,CLI 默认**已经**直接改设备码了;若用户加了 `--no-auto-device`,就把"代理可能吃掉回调"的警告转述给他 |
|
|
588
|
+
| 事件流第一行是 `awaiting_user` 而不是 `awaiting_callback` | 检测到系统代理 → **CLI 直接改用了设备码**(设计行为,不是出错;**绕过列表写了 127.* 也照样改**) | 按 §5.3 把 `paste_text` 转述给用户;想强制回调加 `--no-auto-device`(CLI 会警告风险) |
|
|
589
|
+
| 事件流是 `awaiting_callback`,然后 `data.auto_device.reason:"callback_first_wait"` | ★ 只在**检测到代理**时才会发生:首轮等待(默认 180 秒,可 `--callback-first-wait` 指定)没收到回调 → CLI 自动改设备码继续(**本地回调监听同时保留**) | 正常自愈;如实告诉用户"已同时改用设备码,两条路哪条先成功算哪条,不用重来" |
|
|
590
|
+
| 用户说「我**没开代理**,为什么变成设备码了」 | ★ 2026-10-03 已修:没有代理时 CLI **不再**启用首轮等待自愈(旧版默认 60 秒会误切)。若仍出现 `proxy_warning.detected:true`,那就是真的探到了系统代理(`config --json` 里能看来源) | 先看 `data.proxy_warning.detected`:为 `true` 才是代理的锅;为 `false` 却有 `reason:"callback_first_wait"` ⇒ 只可能是用户自己写了 `--callback-first-wait` |
|
|
591
|
+
| 用户随后才点【同意授权】→ 浏览器显示 **ERR_CONNECTION_REFUSED** | ★ 2026-10-03 已修:切设备码时**不再关掉**本地回调监听(旧版一关就是这个报错)。若仍出现,说明 CLI 进程已经退出(Ctrl+C / 终端关掉了) | 让用户直接在**这一次**的浏览器页面上点同意即可(监听还在),或重跑 `login`;不要让他以为授权失败 |
|
|
592
|
+
| `login --device-continue` 报 `kind:"expired"` | 短码过期(默认 10 分钟)或已被换过票 | 重新 `login --device --no-wait` 申请新短码;**别**反复续跑 |
|
|
593
|
+
| 下单直接返回 `paid:true` | 服务端设计:下单时余额足够就直接扣款并履约 | 这是正常的"钱包支付";用 `wallet` 复核余额 |
|
|
594
|
+
| `sites create` 报 400「无法确定所属客户」 | `owner/admin` 这类账号必须显式指定客户 | 加 `--customer <客户ID>` |
|
|
595
|
+
| 429 | 调用过于频繁 | 等 `retry_after` 秒;**不要**重新授权 |
|
|
596
|
+
| `apps list` 报"未部署" | 该环境的 data 云函数还没发版(**线上已发版并实测过**,见 README §9) | 告诉用户"这是服务端缺口",不要反复重试;`logout` 仍可清理本地登录态 |
|
|
597
|
+
| `skill` 报「当前安装包里没有手册文件(SKILL.md)」 | 跑的是**被裁剪的包**或只拷了 `lib/` 的开发目录(正常 npm 包一定有) | 换 `npx -y wkgeo-cli@latest skill`;这不是命令用错了(`error.kind="missing_skill"`,退出码 1) |
|
|
598
|
+
| 想升级 CLI | 全局安装不会自动升级 | 用 `npx -y wkgeo-cli@latest`,或 `npm i -g wkgeo-cli@latest` |
|