@geoly-ai/social-hub-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/CHANGELOG.md CHANGED
@@ -4,6 +4,43 @@ 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.2.4] - 2026-07-16
8
+
9
+ ### Added
10
+
11
+ - 帖子审核五层交付——SDK postReview + CLI content post-review + skill
12
+ - 需求A P1 收尾——behavioralHealth advisory feature flag(默认关)
13
+ - 需求A P1——behavioralHealth 客户端方法 + query 子命令
14
+ - 互动时序 P2——批量调度上下文 query + correlationKeys + §13 收口
15
+ - 互动时序去同质化 Hub P1——canonical 事件/时区溯源/活动聚合
16
+
17
+ ## [0.2.3] - 2026-07-14
18
+
19
+ content review 命令(评论 LLM 复审+优化,层③);reddit list 排序参数;brands system-update;seqNo 字段
20
+
21
+ ### Added
22
+
23
+ - 评论 LLM 复审+优化服务——发布前三层 gate 的层③(独立模型对抗式复审)
24
+ - Reddit 指标快照剔除内部自建品牌 + brands.is_internal 产品化
25
+ - 帖子表现新增数字序列号 seqNo——可编辑/列表展示/可排序
26
+ - 草稿一稿一投——entry 成功自动 published,禁止再排期(含并发安全与历史回填)
27
+ - publishing plan 状态改为 entries 聚合派生 + status 过滤真正生效
28
+ - CLI/API accounts reassign 命令(账号跨团队迁移产品化)
29
+ - 草稿变更追踪 + 乐观并发(updatedAt/version,防丢更新)
30
+ - 账号注册时间 registeredAt(可编辑)+ 派生只读 age
31
+ - 通用抓取加 scrollCount(无限滚动页懒加载)
32
+
33
+ ### Fixed
34
+
35
+ - 收官清扫——cron addBulk/seed 契约/快照幂等/intel partial 终态对齐
36
+ - sanction 解封语义收紧——枚举化+全写入口归一,web"解除"按钮从未真正解封
37
+ - 终态一致性——complete/fail/skip 幂等 + fail/skip 事务化 + cron 任何终态都续期
38
+ - Firecrawl 兜底改用低产量重试(自托管不支持 actions),去掉 scrape scrollCount
39
+
40
+ ### Documentation
41
+
42
+ - 说明 REVIEW_LLM_BASE_URL 三 provider 通用+allowlist 要求
43
+
7
44
  ## [0.2.2] - 2026-07-03
8
45
 
9
46
  skills install 未指定 agent 时默认自动探测机器上已有的 agent 目录安装(尊重 --global/--project;TUI 空选同样回退);不再报错。
@@ -1,6 +1,6 @@
1
1
  {
2
- "generatedAt": "2026-07-03T08:48:48.566Z",
3
- "cliVersion": "0.2.2",
2
+ "generatedAt": "2026-07-16T10:14:09.932Z",
3
+ "cliVersion": "0.2.4",
4
4
  "commands": [
5
5
  "accounts",
6
6
  "accounts brand-bindings",
@@ -25,6 +25,7 @@
25
25
  "accounts personas",
26
26
  "accounts personas create",
27
27
  "accounts personas list",
28
+ "accounts reassign",
28
29
  "accounts status-snapshots",
29
30
  "accounts status-snapshots create",
30
31
  "accounts status-snapshots list",
@@ -71,6 +72,7 @@
71
72
  "brands list",
72
73
  "brands system-create",
73
74
  "brands system-list",
75
+ "brands system-update",
74
76
  "brands update",
75
77
  "calendar",
76
78
  "calendar list",
@@ -102,6 +104,9 @@
102
104
  "config init",
103
105
  "config set",
104
106
  "config show",
107
+ "content",
108
+ "content post-review",
109
+ "content review",
105
110
  "context",
106
111
  "context account",
107
112
  "context current",
@@ -248,6 +253,8 @@
248
253
  "query",
249
254
  "query account-pool-context",
250
255
  "query account-pools",
256
+ "query interaction-behavior-health",
257
+ "query interaction-schedule-context",
251
258
  "query subreddit-candidates",
252
259
  "reddit",
253
260
  "reddit batch-upsert",
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAw9C9B,8DAA8D;AAC9D,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,IAAI,CAOf;AAk2ED,OAAO,EAAE,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA2BpC,QAAA,MAAM,OAAO,SAAgB,CAAC;AAgiD9B,8DAA8D;AAC9D,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,IAAI,CAOf;AAy3ED,OAAO,EAAE,OAAO,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -334,6 +334,8 @@ reddit
334
334
  .option("--repost-type <t>", "repostType filter (none|first|second)")
335
335
  .option("--contains-brand-keyword <v>", "true | false | unset")
336
336
  .option("--primary-product <text>", "primary product contains match")
337
+ .option("--sort-by <f>", "score|comments_count|posted_at|view_count|seq_no")
338
+ .option("--sort-dir <d>", "asc|desc")
337
339
  .option("-n, --limit <n>", "limit per page", "100")
338
340
  .option("--offset <n>", "pagination offset")
339
341
  .option("--all", "fetch all pages (200 per request until total reached)")
@@ -351,6 +353,8 @@ reddit
351
353
  repostType: opts.repostType,
352
354
  containsBrandKeyword: opts.containsBrandKeyword,
353
355
  primaryProduct: opts.primaryProduct,
356
+ sortBy: opts.sortBy,
357
+ sortDir: opts.sortDir,
354
358
  ...redditAggregateQueryParams(opts),
355
359
  }, { fetchAll: Boolean(opts.all) });
