@geoly-ai/social-hub-cli 0.3.25 → 0.3.26
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/CHANGELOG.md +6 -0
- package/dist/cli-error-channels.test.d.ts +2 -0
- package/dist/cli-error-channels.test.d.ts.map +1 -0
- package/dist/cli-error-channels.test.js +216 -0
- package/dist/cli-error-channels.test.js.map +1 -0
- package/dist/cmd-manifest.json +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -6
- package/dist/index.js.map +1 -1
- package/dist/output.d.ts +32 -0
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +122 -0
- package/dist/output.js.map +1 -1
- package/package.json +3 -3
- package/skills/manifest.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to `@geoly-ai/social-hub-cli` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
Release workflow: `pnpm release:cli` → `pnpm publish:cli`.
|
|
6
6
|
|
|
7
|
+
## [0.3.26] - 2026-08-27
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- 错误边界成为默认行为 —— 修 320 条命令的静默失败(D-06)
|
|
12
|
+
|
|
7
13
|
## [0.3.25] - 2026-08-25
|
|
8
14
|
|
|
9
15
|
### Added
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-error-channels.test.d.ts","sourceRoot":"","sources":["../src/cli-error-channels.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* D-06 回归:CLI 的静默失败 —— 错误变成 Node 原生 unhandled rejection,
|
|
3
|
+
* stdout 一个字节都没有。
|
|
4
|
+
*
|
|
5
|
+
* 现象:`accounts list -n 300` 超过服务端 limit 上限(200),服务端正确返回 400、
|
|
6
|
+
* SDK 正确 throw,但 CLI **stdout 零字节**、stderr 是 Node 原生堆栈
|
|
7
|
+
* (`throw new SocialHubHttpError` + `Node.js v25.x`)。运营侧据此拿到过一次假的
|
|
8
|
+
* 「全队 0 个受标账号」结论。
|
|
9
|
+
*
|
|
10
|
+
* 根因两条:① commander 的同步 `program.parse()` 不 await async action handler
|
|
11
|
+
* 的 promise;② 392 处 `.action(` 里只有 72 处包了 `withCliErrorBoundary`。
|
|
12
|
+
*
|
|
13
|
+
* 这类 bug 的验证必须**钉住输出通道本身**,只断言「有错误」是咬不住的:
|
|
14
|
+
* - 修复前后**退出码都是 1**(Node 对 unhandled rejection 默认就退 1),
|
|
15
|
+
* 所以退出码单独一条断言无法区分 —— 必须同时断言 stdout 零字节 +
|
|
16
|
+
* stderr 是 CLI 格式化文案(**不含** Node 堆栈标记)。
|
|
17
|
+
* - ⚠️ 三个通道**分开断言,绝不用 `2>&1` 合并** —— 合并后无法分辨错误到底
|
|
18
|
+
* 打到了哪个通道(运营侧上一轮就是这么误判「CLI 把错误打到了 stdout」的)。
|
|
19
|
+
* - 成功路径另有一条回归断言:stdout 必须是**干净可解析的 JSON**,
|
|
20
|
+
* stderr 零字节 —— 防止修复时把错误/提示信息漏进 stdout。
|
|
21
|
+
*/
|
|
22
|
+
import { execFile, execFileSync } from "node:child_process";
|
|
23
|
+
import { createServer } from "node:http";
|
|
24
|
+
import { existsSync, mkdtempSync } from "node:fs";
|
|
25
|
+
import { tmpdir } from "node:os";
|
|
26
|
+
import { dirname, join } from "node:path";
|
|
27
|
+
import { fileURLToPath } from "node:url";
|
|
28
|
+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|
29
|
+
const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
|
|
30
|
+
const cliEntry = join(pkgRoot, "dist", "index.js");
|
|
31
|
+
/** Node 原生 unhandled rejection / 未捕获异常的痕迹 —— 出现即说明边界没生效。 */
|
|
32
|
+
const NODE_CRASH_MARKERS = [
|
|
33
|
+
/^\s+at .+$/m, // 堆栈帧
|
|
34
|
+
/\bNode\.js v\d/, // Node 崩溃尾注
|
|
35
|
+
/SocialHubHttpError:/, // 原始 Error 名 + 堆栈头
|
|
36
|
+
/UnhandledPromiseRejection/,
|
|
37
|
+
];
|
|
38
|
+
let server;
|
|
39
|
+
let baseUrl;
|
|
40
|
+
let configDir;
|
|
41
|
+
/**
|
|
42
|
+
* 服务端实际收到的请求 —— 用于证明 CLI 既没有偷偷把 limit clamp 成 200,
|
|
43
|
+
* 也没有在 400 之后偷偷用更小的 limit 重试一次「补救」。
|
|
44
|
+
*/
|
|
45
|
+
const seenRequests = [];
|
|
46
|
+
/** 触发「服务端 code 撞 commander 命名空间」这一变体的 limit 值。 */
|
|
47
|
+
const HOSTILE_CODE_LIMIT = "301";
|
|
48
|
+
/** 触发「超长错误消息」这一变体的 limit 值。 */
|
|
49
|
+
const LONG_MESSAGE_LIMIT = "302";
|
|
50
|
+
/**
|
|
51
|
+
* 远大于任何管道缓冲(macOS 管道通常 64KB)的错误消息 —— 用来证明
|
|
52
|
+
* `process.exit()` 之前的 stderr 写不会被截断。
|
|
53
|
+
*/
|
|
54
|
+
const LONG_MESSAGE = `LONGERR-${"x".repeat(300_000)}-ENDOFLONGERR`;
|
|
55
|
+
function runCli(args) {
|
|
56
|
+
return new Promise((resolve) => {
|
|
57
|
+
// ⚠️ 不合并通道:execFile 天然把 stdout / stderr 分开给回来。
|
|
58
|
+
execFile(process.execPath, [cliEntry, ...args], {
|
|
59
|
+
env: {
|
|
60
|
+
...process.env,
|
|
61
|
+
VITEST: "",
|
|
62
|
+
SOCIAL_HUB_CONFIG_DIR: configDir,
|
|
63
|
+
SOCIAL_HUB_API_URL: baseUrl,
|
|
64
|
+
SOCIAL_HUB_API_KEY: "test-key",
|
|
65
|
+
SOCIAL_HUB_TEAM_ID: "11111111-1111-1111-1111-111111111111",
|
|
66
|
+
// 关掉自更新/skill 同步,否则它们的提示会污染 stderr 断言。
|
|
67
|
+
SOCIAL_HUB_NO_UPDATE_CHECK: "1",
|
|
68
|
+
SOCIAL_HUB_NO_SKILL_CHECK: "1",
|
|
69
|
+
SOCIAL_HUB_NO_AUTO_UPDATE: "1",
|
|
70
|
+
SOCIAL_HUB_NO_SKILLS_AUTO_UPDATE: "1",
|
|
71
|
+
},
|
|
72
|
+
// 入口若回归成「不 await 的 promise」,子进程可能永远不退 —— 给硬超时,
|
|
73
|
+
// 让它以失败告终而不是把测试挂死。
|
|
74
|
+
timeout: 30_000,
|
|
75
|
+
}, (err, stdout, stderr) => {
|
|
76
|
+
const code = err && typeof err.code === "number"
|
|
77
|
+
? err.code
|
|
78
|
+
: err
|
|
79
|
+
? 1
|
|
80
|
+
: 0;
|
|
81
|
+
resolve({ code, stdout, stderr });
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
beforeAll(async () => {
|
|
86
|
+
// 必须跑真实的 bin(dist/index.js):这条 bug 的根因在**进程入口**
|
|
87
|
+
// (parse vs parseAsync + 顶层边界),在 vitest 进程内 import program 再
|
|
88
|
+
// parseAsync 是绕过根因的假验证。CI 的 `turbo run test` 只 build 依赖包、
|
|
89
|
+
// 不 build cli 自己(turbo.json 的 test 只 dependsOn ["^build"]),所以这里
|
|
90
|
+
// 显式编译一次(约 2s),同时保证变异验证跑的是改过的源码而不是陈旧的 dist。
|
|
91
|
+
//
|
|
92
|
+
// 编译目标就用规范的 `dist/` 而不是唯一临时目录:临时目录不在 .gitignore 里,
|
|
93
|
+
// 测试崩溃时残留会把后续 `prettier --check .` 搞红;而 dist 的并发写在本仓不
|
|
94
|
+
// 成立 —— 本包只有这一个测试碰 dist,CI 里 `pnpm run test` 与 `pnpm run build`
|
|
95
|
+
// 是先后两步,不并行。
|
|
96
|
+
// 直接用 node 跑 typescript 的 JS 入口,不走 node_modules/.bin/tsc ——
|
|
97
|
+
// 后者在 Windows 上是 tsc.cmd,execFileSync 直接执行会失败(Codex 评审提出)。
|
|
98
|
+
try {
|
|
99
|
+
execFileSync(process.execPath, [join(pkgRoot, "node_modules", "typescript", "bin", "tsc")], { cwd: pkgRoot, stdio: "pipe" });
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
// stdio:"pipe" 会吞掉 tsc 的诊断,失败时必须把它回显出来,
|
|
103
|
+
// 否则只看到一句「命令失败」根本查不动。
|
|
104
|
+
const { stdout, stderr } = e;
|
|
105
|
+
throw new Error(`CLI tsc build failed:\n${stdout?.toString() ?? ""}\n${stderr?.toString() ?? ""}`);
|
|
106
|
+
}
|
|
107
|
+
expect(existsSync(cliEntry)).toBe(true);
|
|
108
|
+
configDir = mkdtempSync(join(tmpdir(), "social-hub-cli-d06-"));
|
|
109
|
+
server = createServer((req, res) => {
|
|
110
|
+
const url = new URL(req.url ?? "/", "http://stub.local");
|
|
111
|
+
const limit = url.searchParams.get("limit");
|
|
112
|
+
seenRequests.push({ path: url.pathname, limit });
|
|
113
|
+
if (Number(limit ?? "50") > 200) {
|
|
114
|
+
// 与生产一致:超过上限直接 400,不静默截断。
|
|
115
|
+
// limit 的具体值另用来选不同的 400 变体(见下面两条对抗性用例)。
|
|
116
|
+
res.writeHead(400, { "content-type": "application/json" });
|
|
117
|
+
const variant = limit === HOSTILE_CODE_LIMIT
|
|
118
|
+
? // 服务端可控的 code 故意撞 commander 的命名空间。
|
|
119
|
+
{ code: "commander.helpDisplayed", message: "limit must be <= 200" }
|
|
120
|
+
: limit === LONG_MESSAGE_LIMIT
|
|
121
|
+
? { code: "VALIDATION_ERROR", message: LONG_MESSAGE }
|
|
122
|
+
: { code: "VALIDATION_ERROR", message: "limit must be <= 200" };
|
|
123
|
+
res.end(JSON.stringify({ error: variant }));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
res.writeHead(200, { "content-type": "application/json" });
|
|
127
|
+
res.end(JSON.stringify({ items: [], total: 0 }));
|
|
128
|
+
});
|
|
129
|
+
await new Promise((resolve) => {
|
|
130
|
+
server.listen(0, "127.0.0.1", resolve);
|
|
131
|
+
});
|
|
132
|
+
const addr = server.address();
|
|
133
|
+
if (typeof addr === "string" || addr === null)
|
|
134
|
+
throw new Error("no port");
|
|
135
|
+
baseUrl = `http://127.0.0.1:${addr.port}`;
|
|
136
|
+
}, 120_000);
|
|
137
|
+
afterAll(async () => {
|
|
138
|
+
await new Promise((resolve) => server.close(() => resolve()));
|
|
139
|
+
});
|
|
140
|
+
describe("CLI 错误的输出通道纪律(D-06)", () => {
|
|
141
|
+
it("失败路径:stdout 零字节 / stderr 是 CLI 格式化错误 / 退出码 1", async () => {
|
|
142
|
+
seenRequests.length = 0;
|
|
143
|
+
const { code, stdout, stderr } = await runCli([
|
|
144
|
+
"accounts",
|
|
145
|
+
"list",
|
|
146
|
+
"-n",
|
|
147
|
+
"300",
|
|
148
|
+
]);
|
|
149
|
+
// ① stdout 通道:必须一个字节都没有(下游 `jq` 消费的是这一路)。
|
|
150
|
+
expect(stdout).toBe("");
|
|
151
|
+
// ② stderr 通道:必须有 CLI 格式化过的错误,且**不含** Node 原生堆栈痕迹。
|
|
152
|
+
expect(stderr).toContain("HTTP 400");
|
|
153
|
+
expect(stderr).toContain("limit must be <= 200");
|
|
154
|
+
for (const marker of NODE_CRASH_MARKERS) {
|
|
155
|
+
expect(stderr).not.toMatch(marker);
|
|
156
|
+
}
|
|
157
|
+
// 只能打**一遍**:per-command boundary 与顶层 boundary 双打会让运营
|
|
158
|
+
// 以为发生了两次失败。
|
|
159
|
+
expect(stderr.match(/limit must be <= 200/g)).toHaveLength(1);
|
|
160
|
+
// ③ 退出码:精确为 1(只断言「非 0」太松 —— Node 对 unhandled rejection
|
|
161
|
+
// 默认也退 1,修复前后都能过)。
|
|
162
|
+
expect(code).toBe(1);
|
|
163
|
+
// ④ 硬要求:必须真的把 300 原样打到服务端并让它报错 ——
|
|
164
|
+
// 既不能在 CLI 侧悄悄 clamp 成 200,也不能 400 之后偷偷用更小的 limit
|
|
165
|
+
// 重试一次凑出「成功」。所以断言**只发生一次**请求且 limit 就是 300。
|
|
166
|
+
expect(seenRequests).toHaveLength(1);
|
|
167
|
+
expect(seenRequests[0]?.limit).toBe("300");
|
|
168
|
+
expect(seenRequests[0]?.path).toContain("/accounts");
|
|
169
|
+
}, 60_000);
|
|
170
|
+
it("成功路径回归:stdout 是干净可解析的 JSON,stderr 零字节", async () => {
|
|
171
|
+
const { code, stdout, stderr } = await runCli([
|
|
172
|
+
"accounts",
|
|
173
|
+
"list",
|
|
174
|
+
"-n",
|
|
175
|
+
"50",
|
|
176
|
+
]);
|
|
177
|
+
expect(code).toBe(0);
|
|
178
|
+
expect(stderr).toBe("");
|
|
179
|
+
// 不能有任何提示/日志漏进 stdout —— 整段必须能被 JSON.parse 吃掉。
|
|
180
|
+
expect(() => JSON.parse(stdout)).not.toThrow();
|
|
181
|
+
expect(JSON.parse(stdout)).toEqual({ items: [], total: 0 });
|
|
182
|
+
}, 60_000);
|
|
183
|
+
it("服务端把 error.code 伪装成 `commander.*` 也不能让 CLI 静默退出", async () => {
|
|
184
|
+
// 顶层边界会放行 commander 自己的出口(--help 等)。但 `code` 是**服务端
|
|
185
|
+
// 响应体里的字段**、外部可控 —— 如果放行判定只看 `code` 前缀,服务端返回
|
|
186
|
+
// `{"error":{"code":"commander.helpDisplayed"}}` 就能让 CLI 一个字节都不打印
|
|
187
|
+
// 地退出,等于把 D-06 换个入口重造一遍。判定必须同时要求
|
|
188
|
+
// `name === "CommanderError"` 和合法整数 `exitCode`。
|
|
189
|
+
const { code, stdout, stderr } = await runCli([
|
|
190
|
+
"accounts",
|
|
191
|
+
"list",
|
|
192
|
+
"-n",
|
|
193
|
+
HOSTILE_CODE_LIMIT,
|
|
194
|
+
]);
|
|
195
|
+
expect(stdout).toBe("");
|
|
196
|
+
expect(stderr).not.toBe("");
|
|
197
|
+
expect(stderr).toContain("HTTP 400");
|
|
198
|
+
expect(code).toBe(1);
|
|
199
|
+
}, 60_000);
|
|
200
|
+
it("超长错误消息不被 process.exit 截断(stderr 必须完整)", async () => {
|
|
201
|
+
// console.error 对管道的写在 macOS 上是异步的,紧跟 process.exit() 会丢尾巴。
|
|
202
|
+
// 断言开头和**结尾哨兵**都在,才能证明整条消息真的写完了。
|
|
203
|
+
const { code, stdout, stderr } = await runCli([
|
|
204
|
+
"accounts",
|
|
205
|
+
"list",
|
|
206
|
+
"-n",
|
|
207
|
+
LONG_MESSAGE_LIMIT,
|
|
208
|
+
]);
|
|
209
|
+
expect(stdout).toBe("");
|
|
210
|
+
expect(code).toBe(1);
|
|
211
|
+
expect(stderr).toContain("LONGERR-");
|
|
212
|
+
expect(stderr).toContain("-ENDOFLONGERR");
|
|
213
|
+
expect(stderr.length).toBeGreaterThan(300_000);
|
|
214
|
+
}, 60_000);
|
|
215
|
+
});
|
|
216
|
+
//# sourceMappingURL=cli-error-channels.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-error-channels.test.js","sourceRoot":"","sources":["../src/cli-error-channels.test.ts"],"names":[],"mappings":"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"}
|
package/dist/cmd-manifest.json
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8DpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAqiF9B,8DAA8D;AAC9D,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,IAAI,CAOf;AAg+BD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE,CAAC;AAQF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE;IACN,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,GACA,OAAO,CAAC;IAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAkJnE;AAyqBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sBAAsB,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE,GAAG,MAAM,CAiBT;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAyCpD;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8DpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAqiF9B,8DAA8D;AAC9D,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,IAAI,CAOf;AAg+BD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE,CAAC;AAQF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE;IACN,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,GACA,OAAO,CAAC;IAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAkJnE;AAyqBD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,sBAAsB,CAAC,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpE,GAAG,MAAM,CAiBT;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CAyCpD;AAwyDD,OAAO,EAAE,OAAO,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -18,7 +18,7 @@ import { registerDoctorAndVersion } from "./register-shared.js";
|
|
|
18
18
|
import { registerSkillsCommands } from "./register-skills.js";
|
|
19
19
|
import { readJsonArgOrExit, resolveJsonArg } from "./lib/json-arg.js";
|
|
20
20
|
import { applyContentReviewFlags, AUTHOR_RELATION_MISMATCH_HINT, ContentReviewArgError, } from "./lib/content-review-args.js";
|
|
21
|
-
import { withCliErrorBoundary } from "./output.js";
|
|
21
|
+
import { runCliProgram, withCliErrorBoundary } from "./output.js";
|
|
22
22
|
import { maybeNotifyUpdate } from "./update-check.js";
|
|
23
23
|
import { maybeNotifySkillUpdate } from "./skill-update-check.js";
|
|
24
24
|
import { getCliVersion } from "./version-info.js";
|
|
@@ -4721,12 +4721,20 @@ brandAuth
|
|
|
4721
4721
|
console.log(JSON.stringify(res, null, 2));
|
|
4722
4722
|
});
|
|
4723
4723
|
// Only parse when run directly (not when imported for testing)
|
|
4724
|
+
//
|
|
4725
|
+
// D-06:必须是 `parseAsync` + 顶层 `runCliProgram` 边界。
|
|
4726
|
+
// 旧写法是同步 `program.parse()`,commander 不会 await async action handler 的
|
|
4727
|
+
// promise,于是任何 handler 抛出都变成 Node 原生 unhandled rejection ——
|
|
4728
|
+
// stdout 零字节、stderr 是原始堆栈,看起来「什么都没发生」。
|
|
4729
|
+
// 换成 parseAsync 后,handler 的 promise 会串回这里,`runCliProgram` 这一个
|
|
4730
|
+
// catch 就是全部命令的默认错误边界(见 output.ts 的说明)。
|
|
4724
4731
|
if (!process.env["VITEST"]) {
|
|
4725
|
-
void
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4732
|
+
void runCliProgram(async () => {
|
|
4733
|
+
await Promise.allSettled([
|
|
4734
|
+
maybeNotifyUpdate(process.argv),
|
|
4735
|
+
maybeNotifySkillUpdate(process.argv),
|
|
4736
|
+
]);
|
|
4737
|
+
await program.parseAsync();
|
|
4730
4738
|
});
|
|
4731
4739
|
}
|
|
4732
4740
|
export { program };
|