@geoly-ai/social-hub-cli 0.2.20 → 0.2.22

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,19 @@ 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.22] - 2026-07-23
8
+
9
+ ### Added
10
+
11
+ - Trigger P1-D3 —— 四眼 registry 管理面 CLI 命令(social-hub)
12
+
13
+ ## [0.2.21] - 2026-07-22
14
+
15
+ ### Added
16
+
17
+ - 支持受限主体 + 维度派发前鉴权(板块情报收尾 B)
18
+ - 评论回灌直接带分析标签 + 正文编辑标签失效(板块情报收尾 A)
19
+
7
20
  ## [0.2.20] - 2026-07-22
8
21
 
9
22
  ### Added
@@ -1,6 +1,6 @@
1
1
  {
2
- "generatedAt": "2026-07-22T10:57:09.278Z",
3
- "cliVersion": "0.2.20",
2
+ "generatedAt": "2026-07-23T02:34:54.974Z",
3
+ "cliVersion": "0.2.22",
4
4
  "commands": [
5
5
  "accounts",
6
6
  "accounts brand-bindings",
@@ -67,6 +67,24 @@
67
67
  "brand-fit",
68
68
  "brand-fit migrate",
69
69
  "brand-fit show",
70
+ "brand-fit-registry",
71
+ "brand-fit-registry avoid-contexts",
72
+ "brand-fit-registry avoid-contexts list",
73
+ "brand-fit-registry avoid-contexts show",
74
+ "brand-fit-registry proposals",
75
+ "brand-fit-registry proposals apply",
76
+ "brand-fit-registry proposals approve",
77
+ "brand-fit-registry proposals cancel",
78
+ "brand-fit-registry proposals list",
79
+ "brand-fit-registry proposals propose",
80
+ "brand-fit-registry proposals reject",
81
+ "brand-fit-registry proposals show",
82
+ "brand-fit-registry rule-signals",
83
+ "brand-fit-registry rule-signals list",
84
+ "brand-fit-registry rule-signals show",
85
+ "brand-fit-registry triggers",
86
+ "brand-fit-registry triggers list",
87
+ "brand-fit-registry triggers show",
70
88
  "brand-members",
71
89
  "brand-members create",
72
90
  "brand-members list",
package/dist/index.d.ts CHANGED
@@ -4,9 +4,6 @@ declare const program: Command;
4
4
  /** 返回放错位置字段的指引(无则 null)。用于 accounts update / sync core 防呆。 */
5
5
  export declare function detectMisplacedAccountFields(body: Record<string, unknown>): string | null;
6
6
  export type VocAcquireClient = {
7
- getAuthContext(): Promise<{
8
- role: string;
9
- }>;
10
7
  vocQuery(body: unknown): Promise<Record<string, unknown>>;
11
8
  getIntelRunProgress(runId: string): Promise<{
12
9
  status: string;
@@ -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;AAiqD9B,8DAA8D;AAC9D,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,IAAI,CAOf;AAo+BD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC5C,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE,CAAC;AAQF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE;IACN,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,GACA,OAAO,CAAC;IAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CA6JnE;AA0pED,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;AAiqD9B,8DAA8D;AAC9D,wBAAgB,4BAA4B,CAC1C,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,GAAG,IAAI,CAOf;AAs+BD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC1D,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE,CAAC;AAQF,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,gBAAgB,EACxB,MAAM,EAAE;IACN,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACvC,GACA,OAAO,CAAC;IAAE,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAkJnE;AA+1ED,OAAO,EAAE,OAAO,EAAE,CAAC"}
package/dist/index.js CHANGED
@@ -2083,17 +2083,8 @@ export async function orchestrateVocAcquire(client, params) {
2083
2083
  },
2084
2084
  });
2085
2085
  try {
2086
- // 派发前 unrestricted 预检:client 恒拒(第一版;在产生任何副作用前)。
2087
- const auth = await withDeadline(() => client.getAuthContext());
2088
- if (auth.role === "client") {
2089
- return {
2090
- exitCode: 1,
2091
- output: {
2092
- status: "forbidden",
2093
- reason: "voc-acquire 第一版仅 unrestricted 角色(client 恒拒);先派发再发现越权会白占额度",
2094
- },
2095
- };
2096
- }
2086
+ // 无角色预检:权限由服务端强制。voc-query(第一次调用)若无 evidence create 会在**任何派发副作用前**
2087
+ // 403 抛出 → 命令层退 1;有权则派发 + 轮询(受限主体持 runId + dimension read 可轮询 /runs/:runId)
2097
2088
  let round = 0;
2098
2089
  let last = {};
2099
2090
  while (round < params.maxRounds) {
@@ -2203,7 +2194,7 @@ intel
2203
2194
  .command("voc-acquire")
2204
2195
  .description("VOC 有界编排(Hub-native):循环 voc-query --dispatch-on-gap → 轮询 run 到终态 → 重算 coverage 验收 → " +
2205
2196
  "缺口再派一轮。退出码 0=coverage 满足 / 2=尚未满足(超时/多轮后仍缺口,可能需外部 import)/ 1=错误。" +
2206
- "评估口径同 voc-query;第一版仅 unrestricted 角色;--async 只派发不等待。")
2197
+ "评估口径同 voc-query;受限主体亦可(权限由服务端强制:需 evidence create + 该 dimension read);--async 只派发不等待。")
2207
2198
  .option("--brand-keywords <list>", "品牌词,逗号分隔")
2208
2199
  .option("--competitor-keywords <list>", "竞品词,逗号分隔")
2209
2200
  .option("--subreddits <list>", "板块,逗号分隔")
@@ -3783,6 +3774,178 @@ brandRegistry
3783
3774
  cursor: opts.cursor,
3784
3775
  }), null, 2));
