@geoly-ai/social-hub-cli 0.3.12 → 0.3.13

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,43 @@
1
+ /**
2
+ * `social-hub content review` 的便利参数合并(作者关系轴)。
3
+ *
4
+ * 抽成纯函数是为了单测:合并逻辑有两条容易写错的边界 ——
5
+ * (1) `resolveJsonArg<unknown>` 的返回只是**类型断言**,运行时 body 仍可能是
6
+ * `null` / 数组 / 字符串,直接展开会静默丢数据;
7
+ * (2) `--thread-permalink` 要写进嵌套的 `thread` 对象,但 `thread.subreddit` / `postTitle`
8
+ * 是服务端必填 —— body 里没有 thread 时**本地报错**,绝不凭空造一个只有 permalink 的
9
+ * thread(那只会换来一个更晚、更难懂的 400)。
10
+ *
11
+ * ⚠️ `authorRelationHint` 只是**非权威提示**:服务端按账号 handle × permalink × 帖子快照
12
+ * 派生权威 `authorRelation`,证据永远覆盖 hint。本模块不做任何「本地推断身份」的事。
13
+ */
14
+ import type { ContentReviewAuthorRelationHint } from "@geoly-ai/social-hub-sdk";
15
+ /** 参数级错误(CLI 侧本地拒绝,不发请求)。 */
16
+ export declare class ContentReviewArgError extends Error {
17
+ constructor(message: string);
18
+ }
19
+ export declare const AUTHOR_RELATION_HINT_VALUES: readonly ContentReviewAuthorRelationHint[];
20
+ /** 与 contracts `contentReviewThreadSchema.threadPermalink` 的上限一致。 */
21
+ export declare const MAX_THREAD_PERMALINK_LENGTH = 2048;
22
+ /**
23
+ * 解析 `--author-relation-hint`。只收精确的枚举值:大小写/空白一律不猜 ——
24
+ * 猜错会把「声称是作者」和「声称不是」搞反,而前者失配是 422 硬失败。
25
+ */
26
+ export declare function parseAuthorRelationHint(raw: string): ContentReviewAuthorRelationHint;
27
+ export type ContentReviewFlagInput = {
28
+ threadPermalink?: string;
29
+ authorRelationHint?: string;
30
+ };
31
+ /**
32
+ * 把便利参数合并进 body(不原地修改入参;便利参数覆盖 `-j` 里的同名值,
33
+ * 与既有 `--brand-context` 的覆盖语义一致)。
34
+ */
35
+ export declare function applyContentReviewFlags(body: unknown, flags: ContentReviewFlagInput): Record<string, unknown>;
36
+ /**
37
+ * 422 `AUTHOR_RELATION_MISMATCH` 的可行动提示。
38
+ *
39
+ * 刻意**不打印服务端 details**:里面含规范化 handle / 帖子作者 / permalink。
40
+ * 也刻意不建议「换一个 permalink 重试」—— 那是 anchor shopping,正是这条 422 要防的。
41
+ */
42
+ export declare const AUTHOR_RELATION_MISMATCH_HINT: string;
43
+ //# sourceMappingURL=content-review-args.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-review-args.d.ts","sourceRoot":"","sources":["../../src/lib/content-review-args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,0BAA0B,CAAC;AAEhF,6BAA6B;AAC7B,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,eAAO,MAAM,2BAA2B,EAAE,SAAS,+BAA+B,EACxD,CAAC;AAE3B,qEAAqE;AACrE,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAMhD;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,GACV,+BAA+B,CASjC;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B,CAAC;AAEF;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,OAAO,EACb,KAAK,EAAE,sBAAsB,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAsCzB;AAED;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,QAIiC,CAAC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * `social-hub content review` 的便利参数合并(作者关系轴)。
3
+ *
4
+ * 抽成纯函数是为了单测:合并逻辑有两条容易写错的边界 ——
5
+ * (1) `resolveJsonArg<unknown>` 的返回只是**类型断言**,运行时 body 仍可能是
6
+ * `null` / 数组 / 字符串,直接展开会静默丢数据;
7
+ * (2) `--thread-permalink` 要写进嵌套的 `thread` 对象,但 `thread.subreddit` / `postTitle`
8
+ * 是服务端必填 —— body 里没有 thread 时**本地报错**,绝不凭空造一个只有 permalink 的
9
+ * thread(那只会换来一个更晚、更难懂的 400)。
10
+ *
11
+ * ⚠️ `authorRelationHint` 只是**非权威提示**:服务端按账号 handle × permalink × 帖子快照
12
+ * 派生权威 `authorRelation`,证据永远覆盖 hint。本模块不做任何「本地推断身份」的事。
13
+ */
14
+ /** 参数级错误(CLI 侧本地拒绝,不发请求)。 */
15
+ export class ContentReviewArgError extends Error {
16
+ constructor(message) {
17
+ super(message);
18
+ this.name = "ContentReviewArgError";
19
+ }
20
+ }
21
+ export const AUTHOR_RELATION_HINT_VALUES = ["post_author", "other"];
22
+ /** 与 contracts `contentReviewThreadSchema.threadPermalink` 的上限一致。 */
23
+ export const MAX_THREAD_PERMALINK_LENGTH = 2048;
24
+ function isPlainRecord(value) {
25
+ return typeof value === "object" && value !== null && !Array.isArray(value);
26
+ }
27
+ /**
28
+ * 解析 `--author-relation-hint`。只收精确的枚举值:大小写/空白一律不猜 ——
29
+ * 猜错会把「声称是作者」和「声称不是」搞反,而前者失配是 422 硬失败。
30
+ */
31
+ export function parseAuthorRelationHint(raw) {
32
+ if (AUTHOR_RELATION_HINT_VALUES.includes(raw) === false) {
33
+ throw new ContentReviewArgError(`--author-relation-hint 只接受 ${AUTHOR_RELATION_HINT_VALUES.join(" | ")}(区分大小写),收到: ${JSON.stringify(raw)}`);
34
+ }
35
+ return raw;
36
+ }
37
+ /**
38
+ * 把便利参数合并进 body(不原地修改入参;便利参数覆盖 `-j` 里的同名值,
39
+ * 与既有 `--brand-context` 的覆盖语义一致)。
40
+ */
41
+ export function applyContentReviewFlags(body, flags) {
42
+ if (!isPlainRecord(body)) {
43
+ throw new ContentReviewArgError(`content review 的 JSON 入参必须是一个对象,收到: ${Array.isArray(body) ? "array" : String(body === null ? "null" : typeof body)}`);
44
+ }
45
+ const next = { ...body };
46
+ if (flags.authorRelationHint !== undefined) {
47
+ next["authorRelationHint"] = parseAuthorRelationHint(flags.authorRelationHint);
48
+ }
49
+ if (flags.threadPermalink !== undefined) {
50
+ // 空串/纯空白不是「不给」:它会覆盖掉 -j 里本来有效的 permalink,而服务端
51
+ // `z.string().trim().min(1)` 只会回一个 400。本地拒绝,给出能直接照做的提示。
52
+ const permalink = flags.threadPermalink.trim();
53
+ if (permalink.length === 0) {
54
+ throw new ContentReviewArgError("--thread-permalink 不能为空:要么给出所审原帖的真实 permalink,要么整个省略该参数(省略=按 unknown 基线审核,不受惩罚)。");
55
+ }
56
+ if (permalink.length > MAX_THREAD_PERMALINK_LENGTH) {
57
+ throw new ContentReviewArgError(`--thread-permalink 超过服务端上限 ${MAX_THREAD_PERMALINK_LENGTH} 字符(收到 ${permalink.length})。`);
58
+ }
59
+ const thread = next["thread"];
60
+ if (!isPlainRecord(thread)) {
61
+ throw new ContentReviewArgError("--thread-permalink 需要 JSON 入参里已有 thread 对象(thread.subreddit / thread.postTitle 是必填,CLI 不会替你编造)。");
62
+ }
63
+ next["thread"] = { ...thread, threadPermalink: permalink };
64
+ }
65
+ return next;
66
+ }
67
+ /**
68
+ * 422 `AUTHOR_RELATION_MISMATCH` 的可行动提示。
69
+ *
70
+ * 刻意**不打印服务端 details**:里面含规范化 handle / 帖子作者 / permalink。
71
+ * 也刻意不建议「换一个 permalink 重试」—— 那是 anchor shopping,正是这条 422 要防的。
72
+ */
73
+ export const AUTHOR_RELATION_MISMATCH_HINT = "提示:你显式声称 --author-relation-hint post_author,但 Hub 侧帖子快照显示本次发布账号不是该帖作者。\n" +
74
+ " 这是硬失败(本次一次 LLM 都没调用,服务端已落安全审计),重试不会自愈,换一个能通过校验的 permalink 更不行。\n" +
75
+ " 请核对:发布账号是否搞错了 / thread.threadPermalink 是否确实指向本次要回复的那篇帖子。\n" +
76
+ " 如果你本来就不是原帖作者:去掉 --author-relation-hint 重跑即可(不声称=正常按证据派生,不影响评分标准)。";
77
+ //# sourceMappingURL=content-review-args.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-review-args.js","sourceRoot":"","sources":["../../src/lib/content-review-args.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,6BAA6B;AAC7B,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC9C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;IACtC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,2BAA2B,GACtC,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;AAE3B,qEAAqE;AACrE,MAAM,CAAC,MAAM,2BAA2B,GAAG,IAAI,CAAC;AAEhD,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,GAAW;IAEX,IACG,2BAAiD,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,KAAK,EAC1E,CAAC;QACD,MAAM,IAAI,qBAAqB,CAC7B,8BAA8B,2BAA2B,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAC1G,CAAC;IACJ,CAAC;IACD,OAAO,GAAsC,CAAC;AAChD,CAAC;AAOD;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,IAAa,EACb,KAA6B;IAE7B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,qBAAqB,CAC7B,uCAAuC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,CACtH,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,GAA4B,EAAE,GAAG,IAAI,EAAE,CAAC;IAElD,IAAI,KAAK,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;QAC3C,IAAI,CAAC,oBAAoB,CAAC,GAAG,uBAAuB,CAClD,KAAK,CAAC,kBAAkB,CACzB,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;QACxC,8CAA8C;QAC9C,wDAAwD;QACxD,MAAM,SAAS,GAAG,KAAK,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;QAC/C,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,qBAAqB,CAC7B,kFAAkF,CACnF,CAAC;QACJ,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,GAAG,2BAA2B,EAAE,CAAC;YACnD,MAAM,IAAI,qBAAqB,CAC7B,8BAA8B,2BAA2B,UAAU,SAAS,CAAC,MAAM,IAAI,CACxF,CAAC;QACJ,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9B,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,qBAAqB,CAC7B,iGAAiG,CAClG,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,CAAC;IAC7D,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,6BAA6B,GACxC,0EAA0E;IAC1E,wEAAwE;IACxE,kEAAkE;IAClE,yEAAyE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=content-review-args.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-review-args.test.d.ts","sourceRoot":"","sources":["../../src/lib/content-review-args.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * `content review` 便利参数合并的纯函数单测。
3
+ *
4
+ * 重点不是「flag 能写进 body」,而是几条 fail-closed 边界:
5
+ * - JSON 入参运行时不是对象(null/数组/字符串)必须本地报错,不能静默展开成空对象;
6
+ * - `--thread-permalink` 不会凭空造 thread(subreddit/postTitle 是服务端必填);
7
+ * - hint 只收精确枚举值 —— 猜大小写会把「声称是作者」和「声称不是」搞反,而前者失配是
8
+ * 422 硬失败。
9
+ */
10
+ import { describe, expect, it } from "vitest";
11
+ import { applyContentReviewFlags, AUTHOR_RELATION_MISMATCH_HINT, ContentReviewArgError, MAX_THREAD_PERMALINK_LENGTH, parseAuthorRelationHint, } from "./content-review-args.js";
12
+ const baseBody = {
13
+ draft: "Update: turned out to be the PSU cable.",
14
+ commentType: "top_level",
15
+ thread: {
16
+ subreddit: "buildapc",
17
+ postTitle: "PC reboots under load",
18
+ topComments: ["check the PSU"],
19
+ },
20
+ };
21
+ describe("applyContentReviewFlags", () => {
22
+ it("无 flag 时内容不变,且不原地修改入参", () => {
23
+ const input = structuredClone(baseBody);
24
+ const out = applyContentReviewFlags(input, {});
25
+ expect(out).toEqual(baseBody);
26
+ expect(out).not.toBe(input);
27
+ expect(input).toEqual(baseBody);
28
+ });
29
+ it("--thread-permalink 写进 thread 并保留 thread 其他字段", () => {
30
+ const input = structuredClone(baseBody);
31
+ const out = applyContentReviewFlags(input, {
32
+ threadPermalink: "https://www.reddit.com/r/buildapc/comments/abc/x/",
33
+ });
34
+ expect(out["thread"]).toEqual({
35
+ subreddit: "buildapc",
36
+ postTitle: "PC reboots under load",
37
+ topComments: ["check the PSU"],
38
+ threadPermalink: "https://www.reddit.com/r/buildapc/comments/abc/x/",
39
+ });
40
+ // 原 body.thread 未被就地改写。
41
+ expect(input.thread).not.toHaveProperty("threadPermalink");
42
+ });
43
+ it("flag 覆盖 -j 里的同名值(与 --brand-context 覆盖语义一致)", () => {
44
+ const out = applyContentReviewFlags({
45
+ ...structuredClone(baseBody),
46
+ authorRelationHint: "other",
47
+ thread: { ...baseBody.thread, threadPermalink: "https://old/" },
48
+ }, {
49
+ threadPermalink: "https://new/",
50
+ authorRelationHint: "post_author",
51
+ });
52
+ expect(out["authorRelationHint"]).toBe("post_author");
53
+ expect(out["thread"]["threadPermalink"]).toBe("https://new/");
54
+ });
55
+ it("hint 可以单独给(不强制配 permalink;服务端负责派生 unknown + 记 unverified claim)", () => {
56
+ const out = applyContentReviewFlags(structuredClone(baseBody), {
57
+ authorRelationHint: "post_author",
58
+ });
59
+ expect(out["authorRelationHint"]).toBe("post_author");
60
+ expect(out["thread"]["threadPermalink"]).toBe(undefined);
61
+ });
62
+ it.each([
63
+ ["null", null],
64
+ ["数组", [1, 2]],
65
+ ["字符串", "not-json-object"],
66
+ ["数字", 42],
67
+ ])("根 JSON 是 %s → 本地参数错误", (_label, raw) => {
68
+ expect(() => applyContentReviewFlags(raw, {})).toThrow(ContentReviewArgError);
69
+ });
70
+ it.each([
71
+ ["缺失", {}],
72
+ ["null", { thread: null }],
73
+ ["数组", { thread: [] }],
74
+ ["字符串", { thread: "buildapc" }],
75
+ ])("thread %s 时 --thread-permalink 报错而非凭空造 thread", (_l, body) => {
76
+ expect(() => applyContentReviewFlags(body, { threadPermalink: "https://x/" })).toThrow(/thread 对象/);
77
+ });
78
+ it.each([
79
+ ["空串", ""],
80
+ ["纯空白", " "],
81
+ ["制表/换行", "\t\n"],
82
+ ])("--thread-permalink 是 %s → 报错(否则会静默覆盖掉 -j 里的有效 permalink,把可核验审核降级成 unknown)", (_l, raw) => {
83
+ expect(() => applyContentReviewFlags(structuredClone(baseBody), {
84
+ threadPermalink: raw,
85
+ })).toThrow(/不能为空/);
86
+ });
87
+ it("permalink 两端空白被 trim 后写入", () => {
88
+ const out = applyContentReviewFlags(structuredClone(baseBody), {
89
+ threadPermalink: " https://x/ ",
90
+ });
91
+ expect(out["thread"]["threadPermalink"]).toBe("https://x/");
92
+ });
93
+ it("permalink 超过服务端上限 → 本地报错", () => {
94
+ expect(() => applyContentReviewFlags(structuredClone(baseBody), {
95
+ threadPermalink: "a".repeat(MAX_THREAD_PERMALINK_LENGTH + 1),
96
+ })).toThrow(new RegExp(String(MAX_THREAD_PERMALINK_LENGTH)));
97
+ // 边界值本身放行。
98
+ expect(() => applyContentReviewFlags(structuredClone(baseBody), {
99
+ threadPermalink: "a".repeat(MAX_THREAD_PERMALINK_LENGTH),
100
+ })).not.toThrow();
101
+ });
102
+ it("thread 缺失但只给 hint 时不报错(hint 不依赖 thread 结构)", () => {
103
+ const out = applyContentReviewFlags({ draft: "x" }, { authorRelationHint: "other" });
104
+ expect(out["authorRelationHint"]).toBe("other");
105
+ });
106
+ });
107
+ describe("parseAuthorRelationHint", () => {
108
+ it("接受两个精确枚举值", () => {
109
+ expect(parseAuthorRelationHint("post_author")).toBe("post_author");
110
+ expect(parseAuthorRelationHint("other")).toBe("other");
111
+ });
112
+ it.each(["POST_AUTHOR", "Post_Author", " post_author", "op", "unknown", ""])("拒绝 %o(不猜大小写/空白/别名)", (raw) => {
113
+ expect(() => parseAuthorRelationHint(raw)).toThrow(ContentReviewArgError);
114
+ });
115
+ it("unknown 不是可声明的 hint(它是派生结果)", () => {
116
+ expect(() => parseAuthorRelationHint("unknown")).toThrow(/post_author \| other/);
117
+ });
118
+ });
119
+ describe("AUTHOR_RELATION_MISMATCH_HINT", () => {
120
+ it("说明是硬失败、不建议重试,且不诱导换 permalink(anchor shopping)", () => {
121
+ expect(AUTHOR_RELATION_MISMATCH_HINT).toContain("硬失败");
122
+ expect(AUTHOR_RELATION_MISMATCH_HINT).toContain("重试不会自愈");
123
+ expect(AUTHOR_RELATION_MISMATCH_HINT).toMatch(/换一个能通过校验的 permalink 更不行/);
124
+ });
125
+ });
126
+ //# sourceMappingURL=content-review-args.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content-review-args.test.js","sourceRoot":"","sources":["../../src/lib/content-review-args.test.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"permissions-gates-admin.d.ts","sourceRoot":"","sources":["../src/permissions-gates-admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,uDAAuD;AACvD,eAAO,MAAM,sBAAsB,EAAE,iBAAiB,EAqIrD,CAAC"}
1
+ {"version":3,"file":"permissions-gates-admin.d.ts","sourceRoot":"","sources":["../src/permissions-gates-admin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,uDAAuD;AACvD,eAAO,MAAM,sBAAsB,EAAE,iBAAiB,EA8IrD,CAAC"}
@@ -118,11 +118,20 @@ export const ADMIN_PERMISSION_GATES = [
118
118
  resource: "apiKey",
119
119
  action: "update",
120
120
  },
121
+ {
122
+ // 读矩阵也是 admin only —— 端点返回的是**全角色 × 全资源 × 全动作**矩阵 +
123
+ // 字段可见性矩阵(治理配置全貌,不是「我有什么权限」的自查面)。登记它是为了让
124
+ // `auth explain` 本地就说明白,而不是让人打过去吃一个 403。
125
+ command: "permissions matrix",
126
+ resource: "permissionMatrix",
127
+ action: "read",
128
+ note: "admin only;且只认不受限的交互式登录(拒 api_key / 服务 token / 受限 CLI token)",
129
+ },
121
130
  {
122
131
  command: "permissions-update",
123
132
  resource: "permissionMatrix",
124
133
  action: "update",
125
- note: "admin only",
134
+ note: "admin only;且只认不受限的交互式登录(拒 api_key / 服务 token / 受限 CLI token)",
126
135
  },
127
136
  {
128
137
  // 🔴 **update 不是笔误**:能看见「谁在何时改过哪一格权限」是治理信息
@@ -1 +1 @@
1
- {"version":3,"file":"permissions-gates-admin.js","sourceRoot":"","sources":["../src/permissions-gates-admin.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"permissions-gates-admin.js","sourceRoot":"","sources":["../src/permissions-gates-admin.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,17 @@ describe("admin permission gates", () => {
9
9
  action: "update",
10
10
  }));
11
11
  });
12
+ // 读矩阵此前**没有登记** —— `auth explain` 于是不会提示它是 admin only,只能等
13
+ // 服务端 403。登记后本地预检就能说明白。
14
+ it("maps `permissions matrix` to permissionMatrix:read (admin only)", () => {
15
+ const gate = findGateForCommand("permissions matrix");
16
+ expect(gate).toEqual(expect.objectContaining({
17
+ resource: "permissionMatrix",
18
+ action: "read",
19
+ }));
20
+ // 读写两条 gate 的 action 必须不同:read/update 的不对称是有意设计。
21
+ expect(findGateForCommand("permissions-update")?.action).toBe("update");
22
+ });
12
23
  it("gates `permissions revisions` on permissionMatrix:UPDATE (not read)", () => {
13
24
  // 回归护栏:能看见「谁在何时把哪一格从 deny 改成 allow」是治理信息(还露出 actor
14
25
  // user id),门槛必须等同「能改权限」。降成 read 会让矩阵只读角色拿到轨迹。
@@ -1 +1 @@
1
- {"version":3,"file":"permissions-gates-admin.test.js","sourceRoot":"","sources":["../src/permissions-gates-admin.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE5E,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,IAAI,GAAG,kBAAkB,CAAC,oBAAoB,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAClB,MAAM,CAAC,gBAAgB,CAAC;YACtB,QAAQ,EAAE,kBAAkB;YAC5B,MAAM,EAAE,QAAQ;SACjB,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,mDAAmD;QACnD,8CAA8C;QAC9C,MAAM,IAAI,GAAG,kBAAkB,CAAC,uBAAuB,CAAC,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAClB,MAAM,CAAC,gBAAgB,CAAC;YACtB,QAAQ,EAAE,kBAAkB;YAC5B,MAAM,EAAE,QAAQ;SACjB,CAAC,CACH,CAAC;QACF,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,QAAQ,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC9D,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC7C,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAChD,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACjD,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACjD,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,YAAY,GAAG,oBAAoB,CAAC,MAAM,CAC9C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,gBAAgB,CACtC,CAAC;QACF,MAAM,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
1
+ {"version":3,"file":"permissions-gates-admin.test.js","sourceRoot":"","sources":["../src/permissions-gates-admin.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,sBAAsB,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAE5E,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;IACtC,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,IAAI,GAAG,kBAAkB,CAAC,oBAAoB,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAClB,MAAM,CAAC,gBAAgB,CAAC;YACtB,QAAQ,EAAE,kBAAkB;YAC5B,MAAM,EAAE,QAAQ;SACjB,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,0DAA0D;IAC1D,wBAAwB;IACxB,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,IAAI,GAAG,kBAAkB,CAAC,oBAAoB,CAAC,CAAC;QACtD,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAClB,MAAM,CAAC,gBAAgB,CAAC;YACtB,QAAQ,EAAE,kBAAkB;YAC5B,MAAM,EAAE,MAAM;SACf,CAAC,CACH,CAAC;QACF,iDAAiD;QACjD,MAAM,CAAC,kBAAkB,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC1E,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;QAC7E,mDAAmD;QACnD,8CAA8C;QAC9C,MAAM,IAAI,GAAG,kBAAkB,CAAC,uBAAuB,CAAC,CAAC;QACzD,MAAM,CAAC,IAAI,CAAC,CAAC,OAAO,CAClB,MAAM,CAAC,gBAAgB,CAAC;YACtB,QAAQ,EAAE,kBAAkB;YAC5B,MAAM,EAAE,QAAQ;SACjB,CAAC,CACH,CAAC;QACF,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC7D,MAAM,QAAQ,GAAG,sBAAsB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;QAC9D,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,gBAAgB,CAAC,CAAC;QAC7C,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;QAChD,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACjD,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,oBAAoB,CAAC,CAAC;QACjD,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,6BAA6B,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,iEAAiE,EAAE,GAAG,EAAE;QACzE,MAAM,YAAY,GAAG,oBAAoB,CAAC,MAAM,CAC9C,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,gBAAgB,CACtC,CAAC;QACF,MAAM,CAAC,YAAY,CAAC,CAAC,YAAY,CAAC,CAAC,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.12",
3
+ "version": "0.3.13",
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.12",
26
- "@geoly-ai/social-hub-sdk": "0.0.51"
25
+ "@geoly-ai/social-hub-authz": "0.0.13",
26
+ "@geoly-ai/social-hub-sdk": "0.0.52"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@types/node": "^22.10.2",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 1,
3
3
  "package": "social-ops-hub",
4
- "cliVersion": "0.3.12",
4
+ "cliVersion": "0.3.13",
5
5
  "repository": "social-ops-hub",
6
6
  "publish": {
7
7
  "registry": "https://skill.sh",
@@ -6,7 +6,7 @@ description: >-
6
6
  六维评审(情境锚定/突发性/AI 腔/人设声音/合规红线/自我重复)并可选优化重写。
7
7
  不用于:相关性判定(该不该评这帖)、发帖正文审查(用 social-hub-post-review)、账号级风控闸门。
8
8
  metadata:
9
- cliVersion: ">=0.2.3"
9
+ cliVersion: ">=0.3.13"
10
10
  ---
11
11
 
12
12
  # social-hub-content-review
@@ -51,6 +51,61 @@ handle;已选账号时传真实值。AccountBound 的人设
51
51
  (styleGuide/toneTags/brandFit/avoidTopics)由 Hub 按 accountId 服务端
52
52
  enrich,**不要**在请求里传人设内容;`personaId` 传了只做一致性校验。
53
53
 
54
+ ## 作者关系轴(authorRelation)——服务端派生,调用方声明不了
55
+
56
+ `commentType` 表达**结构位置**(`top_level` / `reply`);`authorRelation` 表达
57
+ **说话人是谁**(`post_author` / `other` / `unknown`)。两者正交,都要读。
58
+
59
+ ### 权威来源
60
+
61
+ `authorRelation` 与 `authorRelationEvidence` 只出现在**响应**里,由 Hub 按
62
+ 「实际发布账号 handle × 所审原帖 permalink × Hub 侧帖子快照作者」派生。
63
+ 请求里的 `authorRelationHint` 是**可选的非权威提示**——它不是身份声明的证明,
64
+ 不会被回显为审核结论,**证据永远覆盖 hint**(声称 `other` 但证据显示是作者,
65
+ 仍派生 `post_author`)。
66
+
67
+ ```bash
68
+ # permalink 是作者关系轴的唯一校验入口;hint 可省略(省略不影响评分标准)
69
+ social-hub content review -t <team-id> \
70
+ --thread-permalink "https://www.reddit.com/r/buildapc/comments/<id>/<slug>/" \
71
+ -j '{ "accountId": "<真实账号>", "draft": "...", "commentType": "top_level",
72
+ "thread": { "subreddit": "buildapc", "postTitle": "..." } }'
73
+ ```
74
+
75
+ `--author-relation-hint post_author|other` **通常省略**。它不是让 Hub 采用某种身份的开关:
76
+ 省略不影响评分,也不影响 Hub 按证据派生作者关系。只有当上游工作流必须核验
77
+ 「本账号应当就是原帖作者」这一预期时才传 `post_author`——它的作用是让**已验证的账号错配
78
+ 变成硬失败**(422),而不是争取某种待遇。证据不可得时不会 422,但会按 `unknown` 审核,
79
+ 并在服务端记录一条未核验声明。传 `other` 与证据冲突不会硬失败,证据直接覆盖它。
80
+
81
+ ### 证据条件(只给 hint 没有用)
82
+
83
+ 只有**发布账号、原帖 permalink、Hub 侧该帖快照、以及快照的 subreddit+title 与本次
84
+ thread 绑定**全部一致时,Hub 才可能派生 `post_author` / `verified_snapshot`。
85
+ 只传 hint、或只传 permalink 而 Hub 没有该帖快照,都拿不到这个结果。
86
+ 不得改用别的帖子、猜测账号或替代 permalink 去凑一个能验证的组合。
87
+
88
+ ### 冲突与不可得
89
+
90
+ | 情况 | 结果 |
91
+ | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
92
+ | 显式 `authorRelationHint: post_author`,但证据显示不是该帖作者 | **422 `AUTHOR_RELATION_MISMATCH` 硬失败**:LLM 一次都不调用,服务端落安全审计。不是降级,重试与更换 permalink 都不会自愈;核对发布账号与 permalink,或直接去掉 hint 重跑 |
93
+ | 无 permalink / 无快照 / 作者 `[deleted]` / 无账号 / 绑定校验不过 | `unknown` + `unavailable`:**按普通基线审核,不 block、不惩罚** |
94
+
95
+ `unknown` 不是「证据不足所以从严」,它就是改动前的普通基线。
96
+
97
+ ### 评分边界(这是校准,不是加分项)
98
+
99
+ `post_author` **不加分**:rubric 只扩展了「哪些具体进展可以充当有效线程锚点」
100
+ (从原帖开始的进展/实验/结果/失败尝试),**权重、阈值、证据强度一律未变**,
101
+ compliance / 真实性 / 具体性 / 重复度要求一律不放宽。
102
+ `Update in case anyone lands here later` 之类开头被识别为**中性 discourse marker**——
103
+ 不因它扣 scripted,**也不因它加分**;只评估其后的具体材料,空泛的 update 照样可能
104
+ 撞情境锚定硬红线被 `block`。
105
+
106
+ > **agent 行为规则**:不要为了改变评分而添加 hint、permalink 或 update 开头。
107
+ > 只传本次真实线程里已经存在、且可核对的事实。
108
+
54
109
  ## 两种审核模式
55
110
 
56
111
  | 模式 | 请求 | 结论边界 |
@@ -154,6 +209,8 @@ social-hub comment-drafts schedule -t <team-id> --draft <uuid> \
154
209
  - `executionStatus: "degraded"`(LLM 超时/失败) → verdict 不可信,降级层②自审,
155
210
  回执标 `[hub-offline-fallback]`;`degradedReason` 说明原因。
156
211
  - `score` 为服务端按版本化权重计算的权威综合分;`modelScore` 仅审计对照。
212
+ - `authorRelation` / `authorRelationEvidence` 是**服务端派生**的作者关系(见上一节),
213
+ 记录时按响应原值记,**不要记录或回显你传的 `authorRelationHint`**。
157
214
  - 情境锚定与合规是一票否决维度;静默期账号阈值自动收紧 +10。
158
215
  - AccountBound 出现 `brandFitReviewRequired=true` 或阻断型账号反馈时暂停发布。
159
216