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/lib/page.mjs
CHANGED
|
@@ -21,9 +21,10 @@
|
|
|
21
21
|
* 3. **不依赖 JavaScript**:正文(结论 + 账号 + 兜底文案)全部是静态 HTML + CSS。
|
|
22
22
|
* 这个页面出现的时机很敏感(用户刚在同意页点完"同意"),任何脚本报错
|
|
23
23
|
* 都会让人以为授权失败,所以脚本只能做**锦上添花**的事,绝不能承载信息。
|
|
24
|
-
*
|
|
24
|
+
* 目前唯一的脚本是"尝试自动关闭标签页"(见 `autoCloseScript`):
|
|
25
25
|
* 它失败 / 被浏览器拒绝 / 被用户插件拦掉,页面照旧完整可读。
|
|
26
|
-
* ⚠️
|
|
26
|
+
* ⚠️ 0.2.4 起成功页与失败页都会带它(老板反馈「页面关不掉」),
|
|
27
|
+
* 差别只在延迟(成功 1.2s / 失败 6s),正文永远是静态可读的。
|
|
27
28
|
* 4. **所有动态内容必须转义**(`escapeHtml`)。账号标识来自服务端 `userinfo`,
|
|
28
29
|
* 其中昵称是**用户自己可控**的字符串,直接拼进 HTML 就等于在本机页面上
|
|
29
30
|
* 开一个自伤型 XSS(本机页面同样能去访问 `http://127.0.0.1` 上的其它服务)。
|
|
@@ -168,43 +169,27 @@ function badgeSvg(tone) {
|
|
|
168
169
|
}
|
|
169
170
|
|
|
170
171
|
/**
|
|
171
|
-
*
|
|
172
|
+
* 「尝试自动关闭标签页」的极简内联脚本。
|
|
172
173
|
*
|
|
173
174
|
* ## 为什么加这段(老板 2026-10-01 对比阿里云 MCP 的实测结论)
|
|
174
175
|
* 老板的原话:阿里云那套"点完授权,页面**自动就关了**,这样看着很正常"。
|
|
175
|
-
*
|
|
176
|
+
* 我们的现状是:跳回本机后留一张页面,**要用户自己动手关**——不够顺。
|
|
176
177
|
*
|
|
177
|
-
* ##
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
181
|
-
*
|
|
182
|
-
*
|
|
183
|
-
* * **只有** CLI 真的换到令牌、并且凭据已经写进本机之后的**那张成功页**才尝试关闭
|
|
184
|
-
* (对应 `login.mjs` 里 `api.token(...)` → `writeCredentials(...)` → `successPage(...)`
|
|
185
|
-
* 这个顺序,**不许调换**);
|
|
186
|
-
* * 中间态页面(例如"正在换取令牌…")即使将来真加了,也**不自动关闭**,统一由成功页收尾;
|
|
187
|
-
* * 错误页(state 失败 / 缺少授权码 / 授权未完成·拒绝 / 换票失败 / 405)**一律不关**,
|
|
188
|
-
* 详见 `errorPage` 的说明。
|
|
178
|
+
* ## 0.2.4(2026-10-21 老板反馈「127 那个页面关不掉」)
|
|
179
|
+
* **授权成功页和失败页都要能自动关闭**,避免用户卡在一个已经结束的页面上。
|
|
180
|
+
* 页面仍然保留「本页面可以关闭了 / 直接关闭标签页」的兜底文案(浏览器只允许脚本关闭
|
|
181
|
+
* **由脚本打开的**窗口,我们这张页大概率关不掉,文案就是兜底)。
|
|
182
|
+
* * 成功页:延迟 1.2 秒关(先让用户看清「授权成功」再关,上来就关会让人以为页面崩了);
|
|
183
|
+
* * 失败页:延迟 6 秒关(失败原因要留够时间看清楚,但绝不能永远留在屏幕上)。
|
|
189
184
|
*
|
|
190
|
-
* ##
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
* "用户自己打开的标签页",`window.close()` 通常被**静默忽略**
|
|
195
|
-
* (Chrome / Edge 控制台里会留一句 "Scripts may close only the windows that
|
|
196
|
-
* were opened by them",Firefox 类似)。
|
|
197
|
-
* 所以:
|
|
198
|
-
* * 这段脚本**不是**"关掉页面"的保证,只是"能关就顺手关掉";
|
|
199
|
-
* * 页面上那句「本页面可以关闭了」**必须保留**——它就是给关不掉的情况兜底的;
|
|
200
|
-
* * 绝不为了"看起来关掉了"去做跳转 / 假按钮 / alert(那才是骗用户)。
|
|
185
|
+
* ## 🔴 时机约束(阿里云 `mcp-remote` 踩过的坑,别翻回去)
|
|
186
|
+
* 只有 CLI **真的换到令牌、并且凭据已经写进本机之后**的那张成功页才算"确实成功";
|
|
187
|
+
* 收到 code 就宣告成功并关窗是不允许的(换票失败时用户已经被打发走,只剩疑惑)。
|
|
188
|
+
* 失败页虽然也自动关,但原因文案必须完整可读,且页面里**不出现任何令牌/授权码**。
|
|
201
189
|
*
|
|
202
190
|
* ## 为什么延迟 + 重试两次
|
|
203
|
-
* *
|
|
204
|
-
*
|
|
205
|
-
* 页面一闪而过同样让人发慌)。
|
|
206
|
-
* * **再试一次**:部分浏览器(含移动端内核)对第一次 `close()` 会忽略、
|
|
207
|
-
* 第二次才生效;也有的是 DOM 尚未稳定时忽略。重试成本几乎为零,所以试两次。
|
|
191
|
+
* * **延迟**:先让用户看清结论;
|
|
192
|
+
* * **再试一次**:部分浏览器(含移动端内核)对第一次 `close()` 会忽略、第二次才生效。
|
|
208
193
|
* 两次之后就**不再骚扰**——继续调下去只会刷一屏控制台报错。
|
|
209
194
|
*
|
|
210
195
|
* ## 约束(单测钉住)
|
|
@@ -212,8 +197,12 @@ function badgeSvg(tone) {
|
|
|
212
197
|
* 不用 `eval`;整段脚本不碰 DOM,也不接收任何数据(页面里没有令牌,脚本里更没有)。
|
|
213
198
|
* * 用 `try/catch` 包住:某些内嵌浏览器(Electron / 企业管控浏览器)里
|
|
214
199
|
* `window.close` 可能直接被禁用甚至抛异常,不能因此弄脏页面。
|
|
200
|
+
*
|
|
201
|
+
* @param {number} delayMs 首次尝试关闭前的延迟(毫秒;必须是有限正数)
|
|
215
202
|
*/
|
|
216
|
-
|
|
203
|
+
function autoCloseScript(delayMs) {
|
|
204
|
+
const delay = Number.isFinite(delayMs) && delayMs >= 0 ? Math.round(delayMs) : 1200;
|
|
205
|
+
return `<script>
|
|
217
206
|
(function(){
|
|
218
207
|
var n = 0;
|
|
219
208
|
function tryClose(){
|
|
@@ -221,9 +210,10 @@ const AUTO_CLOSE_SCRIPT = `<script>
|
|
|
221
210
|
try { window.close(); } catch (e) { /* 内嵌浏览器可能直接禁用,忽略 */ }
|
|
222
211
|
if (n < 2) setTimeout(tryClose, 700);
|
|
223
212
|
}
|
|
224
|
-
setTimeout(tryClose,
|
|
213
|
+
setTimeout(tryClose, ${delay});
|
|
225
214
|
})();
|
|
226
215
|
</script>`;
|
|
216
|
+
}
|
|
227
217
|
|
|
228
218
|
/**
|
|
229
219
|
* 拼出整份文档。
|
|
@@ -237,11 +227,11 @@ const AUTO_CLOSE_SCRIPT = `<script>
|
|
|
237
227
|
* @param {string} [spec.note] 小字说明
|
|
238
228
|
* @param {string} [spec.hint] 更小的补充提示
|
|
239
229
|
* @param {boolean} [spec.autoClose] 是否附上"尝试自动关闭标签页"的脚本。
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
*
|
|
244
|
-
*
|
|
230
|
+
* 0.2.4 起**成功页与失败页都打开它**(老板反馈「页面关不掉」),差别只在延迟:
|
|
231
|
+
* 成功页 1.2 秒(先看清"授权成功"),失败页 6 秒(先看清原因)。
|
|
232
|
+
* 打开它的页面**必须同时**有「本页面可以关闭了 / 直接关闭标签页」这类兜底文案
|
|
233
|
+
* (浏览器大概率不允许脚本关闭"用户自己打开的"标签页)。
|
|
234
|
+
* @param {number} [spec.autoCloseDelayMs] 首次尝试关闭前的延迟(默认 1200ms)
|
|
245
235
|
*/
|
|
246
236
|
export function renderPage({
|
|
247
237
|
title,
|
|
@@ -252,6 +242,7 @@ export function renderPage({
|
|
|
252
242
|
note,
|
|
253
243
|
hint,
|
|
254
244
|
autoClose = false,
|
|
245
|
+
autoCloseDelayMs = 1200,
|
|
255
246
|
} = {}) {
|
|
256
247
|
const parts = [badgeSvg(tone), `<h1>${escapeHtml(heading)}</h1>`];
|
|
257
248
|
if (lead) parts.push(`<p class="lead">${escapeHtml(lead)}</p>`);
|
|
@@ -275,7 +266,7 @@ export function renderPage({
|
|
|
275
266
|
<main class="card" role="main">
|
|
276
267
|
${parts.join('\n')}
|
|
277
268
|
</main>
|
|
278
|
-
<footer>悟空AI · 云站平台</footer>${autoClose ? `\n${
|
|
269
|
+
<footer>悟空AI · 云站平台</footer>${autoClose ? `\n${autoCloseScript(autoCloseDelayMs)}` : ''}
|
|
279
270
|
</body>
|
|
280
271
|
</html>`;
|
|
281
272
|
return { title, html };
|
|
@@ -292,11 +283,11 @@ ${parts.join('\n')}
|
|
|
292
283
|
*
|
|
293
284
|
* 文案分区(老板指定):大标题 / 副文案(回终端)/ 已登录账号 / 小字说明。
|
|
294
285
|
*
|
|
295
|
-
* ##
|
|
286
|
+
* ## 会尝试自动关闭标签页的页面之一(`autoClose: true`)
|
|
296
287
|
* 这张页只在 `login.mjs` 里 **`api.token(...)` 换到令牌 → `writeCredentials(...)` 写盘之后**
|
|
297
288
|
* 才被返回,所以"授权成功"是**已经落地的事实**,不是"收到了 code"这种半成品状态
|
|
298
289
|
* —— 这是与阿里云 `mcp-remote` 最关键的区别(它收到 code 就宣告成功并关窗,换票失败时
|
|
299
|
-
* 用户已经被打发走了)。详见 `
|
|
290
|
+
* 用户已经被打发走了)。详见 `autoCloseScript` 的注释。
|
|
300
291
|
* ⚠️ 副文案里的「本页面可以关闭了」和 hint 里的"直接关闭标签页"是**兜底**,
|
|
301
292
|
* 关不掉时用户照着做就行,两条都不许删(单测钉住)。
|
|
302
293
|
*
|
|
@@ -372,25 +363,31 @@ export function alreadyDonePage(account) {
|
|
|
372
363
|
* @param {string} spec.heading 大标题(短,一眼能懂)
|
|
373
364
|
* @param {string} spec.reason 明确的中文原因
|
|
374
365
|
* @param {string} [spec.hint] 补充说明(例如"没有写入任何凭据")
|
|
366
|
+
* @param {boolean} [spec.autoClose] 是否尝试自动关闭(默认 **true**,0.2.4 老板反馈"关不掉")
|
|
367
|
+
* @param {number} [spec.autoCloseDelayMs] 首次关闭延迟(默认 6000ms:先让用户看清原因)
|
|
375
368
|
*
|
|
376
|
-
* ##
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
* ⚠️
|
|
384
|
-
* 真要改这条口径,请先读完 `AUTO_CLOSE_SCRIPT` 上面那段(阿里云 `mcp-remote` 的教训)。
|
|
369
|
+
* ## 0.2.4(2026-10-21 老板反馈「127 那个页面关不掉」)
|
|
370
|
+
* 失败页**也**尝试自动关闭,但延迟 6 秒 —— 既不让用户卡死在一个已经结束的页面上,
|
|
371
|
+
* 也留够时间看清"为什么没成"。正文(原因 + 回终端重试)全部是静态 HTML,
|
|
372
|
+
* 脚本关不掉时页面照旧可读,且带「直接关闭标签页」的兜底文案。
|
|
373
|
+
* ⚠️ 仍然**不允许**把失败页做成"一出来就关":失败原因是排障的唯一线索,
|
|
374
|
+
* 而且其中"state 校验失败 / 缺少授权码 / 405"出现时 CLI 还在等真正的回调,
|
|
375
|
+
* 用户可能正照着页面说明重试。延迟关闭是这两者之间的折中。
|
|
376
|
+
* ⚠️ 任何页面都绝不回显 code / 令牌(模板里没有接收它们的位置)。
|
|
385
377
|
*/
|
|
386
|
-
export function errorPage({ tone = 'error', heading, reason, hint } = {}) {
|
|
378
|
+
export function errorPage({ tone = 'error', heading, reason, hint, autoClose = true, autoCloseDelayMs = 6000 } = {}) {
|
|
379
|
+
const seconds = Math.max(1, Math.round(autoCloseDelayMs / 1000));
|
|
380
|
+
const closeHint = autoClose
|
|
381
|
+
? `本页面会在约 ${seconds} 秒后自动关闭;如果没有关掉,直接关闭标签页即可。`
|
|
382
|
+
: '看完后直接关闭标签页即可。';
|
|
387
383
|
return renderPage({
|
|
388
384
|
tone,
|
|
389
385
|
title: `${heading} · 悟空AI`,
|
|
390
386
|
heading,
|
|
391
387
|
lead: reason,
|
|
392
388
|
note: hint,
|
|
393
|
-
hint: RETRY_HINT
|
|
394
|
-
|
|
389
|
+
hint: `${RETRY_HINT} ${closeHint}`,
|
|
390
|
+
autoClose,
|
|
391
|
+
autoCloseDelayMs,
|
|
395
392
|
});
|
|
396
393
|
}
|