3785
3776
  });
3777
+ // ── Trigger 注册表四眼管理面(P1-D3)——只提案/审批/执行,不提供直写命令 ──────────────
3778
+ const bfRegistry = program
3779
+ .command("brand-fit-registry")
3780
+ .description("Trigger 注册表四眼管理面:trigger/avoid/rule_signal 的 create/update/scope/mandate/retire 全走 propose→approve→apply(无直写命令)");
3781
+ const out = (v) => console.log(JSON.stringify(v, null, 2));
3782
+ /** apply 的 stale 友好提示(不解析 message,靠 SDK RegistryProposalSdkError.isStale)。 */
3783
+ function handleProposalError(err) {
3784
+ const e = err;
3785
+ if (e && e.isStale) {
3786
+ console.error(`proposal 已过时(staleCode=${e.staleCode}):目标已变更,请 show 最新状态后重新 propose`);
3787
+ }
3788
+ else if (e && e.code) {
3789
+ console.error(`${e.code}: ${e.message ?? ""}`);
3790
+ }
3791
+ else {
3792
+ console.error(e?.message ?? String(err));
3793
+ }
3794
+ process.exit(1);
3795
+ }
3796
+ const bfrProposals = bfRegistry
3797
+ .command("proposals")
3798
+ .description("四眼提案(propose/approve/reject/cancel/apply/list/show)");
3799
+ bfrProposals
3800
+ .command("propose")
3801
+ .description("提交一个 registry 变更提案(RegistryProposalInput)")
3802
+ .requiredOption("--input <json>", 'RegistryProposalInput JSON,如 {"type":"trigger.create","triggerKey":"...","matchDefinition":{...},"scopeMode":"global"}')
3803
+ .requiredOption("--idempotency-key <key>", "幂等键(同 proposer 同 key 返既有)")
3804
+ .option("--expires-at <iso>", "过期时间 ISO(可选,须晚于当前)")
3805
+ .action(async (opts) => {
3806
+ let input;
3807
+ try {
3808
+ input = JSON.parse(opts.input);
3809
+ }
3810
+ catch {
3811
+ console.error("Invalid --input JSON");
3812
+ process.exit(1);
3813
+ }
3814
+ try {
3815
+ out(await requireClient().proposeRegistryChange({
3816
+ input,
3817
+ idempotencyKey: opts.idempotencyKey,
3818
+ expiresAt: opts.expiresAt,
3819
+ }));
3820
+ }
3821
+ catch (err) {
3822
+ handleProposalError(err);
3823
+ }
3824
+ });
3825
+ bfrProposals
3826
+ .command("approve")
3827
+ .description("批准提案(四眼第二只眼;审批人须≠提案人)")
3828
+ .requiredOption("--id <id>", "proposal id")
3829
+ .option("--comment <text>", "审批备注")
3830
+ .action(async (opts) => {
3831
+ try {
3832
+ out(await requireClient().approveRegistryProposal(opts.id, {
3833
+ approvalComment: opts.comment,
3834
+ }));
3835
+ }
3836
+ catch (err) {
3837
+ handleProposalError(err);
3838
+ }
3839
+ });
3840
+ bfrProposals
3841
+ .command("reject")
3842
+ .description("拒绝提案")
3843
+ .requiredOption("--id <id>", "proposal id")
3844
+ .option("--reason <text>", "拒绝原因")
3845
+ .action(async (opts) => {
3846
+ try {
3847
+ out(await requireClient().rejectRegistryProposal(opts.id, {
3848
+ rejectionReason: opts.reason,
3849
+ }));
3850
+ }
3851
+ catch (err) {
3852
+ handleProposalError(err);
3853
+ }
3854
+ });
3855
+ bfrProposals
3856
+ .command("cancel")
3857
+ .description("取消提案(proposed 或 approved;apply 前)")
3858
+ .requiredOption("--id <id>", "proposal id")
3859
+ .action(async (opts) => {
3860
+ try {
3861
+ out(await requireClient().cancelRegistryProposal(opts.id));
3862
+ }
3863
+ catch (err) {
3864
+ handleProposalError(err);
3865
+ }
3866
+ });
3867
+ bfrProposals
3868
+ .command("apply")
3869
+ .description("执行已批准提案(stale→提示重新提案)")
3870
+ .requiredOption("--id <id>", "proposal id")
3871
+ .action(async (opts) => {
3872
+ try {
3873
+ out(await requireClient().applyRegistryProposal(opts.id));
3874
+ }
3875
+ catch (err) {
3876
+ handleProposalError(err);
3877
+ }
3878
+ });
3879
+ bfrProposals
3880
+ .command("list")
3881
+ .description("列出提案(可按 status 过滤)")
3882
+ .option("--status <status>", "proposed|approved|rejected|cancelled|applied|stale")
3883
+ .action(async (opts) => {
3884
+ out(await requireClient().listRegistryProposals({ status: opts.status }));
3885
+ });
3886
+ bfrProposals
3887
+ .command("show")
3888
+ .description("查看单个提案")
3889
+ .requiredOption("--id <id>", "proposal id")
3890
+ .action(async (opts) => {
3891
+ try {
3892
+ out(await requireClient().getRegistryProposal(opts.id));
3893
+ }
3894
+ catch (err) {
3895
+ handleProposalError(err);
3896
+ }
3897
+ });
3898
+ const bfrTriggers = bfRegistry
3899
+ .command("triggers")
3900
+ .description("trigger 注册表(只读;写走 proposals)");
3901
+ bfrTriggers
3902
+ .command("list")
3903
+ .action(async () => out(await requireClient().listBrandFitTriggers()));
3904
+ bfrTriggers
3905
+ .command("show")
3906
+ .requiredOption("--key <triggerKey>")
3907
+ .action(async (opts) => {
3908
+ try {
3909
+ out(await requireClient().getBrandFitTrigger(opts.key));
3910
+ }
3911
+ catch (err) {
3912
+ handleProposalError(err);
3913
+ }
3914
+ });
3915
+ const bfrAvoids = bfRegistry
3916
+ .command("avoid-contexts")
3917
+ .description("avoid_context 注册表(只读;写走 proposals)");
3918
+ bfrAvoids
3919
+ .command("list")
3920
+ .action(async () => out(await requireClient().listBrandFitAvoidContexts()));
3921
+ bfrAvoids
3922
+ .command("show")
3923
+ .requiredOption("--key <avoidKey>")
3924
+ .action(async (opts) => {
3925
+ try {
3926
+ out(await requireClient().getBrandFitAvoidContext(opts.key));
3927
+ }
3928
+ catch (err) {
3929
+ handleProposalError(err);
3930
+ }
3931
+ });
3932
+ const bfrSignals = bfRegistry
3933
+ .command("rule-signals")
3934
+ .description("rule_signal 注册表(只读;写走 proposals)");
3935
+ bfrSignals
3936
+ .command("list")
3937
+ .action(async () => out(await requireClient().listBrandFitRuleSignals()));
3938
+ bfrSignals
3939
+ .command("show")
3940
+ .requiredOption("--key <signalKey>")
3941
+ .action(async (opts) => {
3942
+ try {
3943
+ out(await requireClient().getBrandFitRuleSignal(opts.key));
3944
+ }
3945
+ catch (err) {
3946
+ handleProposalError(err);
3947
+ }
3948
+ });
3786
3949
  const brandFit = program
3787
3950
  .command("brand-fit")
3788
3951
  .description("Persona Brand Fit 分级(persona×brand 正式关系):关系表查看 + 受控迁移 validate/apply");