@geoly-ai/social-hub-cli 0.3.41 → 0.3.42
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/cmd-manifest.json +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -1
- package/dist/watchlists-list-filters.test.d.ts +2 -0
- package/dist/watchlists-list-filters.test.d.ts.map +1 -0
- package/dist/watchlists-list-filters.test.js +143 -0
- package/dist/watchlists-list-filters.test.js.map +1 -0
- package/package.json +2 -2
- package/skills/integrity.json +2 -2
- package/skills/manifest.json +1 -1
- package/skills/social-hub-intelligence/SKILL.md +40 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchlists-list-filters.test.d.ts","sourceRoot":"","sources":["../src/watchlists-list-filters.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 五层同步补口:`intel watchlists-list` 的服务端筛选参数。
|
|
3
|
+
*
|
|
4
|
+
* 服务端(contracts/db/api)与 SDK 在 cf63e6b4 已经支持
|
|
5
|
+
* subredditQuery / tag / monitorState / sortBy / sortDir,并顺带补上此前漏传的
|
|
6
|
+
* status / brandId / industryKeys —— 缺的只有 CLI 这一层。
|
|
7
|
+
*
|
|
8
|
+
* 🔴 本文件跑的是**真实 handler**,不是「断言选项已注册」。
|
|
9
|
+
* 只断言注册挡不住本任务最可能的静默失败形状:**解析了但没往下传**
|
|
10
|
+
* (`.option()` 写了、`listSubredditWatchlists({...})` 里忘了那一行)——
|
|
11
|
+
* 那种情况下 `--help` 看着完全正常、命令也不报错,只是筛选悄悄没生效,
|
|
12
|
+
* 返回的是**未筛选的全量**,读起来完全可信。
|
|
13
|
+
*
|
|
14
|
+
* 🔴 monitorState 是**三值**不是布尔。软停语义:
|
|
15
|
+
* · disabled = 加过监听后停了(配置行还在)
|
|
16
|
+
* · never = 压根没加过
|
|
17
|
+
* 用户提这个需求要的正是这个区别,所以这里除了「三个值都能透传」之外,
|
|
18
|
+
* 还钉死「布尔值被拒且错误信息讲清三值」——防的是后人把它简化成 `--monitored`。
|
|
19
|
+
*/
|
|
20
|
+
import { beforeAll, afterEach, describe, expect, it, vi } from "vitest";
|
|
21
|
+
const listSubredditWatchlists = vi
|
|
22
|
+
.fn()
|
|
23
|
+
.mockResolvedValue({ items: [], total: 0 });
|
|
24
|
+
vi.mock("@geoly-ai/social-hub-sdk", () => ({
|
|
25
|
+
SocialHubClient: vi.fn(),
|
|
26
|
+
getSocialHubHealth: vi.fn(),
|
|
27
|
+
}));
|
|
28
|
+
vi.mock("./client.js", () => ({
|
|
29
|
+
requireClient: () => ({ listSubredditWatchlists }),
|
|
30
|
+
resolveTeamId: (t) => t ?? "team-1",
|
|
31
|
+
getBaseUrl: () => "http://localhost",
|
|
32
|
+
}));
|
|
33
|
+
let program;
|
|
34
|
+
let cmd;
|
|
35
|
+
/**
|
|
36
|
+
* ⚠️ commander 把上一次 parse 的 option 值**留在同一个 command 实例上**,
|
|
37
|
+
* 而真实 CLI 每次都是新进程。不清的话「不传 --subreddit-query」会读到上一个
|
|
38
|
+
* 用例残留的值 —— 那既不是真实行为,也会让用例顺序变成隐性依赖
|
|
39
|
+
* (`accounts-list-offset.test.ts` 是靠「把两次 parse 塞进同一个用例、不传的放前面」
|
|
40
|
+
* 绕开的;这里参数多,绕不动,直接清)。
|
|
41
|
+
*/
|
|
42
|
+
const resetOpts = () => {
|
|
43
|
+
for (const opt of cmd.options)
|
|
44
|
+
cmd.setOptionValue(opt.attributeName(), opt.defaultValue);
|
|
45
|
+
};
|
|
46
|
+
const run = (...args) => {
|
|
47
|
+
resetOpts();
|
|
48
|
+
return program.parseAsync([
|
|
49
|
+
"node",
|
|
50
|
+
"social-hub",
|
|
51
|
+
"intelligence",
|
|
52
|
+
"watchlists-list",
|
|
53
|
+
...args,
|
|
54
|
+
]);
|
|
55
|
+
};
|
|
56
|
+
/** 最后一次调用收到的参数对象。 */
|
|
57
|
+
const lastArgs = () => listSubredditWatchlists.mock.calls.at(-1)?.[0];
|
|
58
|
+
beforeAll(async () => {
|
|
59
|
+
program = (await import("./index.js")).program;
|
|
60
|
+
const intel = program.commands.find((c) => c.name() === "intelligence");
|
|
61
|
+
const found = intel?.commands.find((c) => c.name() === "watchlists-list");
|
|
62
|
+
// 正向对照:命令真的存在。找不到时下面 resetOpts 会静默变成空循环,
|
|
63
|
+
// 于是整份用例退化成「跑了个不存在的命令」而依然可能全绿。
|
|
64
|
+
if (!found)
|
|
65
|
+
throw new Error("intelligence watchlists-list 命令没找到");
|
|
66
|
+
cmd = found;
|
|
67
|
+
vi.spyOn(console, "log").mockImplementation(() => { });
|
|
68
|
+
}, 30000);
|
|
69
|
+
afterEach(() => {
|
|
70
|
+
listSubredditWatchlists.mockClear();
|
|
71
|
+
});
|
|
72
|
+
describe("intelligence watchlists-list 服务端筛选参数", () => {
|
|
73
|
+
it("🔴 每个筛选参数都真的传到了 SDK;不传时不发送(保持服务端默认)", async () => {
|
|
74
|
+
await run();
|
|
75
|
+
// 正向对照:handler 确实跑到了、确实带着默认分页调了 SDK。
|
|
76
|
+
// 没有这一条的话,下面一串 toBeUndefined() 在「命令压根没执行」时也全绿。
|
|
77
|
+
expect(listSubredditWatchlists).toHaveBeenCalledTimes(1);
|
|
78
|
+
expect(lastArgs()).toMatchObject({ limit: 100, offset: 0 });
|
|
79
|
+
for (const key of [
|
|
80
|
+
"subredditQuery",
|
|
81
|
+
"status",
|
|
82
|
+
"brandId",
|
|
83
|
+
"industryKeys",
|
|
84
|
+
"tag",
|
|
85
|
+
"monitorState",
|
|
86
|
+
"sortBy",
|
|
87
|
+
"sortDir",
|
|
88
|
+
])
|
|
89
|
+
expect(lastArgs()[key], `${key} 不传时不应发送`).toBeUndefined();
|
|
90
|
+
await run("--subreddit-query", "garden", "--status", "active", "--brand", "11111111-1111-4111-8111-111111111111", "--industry-keys", "home,garden", "--tag", "seed", "--monitor-state", "disabled", "--sort-by", "monitorLastObservedAt", "--sort-dir", "asc", "--limit", "25", "--offset", "50");
|
|
91
|
+
expect(listSubredditWatchlists).toHaveBeenCalledTimes(2);
|
|
92
|
+
expect(lastArgs()).toMatchObject({
|
|
93
|
+
subredditQuery: "garden",
|
|
94
|
+
status: "active",
|
|
95
|
+
brandId: "11111111-1111-4111-8111-111111111111",
|
|
96
|
+
industryKeys: "home,garden",
|
|
97
|
+
tag: "seed",
|
|
98
|
+
monitorState: "disabled",
|
|
99
|
+
sortBy: "monitorLastObservedAt",
|
|
100
|
+
sortDir: "asc",
|
|
101
|
+
limit: 25,
|
|
102
|
+
offset: 50,
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
it("--subreddit(精确)与 --subreddit-query(子串)是两个独立谓词,不许互相顶替", async () => {
|
|
106
|
+
await run("--subreddit", "r/gardening");
|
|
107
|
+
expect(lastArgs()).toMatchObject({ subreddit: "r/gardening" });
|
|
108
|
+
// 变异形状:把 subredditQuery 接到 opts.subreddit 上(或反过来)。
|
|
109
|
+
expect(lastArgs().subredditQuery).toBeUndefined();
|
|
110
|
+
await run("--subreddit-query", "garden");
|
|
111
|
+
expect(lastArgs()).toMatchObject({ subredditQuery: "garden" });
|
|
112
|
+
expect(lastArgs().subreddit).toBeUndefined();
|
|
113
|
+
});
|
|
114
|
+
describe("🔴 monitorState 三值(⛔ 不是布尔)", () => {
|
|
115
|
+
it.each(["enabled", "disabled", "never"])("%s 原样透传为字符串", async (state) => {
|
|
116
|
+
await run("--monitor-state", state);
|
|
117
|
+
expect(lastArgs().monitorState).toBe(state);
|
|
118
|
+
// 变异形状:`monitorState: opts.monitorState === "enabled"` 这类布尔退化。
|
|
119
|
+
// 只写 toBe(state) 也能抓到,这条是把「类型」这层意图写死,防后人改成布尔。
|
|
120
|
+
expect(typeof lastArgs().monitorState).toBe("string");
|
|
121
|
+
});
|
|
122
|
+
it.each(["true", "false", "1", "monitored", ""])("⛔ 布尔式/拼错的值 %j 直接报错,不静默落到某一档", async (bad) => {
|
|
123
|
+
await expect(run("--monitor-state", bad)).rejects.toThrow(/--monitor-state/);
|
|
124
|
+
// 正向对照:报错是「拒绝了」,不是「转发了一个坏值」。
|
|
125
|
+
expect(listSubredditWatchlists).not.toHaveBeenCalled();
|
|
126
|
+
});
|
|
127
|
+
it("错误信息必须讲清 disabled 与 never 的区别(不然读者仍会以为是布尔)", async () => {
|
|
128
|
+
// parseAsync 成功时 resolve 的是 Command,所以这里显式把两种结局收成 unknown,
|
|
129
|
+
// 再由下面的 toBeInstanceOf 判定 —— ⛔ 不要直接 `as Error`,那会让
|
|
130
|
+
// 「命令没报错」这种情况静默走到 err.message 上(undefined.contains 反而报别的错)。
|
|
131
|
+
const err = await run("--monitor-state", "true").then((ok) => ok, (e) => e);
|
|
132
|
+
// 正向对照:确实拿到了一个 Error(而不是命令压根没报错、下面对 undefined 断言)。
|
|
133
|
+
expect(err).toBeInstanceOf(Error);
|
|
134
|
+
const message = err.message;
|
|
135
|
+
for (const token of ["enabled", "disabled", "never"])
|
|
136
|
+
expect(message).toContain(token);
|
|
137
|
+
// 光列三个词还不够 —— 必须说清「停了的」和「从没加过」不是一回事。
|
|
138
|
+
expect(message).toMatch(/软停|配置行/);
|
|
139
|
+
expect(message).toMatch(/没加过|新建配置行/);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
//# sourceMappingURL=watchlists-list-filters.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watchlists-list-filters.test.js","sourceRoot":"","sources":["../src/watchlists-list-filters.test.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geoly-ai/social-hub-cli",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.42",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "social-hub CLI for Social Ops Hub",
|
|
6
6
|
"repository": {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"commander": "^12.1.0",
|
|
25
25
|
"@geoly-ai/social-hub-authz": "0.0.20",
|
|
26
|
-
"@geoly-ai/social-hub-sdk": "0.1.
|
|
26
|
+
"@geoly-ai/social-hub-sdk": "0.1.15"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^22.10.2",
|
package/skills/integrity.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"fileCount": 1656,
|
|
5
5
|
"files": {
|
|
6
6
|
"README.md": "5246d4ae2712e3e656b101041929183d7ae7f8ea487dacf5854187826ab7c335",
|
|
7
|
-
"manifest.json": "
|
|
7
|
+
"manifest.json": "511a9d10759fb3093bd091a04cf613743967548b9b12d623cae22235571fd7e9",
|
|
8
8
|
"reddit-answers-research/SKILL.md": "ab6fd12eeb2fef4dc31a6f3c21a84810d6b5bdb309f130bcdc9eb902f075294a",
|
|
9
9
|
"reddit-answers-research/references/browser-playbook.md": "a8f61d3d43ca93d46dadc8a092a515ddcd32df12fb1d08d334cce2e02709d915",
|
|
10
10
|
"reddit-answers-research/references/report-template.md": "9aa9acf555ba8b2ca503bdbdf5750a5f53c66e19989bf6807fa6200c7317e306",
|
|
@@ -1643,7 +1643,7 @@
|
|
|
1643
1643
|
"social-hub-content-review/SKILL.md": "6d10a84ab56552fd953863b97978bddc0e925e3a2667d64a7a4c51e33a3ddb58",
|
|
1644
1644
|
"social-hub-events-observability/SKILL.md": "f4c17487c64bb1f48381d8d60e97cfa160a94a0f120153b1c0e798a0c7ee7ebe",
|
|
1645
1645
|
"social-hub-graph-compliance/SKILL.md": "2270aeb321733f0318f75da4838ca1ca03996c6cac59e5a12f7152bd1ecdfa26",
|
|
1646
|
-
"social-hub-intelligence/SKILL.md": "
|
|
1646
|
+
"social-hub-intelligence/SKILL.md": "632e6ad7fe92cf82c0b5e9e01c2ea5d6aff94871384c5e39d8617fcd0663e877",
|
|
1647
1647
|
"social-hub-migration/SKILL.md": "1c13f231bb81ae8d242c834eec93a499830481804c66f6dba95922972f56f9ce",
|
|
1648
1648
|
"social-hub-notifications/SKILL.md": "f80f4cf2f9ef0dbf60bd6e8bb4ad7e27dc0ff1fe59f4c7e79e49cc691ed4be6a",
|
|
1649
1649
|
"social-hub-openclaw-context/SKILL.md": "97c48f38f1f1a206c3af2af49fcd445a1cd5b78d4a9c805c2456de4ce98fec09",
|
package/skills/manifest.json
CHANGED
|
@@ -197,7 +197,12 @@ installed_apps 7 天。
|
|
|
197
197
|
## Watchlists
|
|
198
198
|
|
|
199
199
|
```bash
|
|
200
|
-
social-hub intelligence watchlists-list [--campaign <uuid>]
|
|
200
|
+
social-hub intelligence watchlists-list [--campaign <uuid>] [--subreddit <name>] \
|
|
201
|
+
[--subreddit-query <text>] [--status active|paused|archived] [--brand <uuid>] \
|
|
202
|
+
[--industry-keys <k1,k2>] [--tag <tag>] \
|
|
203
|
+
[--monitor-state enabled|disabled|never] \
|
|
204
|
+
[--sort-by createdAt|subreddit|monitorLastObservedAt] [--sort-dir asc|desc] \
|
|
205
|
+
[-n 100] [--offset 0]
|
|
201
206
|
social-hub intelligence watchlists-create -j '{"subreddit":"technology","tags":["core"]}'
|
|
202
207
|
# 品牌洞察(VOC/SOV)用的监控池:显式绑定品牌 + 指定主行业
|
|
203
208
|
social-hub intelligence watchlists-create -j '{"subreddit":"jewelry","primaryIndustryKey":"industry_pools__jewelry","brandIds":["<brand-uuid>"]}'
|
|
@@ -212,6 +217,40 @@ brand 池 > industry 池 > 其他,稳定排序;此前首匹配会把养号宠物
|
|
|
212
217
|
品牌 VOC/SOV 扫描取"全局 + 绑定当前品牌"的池。做品牌洞察(如 Aporro)时创建
|
|
213
218
|
watchlist 须显式传 `brandIds`。
|
|
214
219
|
|
|
220
|
+
**筛选一律在服务端**(分页是 `limit/offset` + `total`)。⛔ 不要"拉一页回来自己过滤":
|
|
221
|
+
那只过滤到**当前页**,筛完看到 3 条会以为总共就 3 条。板块多时用 `--offset` 翻页。
|
|
222
|
+
|
|
223
|
+
**`--subreddit` 与 `--subreddit-query` 是两个谓词,别混**:前者**精确**匹配(板块详情
|
|
224
|
+
聚合视图靠它拿到确定的那一行),后者是**子串**搜索(给"我想找个板块加监听"用)。
|
|
225
|
+
两者都大小写不敏感、`r/` 前缀可省。`--tag` 是**精确包含**单个标签(不是子串)。
|
|
226
|
+
`--industry-keys` 逗号分隔可多个。`--brand` 只返回**显式绑定**该品牌的池,**不含全局池**
|
|
227
|
+
——想要"这个品牌实际会扫到的池",还得再查一次不带 `--brand` 的全局池并合并。
|
|
228
|
+
|
|
229
|
+
### 🔴 `--monitor-state` 是**三值**,不是布尔
|
|
230
|
+
|
|
231
|
+
停止监听是**软停**(`enabled=false`,配置行和观察 ledger 都还在),所以"没在监听"
|
|
232
|
+
在业务上是**两种截然不同**的状态,`--monitor-state` 必须分开表达:
|
|
233
|
+
|
|
234
|
+
| 值 | 含义 | 要做什么才能恢复监听 |
|
|
235
|
+
| ---------- | ---------------------- | ----------------------------------------------------------------- |
|
|
236
|
+
| `enabled` | 正在监听 | — |
|
|
237
|
+
| `disabled` | **加过监听、后来停了** | `monitors-enable` 直接重启;历史水位还在,不会重复报旧帖 |
|
|
238
|
+
| `never` | **压根没加过监听** | `monitors-enable` 会**新建**配置行;该板块历史帖会被当成新帖报一遍 |
|
|
239
|
+
|
|
240
|
+
⛔ 不要把它当成 `--monitored true/false` 用——CLI 会直接报错。想要"所有没在监听的",
|
|
241
|
+
得**分两次查**(`disabled` 一次、`never` 一次),因为对这两批要采取的动作不一样:
|
|
242
|
+
`disabled` 是"我停过它,要不要恢复";`never` 是"这个板块我还没碰过"。
|
|
243
|
+
⚠️ 服务端也没有"合并版"的布尔筛选可用,别去猜一个 `--monitored`。
|
|
244
|
+
|
|
245
|
+
**权限**:`--monitor-state` 与 `--sort-by monitorLastObservedAt` 属**系统级运维配置**面,
|
|
246
|
+
受限主体(client)传任一个都会 **403**;同一道门也把 `monitorLastObservedAt` 字段从
|
|
247
|
+
受限主体的响应里摘掉了。排序也算旁路——按它排序等于把"在监听的"全顶到前面。
|
|
248
|
+
|
|
249
|
+
**排序**:`--sort-by createdAt|subreddit|monitorLastObservedAt`(缺省 `createdAt`)。
|
|
250
|
+
⚠️ 不传 `--sort-dir` 时方向**随排序键而变**:`subreddit` 缺省 `asc`(字母序),
|
|
251
|
+
其余缺省 `desc`(最新在前)。要稳定就显式传。
|
|
252
|
+
⚠️ 分页仍是 offset,排序键的值在翻页期间变化时理论上会跨页漂移(当前规模几十行,可接受)。
|
|
253
|
+
|
|
215
254
|
## 板块新帖监听(定时抓 `/new` → 飞书卡片 @ 负责人)
|
|
216
255
|
|
|
217
256
|
系统级(与 watchlist 同源,**无 `-t/--team`**)。cron 每 12h 一轮:抓已启用板块的
|