356
360
  console.log(JSON.stringify(res, null, 2));
@@ -449,6 +453,43 @@ jobs
449
453
  console.log(JSON.stringify(res, null, 2));
450
454
  });
451
455
  // --- drafts & reports ---
456
+ const content = program
457
+ .command("content")
458
+ .description("内容质量(发布前 LLM 复审)");
459
+ content
460
+ .command("review")
461
+ .description("POST /content-review — 评论发布前独立 LLM 复审+优化(层③);degraded 时降级本地自审")
462
+ .requiredOption("-t, --team <teamId>", "Team UUID")
463
+ .requiredOption("-j, --json <json>", "{ accountId, draft, commentType, thread:{subreddit,postTitle,...}, rewrite?, stage?, attempt? }")
464
+ .action(async (opts) => {
465
+ let body;
466
+ try {
467
+ body = JSON.parse(opts.json);
468
+ }
469
+ catch {
470
+ console.error("Invalid -j / --json");
471
+ process.exit(1);
472
+ }
473
+ const res = await requireClient().contentReview(opts.team, body);
474
+ console.log(JSON.stringify(res, null, 2));
475
+ });
476
+ content
477
+ .command("post-review")
478
+ .description("POST /post-review — 帖子(title+body)发布前独立 LLM 审核(七维,独立模型 key);仅审帖子正文,评论审核用 content review")
479
+ .requiredOption("-t, --team <teamId>", "Team UUID")
480
+ .requiredOption("-j, --json <json>", "{ accountId, title, body, subreddit, subredditRules?, rewrite?, stage?, attempt? }")
481
+ .action(async (opts) => {
482
+ let body;
483
+ try {
484
+ body = JSON.parse(opts.json);
485
+ }
486
+ catch {
487
+ console.error("Invalid -j / --json");
488
+ process.exit(1);
489
+ }
490
+ const res = await requireClient().postReview(opts.team, body);
491
+ console.log(JSON.stringify(res, null, 2));
492
+ });
452
493
  const drafts = program.command("drafts").description("内容草稿");
453
494
  drafts
454
495
  .command("list")
@@ -555,7 +596,7 @@ brands
555
596
  brands
556
597
  .command("system-create")
557
598
  .description("POST /system/brands (preferred)")
