@geoly-ai/social-hub-cli 0.3.26 → 0.3.28

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/dist/output.d.ts CHANGED
@@ -5,6 +5,16 @@ export declare function resolveOutputFormat(opts: {
5
5
  export declare function printResult(data: unknown, format?: OutputFormat): void;
6
6
  export declare function parseJsonOption(raw: string | undefined): Record<string, unknown>;
7
7
  export declare function formatCliError(e: unknown): string;
8
+ /**
9
+ * 把一行文本**同步**写进 stderr,写完再返回(D-06)。
10
+ *
11
+ * 不能用 `console.error` + `process.exit()`:Node 对管道的写在 **macOS 上是异步的**
12
+ * (Windows/Linux 同步),`process.exit()` 会把还没刷出去的尾巴截掉。错误消息一长
13
+ * (服务端返回超长 message 时)就会丢尾,于是「报错了但看不全」——这正是 D-06
14
+ * 那种「输出通道不可信」的同一类毛病。`fs.writeSync` 循环写到写完为止,既不截断,
15
+ * 也不需要为了等 flush 而放弃 `process.exit`(留句柄的 handler 会让进程挂死)。
16
+ */
17
+ export declare function writeStderrSync(text: string): void;
8
18
  /**
9
19
  * per-command 错误边界(历史产物,392 处 `.action(` 里只有 72 处包了它)。
10
20
  *
@@ -36,6 +46,11 @@ export declare function withCliErrorBoundary(run: () => Promise<void>): Promise<
36
46
  * ⚠️ 这里不做任何「降级/截断/兜底成空结果」:比如 `accounts list -n 300` 超出
37
47
  * 服务端上限 200 时必须报错退出,绝不能悄悄按 200 返回 —— 那只会把一种静默失败
38
48
  * 换成另一种假数据。
49
+ *
50
+ * ⚠️ **同一套纪律也适用于「成功但不完整」的返回**:响应体里 `items.length < total`
51
+ * 时,警告走 **stderr**、stdout 保持干净可解析、**退出码不变**(不完整不是错误,
52
+ * 是需要被看见的事实)。落点在 `client.ts` 的 `partialListSink`,复用本文件的
53
+ * `writeStderrSync` —— 不要另起一套 stderr 写法。
39
54
  */
40
55
  export declare function runCliProgram(parseArgv: () => Promise<unknown>): Promise<void>;
41
56
  //# sourceMappingURL=output.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AAM/D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CAI3E;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,OAAO,EACb,MAAM,GAAE,YAAqB,GAC5B,IAAI,CA0BN;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,GAAG,SAAS,GACtB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CA0CjD;AAwCD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GACvB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CAqBf"}
1
+ {"version":3,"file":"output.d.ts","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;AAM/D,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,YAAY,CAI3E;AAED,wBAAgB,WAAW,CACzB,IAAI,EAAE,OAAO,EACb,MAAM,GAAE,YAAqB,GAC5B,IAAI,CA0BN;AAED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,GAAG,SAAS,GACtB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzB;AAED,wBAAgB,cAAc,CAAC,CAAC,EAAE,OAAO,GAAG,MAAM,CA0CjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAsBlD;AAOD;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GACvB,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAChC,OAAO,CAAC,IAAI,CAAC,CAqBf"}
package/dist/output.js CHANGED
@@ -92,7 +92,7 @@ export function formatCliError(e) {
92
92
  * 那种「输出通道不可信」的同一类毛病。`fs.writeSync` 循环写到写完为止,既不截断,
93
93
  * 也不需要为了等 flush 而放弃 `process.exit`(留句柄的 handler 会让进程挂死)。
94
94
  */
95
- function writeStderrSync(text) {
95
+ export function writeStderrSync(text) {
96
96
  const bytes = Buffer.from(`${text}\n`, "utf8");
97
97
  let offset = 0;
98
98
  while (offset < bytes.length) {
@@ -160,6 +160,11 @@ export async function withCliErrorBoundary(run) {
160
160
  * ⚠️ 这里不做任何「降级/截断/兜底成空结果」:比如 `accounts list -n 300` 超出
161
161
  * 服务端上限 200 时必须报错退出,绝不能悄悄按 200 返回 —— 那只会把一种静默失败
162
162
  * 换成另一种假数据。
163
+ *
164
+ * ⚠️ **同一套纪律也适用于「成功但不完整」的返回**:响应体里 `items.length < total`
165
+ * 时,警告走 **stderr**、stdout 保持干净可解析、**退出码不变**(不完整不是错误,
166
+ * 是需要被看见的事实)。落点在 `client.ts` 的 `partialListSink`,复用本文件的
167
+ * `writeStderrSync` —— 不要另起一套 stderr 写法。
163
168
  */
164
169
  export async function runCliProgram(parseArgv) {
165
170
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"output.js","sourceRoot":"","sources":["../src/output.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=partial-list-guard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partial-list-guard.test.d.ts","sourceRoot":"","sources":["../src/partial-list-guard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,327 @@
1
+ /**
2
+ * 「返回了不完整结果、但看起来像完整的」这一族缺陷的回归。
3
+ *
4
+ * 触发用例(运营侧生产实测):
5
+ * social-hub compliance blocklists-list --kind bot_bouncer
6
+ * → items: 100 条,但同一个返回体里 total: 112。CLI 当时**没有任何分页参数**,
7
+ * 所以通过 CLI 拿不到那 12 条,一条都拿不到。
8
+ *
9
+ * 为什么严重:运营侧 skill 明文要求 agent 跑这条命令做板块准入判断(命中即禁入),
10
+ * 所以 agent 每天在用一份不全的黑名单做安全判断,而且它不知道自己漏了。
11
+ * 漏的还不是随机 12 条 —— 默认按 `createdAt DESC`,漏掉的是**最早加入**的那批,
12
+ * 往往是最确定该封的。这个失效方式在 packages/db/src/subreddit-blocklists.ts
13
+ * 的注释里被预言过。
14
+ *
15
+ * 这一族此前已经复发四次(accounts list -n 300 静默空、pools catalog-list 默认单页 50、
16
+ * blocklists-list 截断 100、以及测试断言自己空过),所以这里验两件事:
17
+ * ① blocklists-list 默认自动翻页,最终必须给出**全部** 112 条,且必须**真的**
18
+ * 发出了第二次带 offset 的请求(只断言条数会被「stub 一次给 112 条」蒙混过去)。
19
+ * ② 通用收口:任何返回体同时有 items 与 total 且 items.length < total 时,
20
+ * CLI 在 **stderr** 打警告 —— stdout 保持干净可解析、退出码不变。
21
+ *
22
+ * ⚠️ 三个通道**分开断言,绝不用 `2>&1` 合并** —— 合并后就分不清警告到底打到了
23
+ * 哪个通道,而「警告漏进 stdout」正是会把下游 `jq` 打挂的那种回归
24
+ * (与 cli-error-channels.test.ts 同一条纪律,D-06)。
25
+ */
26
+ import { execFile, execFileSync } from "node:child_process";
27
+ import { createServer } from "node:http";
28
+ import { existsSync, mkdtempSync } from "node:fs";
29
+ import { tmpdir } from "node:os";
30
+ import { dirname, join } from "node:path";
31
+ import { fileURLToPath } from "node:url";
32
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
33
+ const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), "..");
34
+ const cliEntry = join(pkgRoot, "dist", "index.js");
35
+ /** 生产实测的那组数字:total 112,服务端单页上限 200、默认 100。 */
36
+ const BLOCKLIST_TOTAL = 112;
37
+ /** 服务端默认页大小 —— 缺陷现场就是「只拿到这么多」。 */
38
+ const SERVER_DEFAULT_LIMIT = 100;
39
+ /** accounts stub 的总条数(通用截断警告用)。 */
40
+ const ACCOUNTS_TOTAL = 7;
41
+ let server;
42
+ let baseUrl;
43
+ let configDir;
44
+ /** 服务端实际收到的每一次请求 —— 用来证明确实发生了第二次带 offset 的翻页。 */
45
+ const seenRequests = [];
46
+ /**
47
+ * 置真后,blocklist 端点每被请求一次就把 total 报大 1 ——
48
+ * 模拟「翻页期间一直有人在增删」,让任何一趟都无法自证一致。
49
+ */
50
+ let blocklistTotalDrifts = false;
51
+ /** drift 模式下已发生的请求次数。 */
52
+ let blocklistDriftTicks = 0;
53
+ function blocklistRow(index) {
54
+ return {
55
+ id: `00000000-0000-4000-8000-${String(index).padStart(12, "0")}`,
56
+ kind: "bot_bouncer",
57
+ subreddit: `sub_${index}`,
58
+ reason: null,
59
+ source: null,
60
+ expiresAt: null,
61
+ // createdAt DESC:index 越大越新。第一页给的是最新的那批,
62
+ // 漏掉的是 index 最小、也就是最早加入的那批。
63
+ createdAt: new Date(1_700_000_000_000 + index * 1000).toISOString(),
64
+ };
65
+ }
66
+ /** 全量 112 行,按 createdAt DESC(与生产排序一致)。 */
67
+ const ALL_ROWS = Array.from({ length: BLOCKLIST_TOTAL }, (_, i) => blocklistRow(BLOCKLIST_TOTAL - i));
68
+ function runCli(args) {
69
+ return new Promise((resolve) => {
70
+ // ⚠️ 不合并通道:execFile 天然把 stdout / stderr 分开给回来。
71
+ execFile(process.execPath, [cliEntry, ...args], {
72
+ env: {
73
+ ...process.env,
74
+ VITEST: "",
75
+ SOCIAL_HUB_CONFIG_DIR: configDir,
76
+ SOCIAL_HUB_API_URL: baseUrl,
77
+ SOCIAL_HUB_API_KEY: "test-key",
78
+ SOCIAL_HUB_TEAM_ID: "11111111-1111-1111-1111-111111111111",
79
+ // 关掉自更新/skill 同步,否则它们的提示会污染 stderr 断言。
80
+ SOCIAL_HUB_NO_UPDATE_CHECK: "1",
81
+ SOCIAL_HUB_NO_SKILL_CHECK: "1",
82
+ SOCIAL_HUB_NO_AUTO_UPDATE: "1",
83
+ SOCIAL_HUB_NO_SKILLS_AUTO_UPDATE: "1",
84
+ },
85
+ // 自动翻页若回归成死循环,给硬超时让它以失败告终而不是把测试挂死。
86
+ timeout: 30_000,
87
+ maxBuffer: 16 * 1024 * 1024,
88
+ }, (err, stdout, stderr) => {
89
+ const code = err && typeof err.code === "number"
90
+ ? err.code
91
+ : err
92
+ ? 1
93
+ : 0;
94
+ resolve({ code, stdout, stderr });
95
+ });
96
+ });
97
+ }
98
+ beforeAll(async () => {
99
+ // 必须跑真实的 bin(dist/index.js):要验的是**进程**的三个输出通道,
100
+ // 在 vitest 进程内 import program 是验不了 stdout/stderr 分离的。
101
+ // 与 cli-error-channels.test.ts 同理,这里显式编译一次。
102
+ try {
103
+ execFileSync(process.execPath, [join(pkgRoot, "node_modules", "typescript", "bin", "tsc")], { cwd: pkgRoot, stdio: "pipe" });
104
+ }
105
+ catch (e) {
106
+ const { stdout, stderr } = e;
107
+ throw new Error(`CLI tsc build failed:\n${stdout?.toString() ?? ""}\n${stderr?.toString() ?? ""}`);
108
+ }
109
+ expect(existsSync(cliEntry)).toBe(true);
110
+ configDir = mkdtempSync(join(tmpdir(), "social-hub-cli-partial-"));
111
+ server = createServer((req, res) => {
112
+ const url = new URL(req.url ?? "/", "http://stub.local");
113
+ const rawLimit = url.searchParams.get("limit");
114
+ const rawOffset = url.searchParams.get("offset");
115
+ seenRequests.push({
116
+ path: url.pathname,
117
+ limit: rawLimit,
118
+ offset: rawOffset,
119
+ });
120
+ if (url.pathname === "/v1/system/subreddit-blocklists") {
121
+ // 单页**硬顶 100**,即使调用方传了更大的 limit —— 这样才复现生产现场
122
+ // (total=112 / 单页 100),也顺带钉住一条正确性:翻页必须按服务端**实际返回**
123
+ // 的条数推进 offset,不能按自己请求的 limit 推进。后者在服务端给的比要的少时
124
+ // 会跳过整段数据 —— 又是一次「看起来完整的不完整结果」。
125
+ const limit = Math.min(rawLimit === null ? SERVER_DEFAULT_LIMIT : Number(rawLimit), SERVER_DEFAULT_LIMIT);
126
+ const offset = rawOffset === null ? 0 : Number(rawOffset);
127
+ let total = BLOCKLIST_TOTAL;
128
+ if (blocklistTotalDrifts) {
129
+ blocklistDriftTicks += 1;
130
+ total = BLOCKLIST_TOTAL + blocklistDriftTicks;
131
+ }
132
+ res.writeHead(200, { "content-type": "application/json" });
133
+ res.end(JSON.stringify({
134
+ items: ALL_ROWS.slice(offset, offset + limit),
135
+ total,
136
+ limit,
137
+ offset,
138
+ }));
139
+ return;
140
+ }
141
+ // 通用截断警告用。两种形态由 limit 选:
142
+ // · 不传 limit → 只给 2 条但 total=7 → **截断**,必须警告;
143
+ // · limit>=7 → 给满 7 条且 total=7 → **完整**,必须**静默**。
144
+ // 后者是阈值的边界用例:少了它,把判据从 `<` 写成 `<=` 的回归抓不住
145
+ // (per-page 的中间态永远是严格小于,碰不到相等这一点)。
146
+ if (url.pathname.endsWith("/accounts")) {
147
+ const wanted = rawLimit === null ? 2 : Number(rawLimit);
148
+ const shown = Math.min(wanted, ACCOUNTS_TOTAL);
149
+ res.writeHead(200, { "content-type": "application/json" });
150
+ res.end(JSON.stringify({
151
+ items: Array.from({ length: shown }, (_, i) => ({
152
+ id: `a-${i + 1}`,
153
+ })),
154
+ total: ACCOUNTS_TOTAL,
155
+ limit: wanted,
156
+ offset: 0,
157
+ }));
158
+ return;
159
+ }
160
+ // 计数被封顶 / 没有总数的端点(0140 起的评论样本形状):
161
+ // total 不是精确条数,分页信号只有 hasMore。
162
+ if (url.pathname === "/v1/system/subreddit-intelligence/comments") {
163
+ res.writeHead(200, { "content-type": "application/json" });
164
+ res.end(JSON.stringify({
165
+ items: [{ id: "c-1" }],
166
+ total: 1,
167
+ // total 与 items.length 相等 —— 只按数字判会认为「已经全了」,
168
+ // 但服务端明说还有。这条只有 hasMore 判据能抓住。
169
+ totalRelation: "gte",
170
+ hasMore: true,
171
+ }));
172
+ return;
173
+ }
174
+ res.writeHead(404, { "content-type": "application/json" });
175
+ res.end(JSON.stringify({ error: { message: "not stubbed" } }));
176
+ });
177
+ await new Promise((resolve) => {
178
+ server.listen(0, "127.0.0.1", resolve);
179
+ });
180
+ const addr = server.address();
181
+ if (typeof addr === "string" || addr === null)
182
+ throw new Error("no port");
183
+ baseUrl = `http://127.0.0.1:${addr.port}`;
184
+ }, 120_000);
185
+ afterAll(async () => {
186
+ await new Promise((resolve) => server.close(() => resolve()));
187
+ });
188
+ describe("blocklists-list 默认自动翻页拉全量", () => {
189
+ it("total=112 但单页上限时,仍输出 112 条,且真的发出了带 offset 的第二次请求", async () => {
190
+ seenRequests.length = 0;
191
+ blocklistTotalDrifts = false;
192
+ const { code, stdout, stderr } = await runCli([
193
+ "compliance",
194
+ "blocklists-list",
195
+ "--kind",
196
+ "bot_bouncer",
197
+ ]);
198
+ // ① stdout 通道:必须是干净可解析的 JSON,且**条数就是 total**。
199
+ // 这是这条命令存在的全部意义 —— 少一条就是一次错误的准入放行。
200
+ const parsed = JSON.parse(stdout);
201
+ expect(parsed.total).toBe(BLOCKLIST_TOTAL);
202
+ expect(parsed.items).toHaveLength(BLOCKLIST_TOTAL);
203
+ // 漏的不是随机 12 条,是**最早加入**的那批(createdAt DESC 的尾巴)。
204
+ // 所以专门钉住尾巴那几条在不在 —— 只断言长度挡不住「补了 12 条重复的」。
205
+ const subs = new Set(parsed.items.map((r) => r.subreddit));
206
+ expect(subs.size).toBe(BLOCKLIST_TOTAL);
207
+ for (const oldest of ["sub_1", "sub_2", "sub_12"]) {
208
+ expect(subs.has(oldest)).toBe(true);
209
+ }
210
+ // ② 必须**真的**翻了页 —— stub 分片给出,只有第二次带 offset 的请求
211
+ // 才可能凑齐 112 条。只断言条数会被「一次就给全」的实现蒙混过去。
212
+ const pages = seenRequests.filter((r) => r.path === "/v1/system/subreddit-blocklists");
213
+ expect(pages.length).toBeGreaterThanOrEqual(2);
214
+ expect(pages[0]?.offset).toBe("0");
215
+ expect(Number(pages[1]?.offset)).toBeGreaterThan(0);
216
+ // ③ 退出码 0,且 stderr 里**没有**截断警告 —— 拿到的就是全量,
217
+ // 翻页中间态的 per-page 警告必须被抑制,否则拉全量反而刷一屏假警告。
218
+ expect(code).toBe(0);
219
+ expect(stderr).not.toContain("mode=truncated");
220
+ }, 60_000);
221
+ it("不提供 --limit/--offset 逃生口(安全闸不留部分结果的路)", async () => {
222
+ const { code, stdout, stderr } = await runCli([
223
+ "compliance",
224
+ "blocklists-list",
225
+ "--limit",
226
+ "10",
227
+ ]);
228
+ // commander 对未知 option 报错退出:证明这个口子确实没开。
229
+ expect(code).not.toBe(0);
230
+ expect(stdout).toBe("");
231
+ expect(stderr).toContain("unknown option");
232
+ }, 60_000);
233
+ it("翻页期间 total 一直在变 → 报错,绝不静默拼一个可能不一致的结果", async () => {
234
+ seenRequests.length = 0;
235
+ blocklistTotalDrifts = true;
236
+ blocklistDriftTicks = 0;
237
+ const { code, stdout, stderr } = await runCli([
238
+ "compliance",
239
+ "blocklists-list",
240
+ ]);
241
+ blocklistTotalDrifts = false;
242
+ // stdout 零字节:宁可什么都不给,也不给一个「可能不全但看起来完整」的结果。
243
+ // 这是本次修复的核心取舍 —— 对安全闸来说「没有答案」是安全的(重跑一次即可),
244
+ // 而「一个可能不全、但看起来完整的答案」正是本次要消灭的缺陷本身。
245
+ expect(stdout).toBe("");
246
+ expect(code).toBe(1);
247
+ expect(stderr).toContain("possibly-incomplete blocklist");
248
+ // 必须**真的**重试满 3 趟才放弃,不是一遇到不一致就报错。
249
+ expect(stderr).toContain("attempt 3");
250
+ }, 60_000);
251
+ });
252
+ describe("通用收口:items.length < total 一律 stderr 警告", () => {
253
+ it("stdout 仍是干净可解析 JSON / 警告只在 stderr / 退出码不变", async () => {
254
+ seenRequests.length = 0;
255
+ // 显式给 -n 2:让服务端只给 2 条而 total 是 7 —— 这正是「拿到一页、
256
+ // 却看起来像全量」的形状。(不传 -n 的话 CLI 有自己的默认 limit,
257
+ // stub 会给满 7 条,反而验不到截断。)
258
+ const { code, stdout, stderr } = await runCli([
259
+ "accounts",
260
+ "list",
261
+ "-n",
262
+ "2",
263
+ ]);
264
+ // ① stdout:干净、可 JSON.parse —— 警告一个字节都不许漏进来。
265
+ const parsed = JSON.parse(stdout);
266
+ expect(parsed.items).toHaveLength(2);
267
+ expect(parsed.total).toBe(7);
268
+ expect(stdout).not.toContain("mode=truncated");
269
+ expect(stdout).not.toContain("[warn]");
270
+ // ② stderr:必须有警告,且把三个关键数字都说清楚
271
+ // (拿到几条 / 一共几条 / 缺几条),否则 agent 看到也不知道该干嘛。
272
+ expect(stderr).toContain("mode=truncated");
273
+ expect(stderr).toContain("items=2");
274
+ expect(stderr).toContain("total=7");
275
+ expect(stderr).toContain("缺 5 条");
276
+ // ③ 退出码不变 —— 不完整是需要被看见的**事实**,不是错误。
277
+ // 当成错误退出会打断一批把 items 当分页用的既有脚本。
278
+ expect(code).toBe(0);
279
+ }, 60_000);
280
+ it("total 被封顶(totalRelation=gte)时靠 hasMore 判,且不编造「缺 N 条」", async () => {
281
+ seenRequests.length = 0;
282
+ // 服务端给的是 items=1 / total=1 / totalRelation=gte / hasMore=true。
283
+ // 只按 `items.length < total` 判会认为「已经全了」而**静默放过** ——
284
+ // 0140 起评论样本正是这个形状,漏的是一整类端点。
285
+ const { code, stdout, stderr } = await runCli([
286
+ "intelligence",
287
+ "comments",
288
+ "--subreddit",
289
+ "test",
290
+ ]);
291
+ expect(code).toBe(0);
292
+ expect(JSON.parse(stdout).items).toHaveLength(1);
293
+ // 必须报,且必须点明 total 只是下界。
294
+ expect(stderr).toContain("mode=truncated");
295
+ expect(stderr).toContain("gte");
296
+ // 🔴 绝不能算出「缺 0 条」这种既错又误导的数字 —— total 是下界,缺口算不出来。
297
+ expect(stderr).not.toContain("缺 0 条");
298
+ }, 60_000);
299
+ it("警告文案不写死 --limit/--offset(blocklists 刻意没有这两个参数)", async () => {
300
+ seenRequests.length = 0;
301
+ const { stderr } = await runCli(["accounts", "list", "-n", "2"]);
302
+ expect(stderr).toContain("mode=truncated");
303
+ // 通用文案若写死「用 --limit/--offset 翻页」,照着做的 agent 在
304
+ // blocklists-list 上会撞 "unknown option" —— 提示必须是条件式的。
305
+ expect(stderr).not.toContain("用 --limit/--offset 翻页");
306
+ }, 60_000);
307
+ it("items.length === total 时不打警告(否则每条列表命令都刷噪音)", async () => {
308
+ seenRequests.length = 0;
309
+ // ⚠️ 这一条必须打到**恰好相等**这个点上。翻页的 per-page 中间态永远是
310
+ // 严格小于,碰不到相等 —— 用它来验「相等不警告」是验不出来的
311
+ // (实测:把判据从 `>=` 改成 `>` 的变异能存活)。所以这里显式让服务端
312
+ // 一次给满 7 条、total 也是 7。
313
+ const { code, stdout, stderr } = await runCli([
314
+ "accounts",
315
+ "list",
316
+ "-n",
317
+ String(ACCOUNTS_TOTAL),
318
+ ]);
319
+ const parsed = JSON.parse(stdout);
320
+ expect(parsed.items).toHaveLength(ACCOUNTS_TOTAL);
321
+ expect(parsed.total).toBe(ACCOUNTS_TOTAL);
322
+ expect(code).toBe(0);
323
+ expect(stderr).not.toContain("mode=truncated");
324
+ expect(stderr).not.toContain("[warn]");
325
+ }, 60_000);
326
+ });
327
+ //# sourceMappingURL=partial-list-guard.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partial-list-guard.test.js","sourceRoot":"","sources":["../src/partial-list-guard.test.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
1
  export declare const PACKAGE_NAME = "@geoly-ai/social-hub-cli";
2
+ /** 全局 bin 名(package.json 的 `bin` 键)。自更新后要校验它还在。 */
3
+ export declare const BIN_NAME = "social-hub";
2
4
  type UpdateCache = {
3
5
  checkedAt: string;
4
6
  latest: string;
@@ -47,7 +49,27 @@ export declare function shouldSkipAutoUpdate(): boolean;
47
49
  export declare function acquireAutoUpdateLock(lockFile?: string): boolean;
48
50
  export declare function releaseAutoUpdateLock(lockFile?: string): void;
49
51
  /**
50
- * 后台(detached、静默、非阻塞)启动 `npm install -g @latest`。当前进程立即返回。
52
+ * 后台自更新要跑的 shell 脚本。
53
+ *
54
+ * 🔴 **为什么不能只 `npm install -g`**:npm 的全局安装**不是原子的** —— 先把包解到
55
+ * `lib/node_modules/`,**再**建 `bin/` 软链。后台进程在这两步之间被杀(cron/agent
56
+ * harness 按 cgroup 或 session 整体杀时,`detached` 挡不住),就落在「包已是新版本、
57
+ * 软链已经没了」这个状态:那台机器此后所有 `social-hub` 命令都是 command not found。
58
+ *
59
+ * ⚠️ **而且这个状态没法由 CLI 自己在启动时救** —— 软链**就是**入口,它没了 CLI 根本
60
+ * 跑不起来,任何「启动时自检」的代码都不会被执行到。所以修复只能发生在**更新器这一侧**:
61
+ * 装完立刻验一次软链,缺了就地补装一次。
62
+ *
63
+ * 校验用 `npm prefix -g` 取全局前缀而不是猜路径(nvm/volta/homebrew 各不相同)。
64
+ * 补装仍可能再次被杀 —— 这一步收窄的是窗口,不是消灭它;真正杀不死的兜底得在
65
+ * 调用方(部署脚本里的 `npx` 回退),见 docs/。
66
+ *
67
+ * 只在校验通过后才删锁:装失败/被杀时锁留着,靠 TTL 过期后下次调用重试,
68
+ * 不会因为「删了锁」把失败当成功。
69
+ */
70
+ export declare function buildBackgroundUpdateScript(latest: string, lockFile: string, binName?: string): string;
71
+ /**
72
+ * 后台(detached、静默、非阻塞)启动自更新。当前进程立即返回。
51
73
  *
52
74
  * ⚠️ 这一步**只换二进制,一行不碰 skills**。skills 的同步发生在**下一条**命令:
53
75
  * 那时加载的是新二进制,`resolveBundledSource()` 拿到新版本号,skill-check 缓存
@@ -55,8 +77,13 @@ export declare function releaseAutoUpdateLock(lockFile?: string): void;
55
77
  * 钩子也补不上这一拍(`src/postinstall.ts` 只打印文案)。所以准确说法是
56
78
  * 「升级后**下一条**命令自动同步 skills」,要立刻同步得跑 `social-hub skills sync`
57
79
  * (或 `social-hub self-update`,它把两件事串起来做完)。
58
- * 成功启动返回 true;锁在途/启动失败返回 false。锁在成功装完后不主动释放(detached 无法感知
59
- * 完成),靠 TTL 过期——届时 current 已=latest 不再触发;若装失败,锁到期后下次调用重试。
80
+ *
81
+ * ⚠️ 而且「下一条命令」只同步**当次 cwd 发现到的**托管目录(见 skill-update-check
82
+ * 的 `discoverManagedTargets(process.cwd(), ...)`)—— 一台机器多个托管目录时,
83
+ * 没被当作 cwd 的那些永远不会被同步。这是已知缺口,单独排期。
84
+ *
85
+ * 成功启动返回 true;锁在途/启动失败返回 false。锁由脚本在**校验软链通过后**删除;
86
+ * 装失败或中途被杀则锁留着,靠 TTL 过期后下次调用重试。
60
87
  */
61
88
  export declare function spawnBackgroundUpdate(latest: string, lockFile?: string): boolean;
62
89
  /** 后台更新已启动时给人/agent 的一行提示(stderr;不污染 stdout/JSON)。 */
@@ -1 +1 @@
1
- {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY,6BAA6B,CAAC;AAavD,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAkB7D;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAYjE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAavE;AAuBD,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAIxD;AAED,+DAA+D;AAC/D,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE/D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,GAC5B,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAgBvD;AAoBD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED,uEAAuE;AACvE,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,EAAE,CAUV;AAaD,wBAAgB,oBAAoB,IAAI,OAAO,CAI9C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,GAAE,MAA8B,GACvC,OAAO,CA4BT;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,GAAE,MAA8B,GACvC,IAAI,CAMN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAA8B,GACvC,OAAO,CAcT;AAED,sDAAsD;AACtD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,EAAE,CASV;AAoBD,wBAAsB,iBAAiB,CACrC,IAAI,GAAE,MAAM,EAAiB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Bf"}
1
+ {"version":3,"file":"update-check.d.ts","sourceRoot":"","sources":["../src/update-check.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY,6BAA6B,CAAC;AACvD,mDAAmD;AACnD,eAAO,MAAM,QAAQ,eAAe,CAAC;AAcrC,KAAK,WAAW,GAAG;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAkB7D;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAYjE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAavE;AAuBD,wBAAgB,YAAY,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAIxD;AAED,+DAA+D;AAC/D,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAE/D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,UAAU,EAAE,OAAO,CAAA;CAAE,GAC5B,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CA0BvD;AAoBD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAI3D;AAED,uEAAuE;AACvE,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,EAAE,CAUV;AAaD,wBAAgB,oBAAoB,IAAI,OAAO,CAI9C;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,QAAQ,GAAE,MAA8B,GACvC,OAAO,CA4BT;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,GAAE,MAA8B,GACvC,IAAI,CAMN;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,2BAA2B,CACzC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE,MAAiB,GACzB,MAAM,CAYR;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAA8B,GACvC,OAAO,CAsBT;AAED,sDAAsD;AACtD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,MAAM,EAAE,CASV;AAoBD,wBAAsB,iBAAiB,CACrC,IAAI,GAAE,MAAM,EAAiB,GAC5B,OAAO,CAAC,IAAI,CAAC,CA2Bf"}
@@ -5,6 +5,8 @@ import { join } from "node:path";
5
5
  import { isRunningViaNpx } from "./skills/paths.js";
6
6
  import { getCliVersion } from "./version-info.js";
7
7
  export const PACKAGE_NAME = "@geoly-ai/social-hub-cli";
8
+ /** 全局 bin 名(package.json 的 `bin` 键)。自更新后要校验它还在。 */
9
+ export const BIN_NAME = "social-hub";
8
10
  const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME.replace("/", "%2F")}/latest`;
9
11
  // Notice cache TTL: fresh cache is reused (no npm re-fetch); once older than
10
12
  // this the next non-version/help command re-queries the registry. Kept short
@@ -116,6 +118,16 @@ export function buildSelfUpdateSteps(latest, opts) {
116
118
  args: ["install", "-g", `${PACKAGE_NAME}@${latest}`],
117
119
  label: `npm install -g ${PACKAGE_NAME}@${latest}`,
118
120
  },
121
+ // 同 buildBackgroundUpdateScript 的理由:npm 全局安装非原子,包换了而 bin 软链
122
+ // 没建上时,机器此后一条 social-hub 都跑不了。前台路径也验一次,缺了就地补装。
123
+ {
124
+ cmd: "sh",
125
+ args: [
126
+ "-c",
127
+ `[ -x "$(npm prefix -g)/bin/${BIN_NAME}" ] || npm install -g '${PACKAGE_NAME}@${latest}'`,
128
+ ],
129
+ label: `verify ${BIN_NAME} bin link (reinstall if missing)`,
130
+ },
119
131
  ];
120
132
  if (!opts.skipSkills) {
121
133
  steps.push({
@@ -220,7 +232,39 @@ export function releaseAutoUpdateLock(lockFile = AUTO_UPDATE_LOCK_FILE) {
220
232
  }
221
233
  }
222
234
  /**
223
- * 后台(detached、静默、非阻塞)启动 `npm install -g @latest`。当前进程立即返回。
235
+ * 后台自更新要跑的 shell 脚本。
236
+ *
237
+ * 🔴 **为什么不能只 `npm install -g`**:npm 的全局安装**不是原子的** —— 先把包解到
238
+ * `lib/node_modules/`,**再**建 `bin/` 软链。后台进程在这两步之间被杀(cron/agent
239
+ * harness 按 cgroup 或 session 整体杀时,`detached` 挡不住),就落在「包已是新版本、
240
+ * 软链已经没了」这个状态:那台机器此后所有 `social-hub` 命令都是 command not found。
241
+ *
242
+ * ⚠️ **而且这个状态没法由 CLI 自己在启动时救** —— 软链**就是**入口,它没了 CLI 根本
243
+ * 跑不起来,任何「启动时自检」的代码都不会被执行到。所以修复只能发生在**更新器这一侧**:
244
+ * 装完立刻验一次软链,缺了就地补装一次。
245
+ *
246
+ * 校验用 `npm prefix -g` 取全局前缀而不是猜路径(nvm/volta/homebrew 各不相同)。
247
+ * 补装仍可能再次被杀 —— 这一步收窄的是窗口,不是消灭它;真正杀不死的兜底得在
248
+ * 调用方(部署脚本里的 `npx` 回退),见 docs/。
249
+ *
250
+ * 只在校验通过后才删锁:装失败/被杀时锁留着,靠 TTL 过期后下次调用重试,
251
+ * 不会因为「删了锁」把失败当成功。
252
+ */
253
+ export function buildBackgroundUpdateScript(latest, lockFile, binName = BIN_NAME) {
254
+ const spec = `${PACKAGE_NAME}@${latest}`;
255
+ // 单引号包裹 + 转义内嵌单引号:lockFile 来自 homedir(),路径含空格/引号也不会被拆词。
256
+ const quote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
257
+ return [
258
+ `npm install -g ${quote(spec)} || exit 1`,
259
+ `PREFIX="$(npm prefix -g)" || exit 1`,
260
+ // 软链没了就地补一次;补完再验一遍,仍然没有就保留锁退出(失败,交给 TTL 重试)。
261
+ `[ -x "$PREFIX/bin/"${quote(binName)} ] || npm install -g ${quote(spec)} || exit 1`,
262
+ `[ -x "$PREFIX/bin/"${quote(binName)} ] || exit 1`,
263
+ `rm -f ${quote(lockFile)}`,
264
+ ].join("\n");
265
+ }
266
+ /**
267
+ * 后台(detached、静默、非阻塞)启动自更新。当前进程立即返回。
224
268
  *
225
269
  * ⚠️ 这一步**只换二进制,一行不碰 skills**。skills 的同步发生在**下一条**命令:
226
270
  * 那时加载的是新二进制,`resolveBundledSource()` 拿到新版本号,skill-check 缓存
@@ -228,17 +272,29 @@ export function releaseAutoUpdateLock(lockFile = AUTO_UPDATE_LOCK_FILE) {
228
272
  * 钩子也补不上这一拍(`src/postinstall.ts` 只打印文案)。所以准确说法是
229
273
  * 「升级后**下一条**命令自动同步 skills」,要立刻同步得跑 `social-hub skills sync`
230
274
  * (或 `social-hub self-update`,它把两件事串起来做完)。
231
- * 成功启动返回 true;锁在途/启动失败返回 false。锁在成功装完后不主动释放(detached 无法感知
232
- * 完成),靠 TTL 过期——届时 current 已=latest 不再触发;若装失败,锁到期后下次调用重试。
275
+ *
276
+ * ⚠️ 而且「下一条命令」只同步**当次 cwd 发现到的**托管目录(见 skill-update-check
277
+ * 的 `discoverManagedTargets(process.cwd(), ...)`)—— 一台机器多个托管目录时,
278
+ * 没被当作 cwd 的那些永远不会被同步。这是已知缺口,单独排期。
279
+ *
280
+ * 成功启动返回 true;锁在途/启动失败返回 false。锁由脚本在**校验软链通过后**删除;
281
+ * 装失败或中途被杀则锁留着,靠 TTL 过期后下次调用重试。
233
282
  */
234
283
  export function spawnBackgroundUpdate(latest, lockFile = AUTO_UPDATE_LOCK_FILE) {
235
284
  if (!acquireAutoUpdateLock(lockFile))
236
285
  return false;
237
286
  try {
238
- const child = spawn("npm", ["install", "-g", `${PACKAGE_NAME}@${latest}`], {
239
- detached: true,
240
- stdio: "ignore",
241
- });
287
+ // Windows 没有 POSIX `sh`,校验脚本跑不了 —— 退回原来的裸 npm 调用(拿不到软链
288
+ // 自愈,但至少不会因为 spawn 失败而整个不更新)。锁沿用旧语义:靠 TTL 过期。
289
+ const child = process.platform === "win32"
290
+ ? spawn("npm", ["install", "-g", `${PACKAGE_NAME}@${latest}`], {
291
+ detached: true,
292
+ stdio: "ignore",
293
+ })
294
+ : spawn("sh", ["-c", buildBackgroundUpdateScript(latest, lockFile)], {
295
+ detached: true,
296
+ stdio: "ignore",
297
+ });
242
298
  child.on("error", () => releaseAutoUpdateLock(lockFile));
243
299
  child.unref();
244
300
  return true;