@geoly-ai/social-hub-cli 0.3.33 → 0.3.35

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.
@@ -0,0 +1,33 @@
1
+ import type { Command } from "commander";
2
+ /**
3
+ * Reddit 账号平台判定状态交付面(Block C)—— `social-hub reddit-verification ...`。
4
+ *
5
+ * team 维度端点(`/v1/teams/:teamId/...`):读 = 非 client 内部角色 + internal 只读;
6
+ * 写(record observation / patch evidence)= senior 及以上(client 恒拒,见 authz
7
+ * `redditAccountVerification` 资源梯度)。⛔ 一期不暴露 dispatch policy——本 CLI 只吐
8
+ * 原始 verification state,不做「能不能派活」的建议。
9
+ */
10
+ declare const evidenceKinds: readonly ["dom_snapshot", "screenshot", "agent_narrative"];
11
+ declare const observedStates: readonly ["verification_required", "app_labeled", "verified"];
12
+ /** `observations record` 请求体的本地形状校验(非法输入 dry-run 前即拒)。 */
13
+ export declare function buildRecordObservationBody(raw: Record<string, unknown>): {
14
+ observedState: (typeof observedStates)[number];
15
+ observedAt: string;
16
+ requestId: string;
17
+ sourceEventType: string;
18
+ deadlineRaw?: string | null;
19
+ deadlineAt?: string | null;
20
+ platformStatusRaw?: string | null;
21
+ platformCaseId?: string | null;
22
+ documentLang?: string | null;
23
+ evidence: Array<{
24
+ kind: (typeof evidenceKinds)[number];
25
+ ref?: string | null;
26
+ host?: string | null;
27
+ capturedAt?: string | null;
28
+ expiresAt?: string | null;
29
+ }>;
30
+ };
31
+ export declare function registerRedditAccountVerificationCommands(program: Command): void;
32
+ export {};
33
+ //# sourceMappingURL=register-reddit-account-verification.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-reddit-account-verification.d.ts","sourceRoot":"","sources":["../src/register-reddit-account-verification.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUzC;;;;;;;GAOG;AAEH,QAAA,MAAM,aAAa,4DAIT,CAAC;AACX,QAAA,MAAM,cAAc,+DAIV,CAAC;AAQX,0DAA0D;AAC1D,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IACxE,aAAa,EAAE,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;QACrC,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3B,CAAC,CAAC;CACJ,CAqEA;AAED,wBAAgB,yCAAyC,CACvD,OAAO,EAAE,OAAO,GACf,IAAI,CA2LN"}
@@ -0,0 +1,218 @@
1
+ import { requireClient, resolveTeamId } from "./client.js";
2
+ import { assertApplyOrDryRun } from "./dry-run.js";
3
+ import { parseJsonOption, printResult, withCliErrorBoundary, } from "./output.js";
4
+ import { withPermissionGates } from "./permission-runner.js";
5
+ /**
6
+ * Reddit 账号平台判定状态交付面(Block C)—— `social-hub reddit-verification ...`。
7
+ *
8
+ * team 维度端点(`/v1/teams/:teamId/...`):读 = 非 client 内部角色 + internal 只读;
9
+ * 写(record observation / patch evidence)= senior 及以上(client 恒拒,见 authz
10
+ * `redditAccountVerification` 资源梯度)。⛔ 一期不暴露 dispatch policy——本 CLI 只吐
11
+ * 原始 verification state,不做「能不能派活」的建议。
12
+ */
13
+ const evidenceKinds = [
14
+ "dom_snapshot",
15
+ "screenshot",
16
+ "agent_narrative",
17
+ ];
18
+ const observedStates = [
19
+ "verification_required",
20
+ "app_labeled",
21
+ "verified",
22
+ ];
23
+ const evidenceAvailabilities = [
24
+ "available",
25
+ "expired",
26
+ "missing",
27
+ "revoked",
28
+ ];
29
+ /** `observations record` 请求体的本地形状校验(非法输入 dry-run 前即拒)。 */
30
+ export function buildRecordObservationBody(raw) {
31
+ const fail = (msg) => {
32
+ console.error(msg);
33
+ process.exit(1);
34
+ };
35
+ const str = (k, required) => {
36
+ const v = raw[k];
37
+ if (v === undefined || v === null) {
38
+ if (required)
39
+ return fail(`--json 缺少必填字段 ${k}(string)`);
40
+ return undefined;
41
+ }
42
+ if (typeof v !== "string" || v.trim() === "") {
43
+ return fail(`--json 字段 ${k} 须为非空 string`);
44
+ }
45
+ return v;
46
+ };
47
+ const observedState = str("observedState", true);
48
+ if (!observedStates.includes(observedState)) {
49
+ return fail(`--json 字段 observedState 须为 ${observedStates.join("|")}`);
50
+ }
51
+ const observedAt = str("observedAt", true);
52
+ const requestId = str("requestId", true);
53
+ const sourceEventType = str("sourceEventType", true);
54
+ const deadlineRaw = str("deadlineRaw", false);
55
+ const deadlineAt = str("deadlineAt", false);
56
+ // 🔴 迁移 0164 的三列。本函数是**显式白名单**(逐键取,不是透传),所以服务端每加
57
+ // 一个字段这里都必须同步加一条 —— 否则调用方送了、CLI 静默丢掉,而 `--dry-run` 的
58
+ // wouldApply 是 CLI **自己重新拼出来的**,同样不会回显被丢掉的键:
59
+ // 「送了但没写进去」与「写进去了」在 CLI 输出上完全一样。
60
+ // ⚠️ 这已经真实发生过一次(0.3.34 漏了这三列,矩阵侧端到端才发现)。
61
+ const platformStatusRaw = str("platformStatusRaw", false);
62
+ const platformCaseId = str("platformCaseId", false);
63
+ const documentLang = str("documentLang", false);
64
+ const evidenceRaw = raw.evidence;
65
+ if (!Array.isArray(evidenceRaw) || evidenceRaw.length === 0) {
66
+ return fail("--json 字段 evidence 须为非空数组");
67
+ }
68
+ const evidence = evidenceRaw.map((entry, i) => {
69
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
70
+ return fail(`--json evidence[${i}] 须为对象`);
71
+ }
72
+ const e = entry;
73
+ const kind = e.kind;
74
+ if (typeof kind !== "string" ||
75
+ !evidenceKinds.includes(kind)) {
76
+ return fail(`--json evidence[${i}].kind 须为 ${evidenceKinds.join("|")}`);
77
+ }
78
+ return {
79
+ kind: kind,
80
+ ref: typeof e.ref === "string" ? e.ref : undefined,
81
+ host: typeof e.host === "string" ? e.host : undefined,
82
+ capturedAt: typeof e.capturedAt === "string" ? e.capturedAt : undefined,
83
+ expiresAt: typeof e.expiresAt === "string" ? e.expiresAt : undefined,
84
+ };
85
+ });
86
+ return {
87
+ observedState: observedState,
88
+ observedAt,
89
+ requestId,
90
+ sourceEventType,
91
+ ...(deadlineRaw ? { deadlineRaw } : {}),
92
+ ...(deadlineAt ? { deadlineAt } : {}),
93
+ ...(platformStatusRaw ? { platformStatusRaw } : {}),
94
+ ...(platformCaseId ? { platformCaseId } : {}),
95
+ ...(documentLang ? { documentLang } : {}),
96
+ evidence,
97
+ };
98
+ }
99
+ export function registerRedditAccountVerificationCommands(program) {
100
+ const rav = program
101
+ .command("reddit-verification")
102
+ .description("Reddit 账号平台判定状态(team 级;client 恒拒;⛔ 不含 dispatch policy)");
103
+ // ── 读:单账号详情(current + 最近 N 条 observation)─────────────────────
104
+ rav
105
+ .command("get")
106
+ .description("GET .../accounts/:accountId/reddit-verification — current + 最近 N 条 observation(含 evidence 摘要)")
107
+ .option("-t, --team <teamId>", "Team UUID(缺省用当前 context)")
108
+ .requiredOption("--account-id <uuid>", "social_accounts.id")
109
+ .option("--limit <n>", "最近观测条数上限(1-100,默认 20)")
110
+ .action(async (opts) => {
111
+ await withCliErrorBoundary(async () => {
112
+ await withPermissionGates({
113
+ command: "reddit-verification get",
114
+ gates: [{ resource: "redditAccountVerification", action: "read" }],
115
+ teamId: opts.team,
116
+ }, async () => {
117
+ const teamId = resolveTeamId(opts.team);
118
+ printResult(await requireClient().getRedditAccountVerification(teamId, opts.accountId, { limit: opts.limit ? Number(opts.limit) : undefined }));
119
+ });
120
+ });
121
+ });
122
+ // ── 读:列表 + coverage ──────────────────────────────────────────────────
123
+ rav
124
+ .command("list")
125
+ .description("GET .../reddit-verification/accounts — 列表 + coverage 计数(默认只看需处理:verification_required+app_labeled)")
126
+ .option("-t, --team <teamId>", "Team UUID(缺省用当前 context)")
127
+ .option("--all", "看全量(含 unknown/verified),默认只看需处理队列", false)
128
+ .option("--limit <n>", "最多返回条数(1-200,默认 50)")
129
+ .option("--offset <n>", "偏移")
130
+ .action(async (opts) => {
131
+ await withCliErrorBoundary(async () => {
132
+ await withPermissionGates({
133
+ command: "reddit-verification list",
134
+ gates: [{ resource: "redditAccountVerification", action: "list" }],
135
+ teamId: opts.team,
136
+ }, async () => {
137
+ const teamId = resolveTeamId(opts.team);
138
+ printResult(await requireClient().listRedditAccountVerificationAccounts(teamId, {
139
+ needsAttention: opts.all ? false : true,
140
+ limit: opts.limit ? Number(opts.limit) : undefined,
141
+ offset: opts.offset ? Number(opts.offset) : undefined,
142
+ }));
143
+ });
144
+ });
145
+ });
146
+ // ── 写:记一条观测(幂等键 requestId;响应固定三态)───────────────────────
147
+ rav
148
+ .command("record-observation")
149
+ .description("POST .../accounts/:accountId/reddit-verification/observations — 幂等键 requestId;201=recorded/200=duplicate_delivery/409=两种专用冲突码")
150
+ .option("-t, --team <teamId>", "Team UUID(缺省用当前 context)")
151
+ .requiredOption("--account-id <uuid>", "social_accounts.id")
152
+ .requiredOption("-j, --json <json>", '{ "observedState","observedAt","requestId","sourceEventType", 可选 "deadlineRaw"/"deadlineAt", "evidence":[{"kind",...}] }')
153
+ .option("--dry-run", "预览", false)
154
+ .option("--apply", "执行写入", false)
155
+ .action(async (opts) => {
156
+ await withCliErrorBoundary(async () => {
157
+ await withPermissionGates({
158
+ command: "reddit-verification record-observation",
159
+ gates: [
160
+ { resource: "redditAccountVerification", action: "create" },
161
+ ],
162
+ teamId: opts.team,
163
+ }, async () => {
164
+ const body = buildRecordObservationBody(parseJsonOption(opts.json));
165
+ if (!assertApplyOrDryRun(opts, {
166
+ command: "reddit-verification record-observation",
167
+ danger: "write",
168
+ summary: { accountId: opts.accountId, ...body },
169
+ })) {
170
+ return;
171
+ }
172
+ const teamId = resolveTeamId(opts.team);
173
+ printResult(await requireClient().recordRedditAccountVerificationObservation(teamId, opts.accountId, body));
174
+ });
175
+ });
176
+ });
177
+ // ── 写:标注证据可用性(只改 availability;拒绝较旧检查覆盖较新结果)────
178
+ rav
179
+ .command("mark-evidence")
180
+ .description("PATCH .../reddit-verification/evidence/:evidenceId — 只改 availability;checkedAt 必填,较旧检查覆盖较新结果会被拒(409)")
181
+ .option("-t, --team <teamId>", "Team UUID(缺省用当前 context)")
182
+ .requiredOption("--evidence-id <uuid>", "evidence 行 id")
183
+ .requiredOption("--availability <a>", `证据可用性(${evidenceAvailabilities.join("|")})`)
184
+ .requiredOption("--checked-at <iso>", "调用方实际检查该证据的时间(ISO 8601)")
185
+ .option("--dry-run", "预览", false)
186
+ .option("--apply", "执行写入", false)
187
+ .action(async (opts) => {
188
+ await withCliErrorBoundary(async () => {
189
+ await withPermissionGates({
190
+ command: "reddit-verification mark-evidence",
191
+ gates: [
192
+ { resource: "redditAccountVerification", action: "update" },
193
+ ],
194
+ teamId: opts.team,
195
+ }, async () => {
196
+ const availability = opts.availability;
197
+ if (!evidenceAvailabilities.includes(availability)) {
198
+ console.error(`--availability 须为 ${evidenceAvailabilities.join("|")}`);
199
+ process.exit(1);
200
+ }
201
+ const body = {
202
+ availability: availability,
203
+ checkedAt: opts.checkedAt,
204
+ };
205
+ if (!assertApplyOrDryRun(opts, {
206
+ command: "reddit-verification mark-evidence",
207
+ danger: "write",
208
+ summary: { evidenceId: opts.evidenceId, ...body },
209
+ })) {
210
+ return;
211
+ }
212
+ const teamId = resolveTeamId(opts.team);
213
+ printResult(await requireClient().patchRedditAccountVerificationEvidence(teamId, opts.evidenceId, body));
214
+ });
215
+ });
216
+ });
217
+ }
218
+ //# sourceMappingURL=register-reddit-account-verification.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-reddit-account-verification.js","sourceRoot":"","sources":["../src/register-reddit-account-verification.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=register-reddit-account-verification.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-reddit-account-verification.test.d.ts","sourceRoot":"","sources":["../src/register-reddit-account-verification.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * `buildRecordObservationBody` —— Reddit 账号验证观测的 `--json` 解析。
3
+ *
4
+ * 🔴 这个文件只为一件事存在:**它是显式白名单,不是透传**。服务端每加一个字段,
5
+ * 这里都必须同步加一条,否则调用方送了、CLI 静默丢掉。
6
+ *
7
+ * ⚠️ 而且失败的样子和成功一模一样:`--dry-run` 的 `wouldApply` 是 CLI **自己重新拼出来
8
+ * 的**,不是调用方 payload 的回声,所以被丢掉的键在 dry-run 里同样不显示。
9
+ * 「送了但没写进去」与「写进去了」在 CLI 输出上**完全无法区分**。
10
+ *
11
+ * 这已经真实发生过:0.3.34 漏了迁移 0164 的三列(`platformStatusRaw` /
12
+ * `platformCaseId` / `documentLang`),矩阵侧做端到端才发现。后果不只是少三个字段——
13
+ * 调用方的 `requestId` 是**带着这三个字段**算出来的,少带写进去,存下来的行就与它自己
14
+ * 的幂等键对不上;将来用支持字段的 CLI 重送同一次 capture,会撞「同 requestId、
15
+ * 不同内容」的 409。
16
+ *
17
+ * 所以下面那条「透传全部已知字段」的用例是**回归闸门**:新增字段时它会红,提醒你
18
+ * 这里要跟着改。
19
+ */
20
+ import { describe, expect, it } from "vitest";
21
+ import { buildRecordObservationBody } from "./register-reddit-account-verification.js";
22
+ const base = {
23
+ observedState: "verification_required",
24
+ observedAt: "2026-09-01T00:00:00.000Z",
25
+ requestId: "req-1",
26
+ sourceEventType: "risk.human_verification",
27
+ evidence: [{ kind: "dom_snapshot", ref: "dom-1" }],
28
+ };
29
+ describe("buildRecordObservationBody", () => {
30
+ it("🔴 迁移 0164 的三列必须透传,⛔ 不许被白名单吞掉", () => {
31
+ const body = buildRecordObservationBody({
32
+ ...base,
33
+ platformStatusRaw: "ACTION_NEEDED",
34
+ platformCaseId: "67a222f2-86d1-44ba-a59a-3a24e8f5c41a",
35
+ documentLang: "es-ES",
36
+ });
37
+ expect(body.platformStatusRaw).toBe("ACTION_NEEDED");
38
+ expect(body.platformCaseId).toBe("67a222f2-86d1-44ba-a59a-3a24e8f5c41a");
39
+ expect(body.documentLang).toBe("es-ES");
40
+ });
41
+ /**
42
+ * `platformStatusRaw` 的值域**刻意不校验** —— 这个字段存在的唯一理由就是接住 Reddit
43
+ * 将来新增的、我们还不认识的状态值。若在 CLI 或契约层限死枚举,未知值会被拒收,
44
+ * 那就回到了「只有映射结果、发现不了新状态」的老问题。
45
+ */
46
+ it("🔴 未知的 platformStatusRaw 原样透传,⛔ 不做枚举校验", () => {
47
+ const body = buildRecordObservationBody({
48
+ ...base,
49
+ platformStatusRaw: "SOME_FUTURE_STATE",
50
+ });
51
+ expect(body.platformStatusRaw).toBe("SOME_FUTURE_STATE");
52
+ });
53
+ // 正向控制组:证明上面那两条不是因为函数把**所有**输入都原样带出来了。
54
+ // 未知键必须被丢弃(白名单的本意),⛔ 不能连它一起透传。
55
+ it("未在白名单里的键仍然被丢弃(证明白名单本身还在起作用)", () => {
56
+ const body = buildRecordObservationBody({
57
+ ...base,
58
+ someUnknownKey: "should-not-appear",
59
+ });
60
+ expect(body).not.toHaveProperty("someUnknownKey");
61
+ // 前提:合法字段确实带出来了,否则上面那条 not.toHaveProperty 在验空气。
62
+ expect(body.sourceEventType).toBe("risk.human_verification");
63
+ });
64
+ it("三列省略时不出现在 body 里(`null` 由服务端解释为「本次未取到」)", () => {
65
+ const body = buildRecordObservationBody({ ...base });
66
+ expect(body).not.toHaveProperty("platformStatusRaw");
67
+ expect(body).not.toHaveProperty("platformCaseId");
68
+ expect(body).not.toHaveProperty("documentLang");
69
+ // 前提:必填字段在,证明这次解析确实成功了。
70
+ expect(body.requestId).toBe("req-1");
71
+ });
72
+ });
73
+ //# sourceMappingURL=register-reddit-account-verification.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-reddit-account-verification.test.js","sourceRoot":"","sources":["../src/register-reddit-account-verification.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAE9C,OAAO,EAAE,0BAA0B,EAAE,MAAM,2CAA2C,CAAC;AAEvF,MAAM,IAAI,GAAG;IACX,aAAa,EAAE,uBAAuB;IACtC,UAAU,EAAE,0BAA0B;IACtC,SAAS,EAAE,OAAO;IAClB,eAAe,EAAE,yBAAyB;IAC1C,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC;CACnD,CAAC;AAEF,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;IAC1C,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,IAAI,GAAG,0BAA0B,CAAC;YACtC,GAAG,IAAI;YACP,iBAAiB,EAAE,eAAe;YAClC,cAAc,EAAE,sCAAsC;YACtD,YAAY,EAAE,OAAO;SACtB,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;QACzE,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1C,CAAC,CAAC,CAAC;IAEH;;;;OAIG;IACH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,IAAI,GAAG,0BAA0B,CAAC;YACtC,GAAG,IAAI;YACP,iBAAiB,EAAE,mBAAmB;SACvC,CAAC,CAAC;QACH,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;IAEH,uCAAuC;IACvC,+BAA+B;IAC/B,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,MAAM,IAAI,GAAG,0BAA0B,CAAC;YACtC,GAAG,IAAI;YACP,cAAc,EAAE,mBAAmB;SACT,CAAC,CAAC;QAC9B,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;QAClD,gDAAgD;QAChD,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yCAAyC,EAAE,GAAG,EAAE;QACjD,MAAM,IAAI,GAAG,0BAA0B,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;QACrD,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,cAAc,CAAC,cAAc,CAAC,CAAC;QAChD,wBAAwB;QACxB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geoly-ai/social-hub-cli",
3
- "version": "0.3.33",
3
+ "version": "0.3.35",
4
4
  "type": "module",