558
- .requiredOption("-j, --json <json>", "{ name, slug? }")
599
+ .requiredOption("-j, --json <json>", "{ name, slug?, isInternal? }")
559
600
  .action(async (opts) => {
560
601
  let body;
561
602
  try {
@@ -568,9 +609,27 @@ brands
568
609
  const res = await requireClient().createSystemBrand({
569
610
  name: String(body.name ?? ""),
570
611
  slug: body.slug,
612
+ isInternal: body.isInternal,
571
613
  });
572
614
  console.log(JSON.stringify(res, null, 2));
573
615
  });
616
+ brands
617
+ .command("system-update")
618
+ .description("PATCH /system/brands/:id")
619
+ .requiredOption("--brand <uuid>", "Brand UUID")
620
+ .requiredOption("-j, --json <json>", "{ name?, status?, isInternal? }")
621
+ .action(async (opts) => {
622
+ let body;
623
+ try {
624
+ body = JSON.parse(opts.json);
625
+ }
626
+ catch {
627
+ console.error("Invalid -j / --json");
628
+ process.exit(1);
629
+ }
630
+ const res = await requireClient().updateSystemBrand(opts.brand, body);
631
+ console.log(JSON.stringify(res, null, 2));
632
+ });
574
633
  brands
575
634
  .command("list")
576
635
  .description("GET /teams/:teamId/brands (compat)")
@@ -821,10 +880,12 @@ plans
821
880
  .requiredOption("-t, --team <teamId>", "Team UUID")
822
881
  .option("--brand <uuid>", "brandId 过滤(推荐)")
823
882
  .option("--campaign <uuid>", "campaignId 过滤(legacy)")
883
+ .option("--status <status>", "派生状态过滤(scheduled/running/succeeded/failed/cancelled;无 entries 的计划保留存储值 draft/exported)")
824
884
  .action(async (opts) => {
825
885
  const res = await requireClient().listPublishingPlans(opts.team, {
826
886
  brandId: opts.brand,
827
887
  campaignId: opts.campaign,
888
+ status: opts.status,
828
889
  });
829
890
  console.log(JSON.stringify(res, null, 2));
830
891
  });
@@ -1253,7 +1314,7 @@ accounts
1253
1314
  .description("PATCH /accounts/:id — 更新 handle/status/karma/accountAgeDays 等核心字段。安全时段/时区/风险级别请用 `compliance risk-put`;浏览器环境请用 `browser-envs`")
1254
1315
  .requiredOption("-t, --team <teamId>", "Team UUID")
1255
1316
  .requiredOption("--account <uuid>", "Account UUID")
1256
- .requiredOption("-j, --json <json>", "{ handle?, profileUrl?, status?, externalId?, externalRef?, personaId?, karma?, accountAgeDays?, workflowStage?, metricsSource?, metricsUpdatedAt?, credentialsRef?, primaryProducts? }")
1317
+ .requiredOption("-j, --json <json>", "{ handle?, profileUrl?, status?, externalId?, externalRef?, personaId?, karma?, accountAgeDays?, registeredAt?, workflowStage?, metricsSource?, metricsUpdatedAt?, credentialsRef?, primaryProducts? }")
1257
1318
  .action(async (opts) => {
1258
1319
  const body = JSON.parse(opts.json);
1259
1320
  const misplaced = detectMisplacedAccountFields(body);
@@ -1264,6 +1325,18 @@ accounts
1264
1325
  const res = await requireClient().updateAccount(opts.team, opts.account, body);
1265
1326
  console.log(JSON.stringify(res, null, 2));
1266
1327
  });
1328
+ accounts
1329
+ .command("reassign")
1330
+ .description("POST /accounts/:id/reassign — 把账号从 -t <team> 移到 --to <team>。运营配置(浏览器环境/人设/子版亲和/封禁)跟账号走,纯历史留原队,原队待跑任务取消。目标队撞唯一键返回 409")
1331
+ .requiredOption("-t, --team <teamId>", "源 Team UUID(账号当前所属)")
1332
+ .requiredOption("--account <uuid>", "Account UUID")
1333
+ .requiredOption("--to <teamId>", "目标 Team UUID")
1334
+ .action(async (opts) => {
1335
+ const res = await requireClient().reassignAccount(opts.team, opts.account, {
1336
+ toTeamId: opts.to,
1337
+ });
1338
+ console.log(JSON.stringify(res, null, 2));
1339
+ });
1267
1340
  // 账号数据多面一次性同步:纯 CLI 编排——把单个 JSON 拆成各写入面,依次调用
1268
1341
  // 已有的 per-resource SDK 方法。服务端无新端点;每个面仍走各自校验过的路由。
1269
1342
  // JSON 中出现的面才写,未出现的跳过;逐面 continue-and-report,任一面失败退出码非零。
@@ -1439,7 +1512,7 @@ drafts
1439
1512
  .description("PATCH /content-drafts/:id")
1440
1513
  .requiredOption("-t, --team <teamId>", "Team UUID")
1441
1514
  .requiredOption("--draft <uuid>", "Draft UUID")
1442
- .requiredOption("-j, --json <json>", "{ title?, body?, status? }")
1515
+ .requiredOption("-j, --json <json>", "{ title?, body?, status?, expectedVersion? }(传 expectedVersion 开乐观并发,版本不匹配→409)")
1443
1516
  .action(async (opts) => {
1444
1517
  const body = JSON.parse(opts.json);
1445
1518
  const res = await requireClient().updateContentDraft(opts.team, opts.draft, body);
@@ -1950,7 +2023,7 @@ intel
1950
2023
  .command("runs-list")
1951
2024
  .description("GET .../subreddit-intelligence/runs")
1952
2025
  .option("--dimension <d>", "industry|keywords|tier")
1953
- .option("--status <s>", "queued|running|succeeded|failed")
2026
+ .option("--status <s>", "queued|running|succeeded|partial|failed")
1954
2027
  .option("-n, --limit <n>", "limit", "50")
1955
2028
  .option("--offset <n>", "offset", "0")
1956
2029
  .action(async (opts) => {
@@ -2781,6 +2854,8 @@ reddit
2781
2854
  .option("--repost-type <t>", "repostType filter (none|first|second)")
2782
2855
  .option("--contains-brand-keyword <v>", "true | false | unset")
2783
2856
  .option("--primary-product <text>", "primary product contains match")
2857
+ .option("--sort-by <f>", "score|comments_count|posted_at|view_count|seq_no")
2858
+ .option("--sort-dir <d>", "asc|desc")
2784
2859
  .option("-n, --limit <n>", "limit per page", "100")
2785
2860
  .option("--offset <n>", "pagination offset")
2786
2861
  .option("--all", "fetch all pages (200 per request until total reached)")
@@ -2797,6 +2872,8 @@ reddit
2797
2872
  repostType: opts.repostType,
2798
2873
  containsBrandKeyword: opts.containsBrandKeyword,
2799
2874
  primaryProduct: opts.primaryProduct,
2875
+ sortBy: opts.sortBy,
2876
+ sortDir: opts.sortDir,
2800
2877
  limit: Number(opts.limit),
2801
2878
  offset: opts.offset !== undefined ? Number(opts.offset) : undefined,
2802
2879
  ...redditAggregateQueryParams(opts),