5
5
  "description": "social-hub CLI for Social Ops Hub",
6
6
  "repository": {
@@ -22,8 +22,8 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "commander": "^12.1.0",
25
- "@geoly-ai/social-hub-authz": "0.0.19",
26
- "@geoly-ai/social-hub-sdk": "0.1.6"
25
+ "@geoly-ai/social-hub-authz": "0.0.20",
26
+ "@geoly-ai/social-hub-sdk": "0.1.9"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^22.10.2",
@@ -4,7 +4,7 @@
4
4
  "fileCount": 1656,
5
5
  "files": {
6
6
  "README.md": "5246d4ae2712e3e656b101041929183d7ae7f8ea487dacf5854187826ab7c335",
7
- "manifest.json": "31478ce9f69ae1e789065feba800e702fdf6f7bf8e6a2f18a5c45cb84fabfe63",
7
+ "manifest.json": "758db248bba076db32436dfa19bb7906e073998b1bd118dcbe14ac60105e1b59",
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": "a3f615eb19c96161cbbcf7d014b51f9980149ffa4a5ef11f19352db813fa047d",
1646
+ "social-hub-intelligence/SKILL.md": "beb25541425dda8639e799b6d8c027dee7e9d4ecd02433b8236ea06280bd82af",
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",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 1,
3
3
  "package": "social-ops-hub",
4
- "cliVersion": "0.3.33",
4
+ "cliVersion": "0.3.35",
5
5
  "repository": "social-ops-hub",
6
6
  "publish": {
7
7
  "registry": "https://skill.sh",
@@ -79,6 +79,36 @@ social-hub intelligence posts-import --file posts.json
79
79
  # 分类=material(标题/正文/flair/作者)变化;指标(score/comments)变不算 updated;陈旧回放不覆盖较新数据。
80
80
  ```
81
81
 
82
+ **🔴 强烈建议带上 `rawPayload`(可选字段,但不带就永久丢失 format)**:
83
+
84
+ ```jsonc
85
+ // posts.json 的每一项,除上面那 6 个必填字段外,再带:
86
+ "rawPayload": {
87
+ "is_self": true, // 有正文帖 / link 帖
88
+ "is_video": false,
89
+ "is_gallery": false,
90
+ "url": "https://...", // ⛔ 键名必须是 url,不是 link_url(见下)
91
+ "selftext": "..." // 有就带,没有就省略
92
+ }
93
+ ```
94
+
95
+ **为什么重要**:服务端从 `rawPayload` 派生帖子的 `format`(poll/gallery/native_video/
96
+ image/external_link/self_text),**这个派生不问 LLM、只认原始字段**。不带 rawPayload 时
97
+ format 恒为 `unknown` —— 2026-08-31 实测生产 200 条连续 run **100% 是 unknown**,
98
+ 根因就是回灌方没带这些字段(而本文档此前也没说要带)。
99
+
100
+ ⚠️ **键名必须用 Reddit 原始名。** `reddit-voc-volume` 脚本输出的是 **`link_url`**,
101
+ 而服务端读的是 **`url`** —— 直接把脚本输出整个塞进 rawPayload,external_link 那一档
102
+ **匹配不上、静默落 unknown**,比不带更难查(因为"明明传了")。映射一下再传。
103
+
104
+ ⚠️ ⛔ **不要传脚本的加工字段**(`selftext_chars` / `selftext_excerpt` / `body_status` /
105
+ `sample_role` 等):对 format 派生无用,只会让 rawPayload 变大、且看起来像服务端应该认识它们。
106
+
107
+ ⚠️ `image` 与 `poll` 两类**仍会是 unknown**(需要 `post_hint` / `poll_data`,
108
+ Arctic Shift 经脚本层后拿不到)。这是"确实没有证据",可接受;
109
+ ⛔ **不要为了凑满六值去猜**(比如拿 thumbnail 猜 image)——format 进 revision hash,
110
+ 猜错会静默产生一个错误的 revision 并据此重标。
111
+
82
112
  **热帖归因**:`watchlistId` 是品牌归因主键(watchlist 挂 brandId);受限主体回灌时该条
83
113
  必须能归因到授权板块、watchlist 属本牌、permalink 板块与声明一致,否则该条 rejected
84
114
  (不整批拒)。permalink 全局去重(同帖多次回灌折叠为一行)。