@geoly-ai/social-hub-cli 0.0.12 → 0.0.14
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 +16 -0
- package/dist/admin-index-gates.test.d.ts +2 -0
- package/dist/admin-index-gates.test.d.ts.map +1 -0
- package/dist/admin-index-gates.test.js +33 -0
- package/dist/admin-index-gates.test.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +810 -63
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +87 -5
- package/dist/index.test.js.map +1 -1
- package/dist/permission-runner.d.ts +11 -0
- package/dist/permission-runner.d.ts.map +1 -0
- package/dist/permission-runner.js +30 -0
- package/dist/permission-runner.js.map +1 -0
- package/dist/permission-runner.test.d.ts +2 -0
- package/dist/permission-runner.test.d.ts.map +1 -0
- package/dist/permission-runner.test.js +69 -0
- package/dist/permission-runner.test.js.map +1 -0
- package/dist/permissions-gates-admin.d.ts +4 -0
- package/dist/permissions-gates-admin.d.ts.map +1 -0
- package/dist/permissions-gates-admin.js +80 -0
- package/dist/permissions-gates-admin.js.map +1 -0
- package/dist/permissions-gates-admin.test.d.ts +2 -0
- package/dist/permissions-gates-admin.test.d.ts.map +1 -0
- package/dist/permissions-gates-admin.test.js +25 -0
- package/dist/permissions-gates-admin.test.js.map +1 -0
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +2 -3
- package/dist/permissions.js.map +1 -1
- package/dist/register-admin.d.ts.map +1 -1
- package/dist/register-admin.js +343 -29
- package/dist/register-admin.js.map +1 -1
- package/dist/register-extensions.d.ts.map +1 -1
- package/dist/register-extensions.js +26 -19
- package/dist/register-extensions.js.map +1 -1
- package/dist/register-shared.js +1 -1
- package/dist/register-shared.js.map +1 -1
- package/package.json +2 -2
- package/skills/README.md +7 -5
- package/skills/manifest.json +17 -7
- package/skills/social-hub-accounts/SKILL.md +46 -13
- package/skills/social-hub-admin/SKILL.md +76 -10
- package/skills/social-hub-calendar-jobs/SKILL.md +26 -10
- package/skills/social-hub-cli/SKILL.md +35 -191
- package/skills/social-hub-cli/evals/evals.json +4 -4
- package/skills/social-hub-events-observability/SKILL.md +50 -0
- package/skills/social-hub-graph-compliance/SKILL.md +60 -0
- package/skills/social-hub-intelligence/SKILL.md +72 -7
- package/skills/social-hub-migration/SKILL.md +18 -5
- package/skills/social-hub-openclaw-context/SKILL.md +23 -8
- package/skills/social-hub-ops-runtime/SKILL.md +10 -5
- package/skills/social-hub-posts/SKILL.md +56 -23
- package/skills/social-hub-posts/evals/evals.json +23 -0
- package/skills/social-hub-publishing/SKILL.md +75 -11
- package/skills/social-hub-shared/SKILL.md +8 -4
package/dist/index.js
CHANGED
|
@@ -4,6 +4,8 @@ import { getSocialHubHealth } from "@geoly-ai/social-hub-sdk";
|
|
|
4
4
|
import { getBaseUrl, requireClient } from "./client.js";
|
|
5
5
|
import { registerAccountsExtensions, registerAuthAndConfig, registerPermissionsExplain, registerRedditExtensions, } from "./register-extensions.js";
|
|
6
6
|
import { registerAdminCommands } from "./register-admin.js";
|
|
7
|
+
import { assertApplyOrDryRun } from "./dry-run.js";
|
|
8
|
+
import { withPermissionGate } from "./permission-runner.js";
|
|
7
9
|
import { registerBatchAndExport } from "./register-batch.js";
|
|
8
10
|
import { registerAgentContext, registerOpsRuntime, } from "./register-ops.js";
|
|
9
11
|
import { registerDoctorAndVersion } from "./register-shared.js";
|
|
@@ -44,6 +46,12 @@ events
|
|
|
44
46
|
.command("append")
|
|
45
47
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
46
48
|
.requiredOption("--type <type>", "事件类型,如 comment")
|
|
49
|
+
.option("--external-ref <ref>", "幂等键(也可放在 payload.externalRef / event_id)")
|
|
50
|
+
.option("--account <uuid>", "socialAccountId")
|
|
51
|
+
.option("--subreddit <name>", "subreddit")
|
|
52
|
+
.option("--result <result>", "succeeded | failed | skipped")
|
|
53
|
+
.option("--target-url <url>", "目标 URL / permalink")
|
|
54
|
+
.option("--actor <actor>", "user | agent | cron")
|
|
47
55
|
.option("--payload <json>", "JSON 对象", "{}")
|
|
48
56
|
.action(async (opts) => {
|
|
49
57
|
let payload = {};
|
|
@@ -56,6 +64,30 @@ events
|
|
|
56
64
|
}
|
|
57
65
|
const res = await requireClient().appendEvent(opts.team, {
|
|
58
66
|
type: opts.type,
|
|
67
|
+
externalRef: opts.externalRef ??
|
|
68
|
+
(typeof payload.externalRef === "string" ? payload.externalRef : undefined) ??
|
|
69
|
+
(typeof payload.event_id === "string" ? payload.event_id : undefined),
|
|
70
|
+
socialAccountId: opts.account ??
|
|
71
|
+
(typeof payload.socialAccountId === "string"
|
|
72
|
+
? payload.socialAccountId
|
|
73
|
+
: undefined),
|
|
74
|
+
subreddit: opts.subreddit ??
|
|
75
|
+
(typeof payload.subreddit === "string" ? payload.subreddit : undefined),
|
|
76
|
+
result: opts.result ??
|
|
77
|
+
(payload.result === "succeeded" ||
|
|
78
|
+
payload.result === "failed" ||
|
|
79
|
+
payload.result === "skipped"
|
|
80
|
+
? payload.result
|
|
81
|
+
: undefined),
|
|
82
|
+
targetUrl: opts.targetUrl ??
|
|
83
|
+
(typeof payload.targetUrl === "string" ? payload.targetUrl : undefined) ??
|
|
84
|
+
(typeof payload.permalink === "string" ? payload.permalink : undefined),
|
|
85
|
+
actor: opts.actor ??
|
|
86
|
+
(payload.actor === "user" ||
|
|
87
|
+
payload.actor === "agent" ||
|
|
88
|
+
payload.actor === "cron"
|
|
89
|
+
? payload.actor
|
|
90
|
+
: undefined),
|
|
59
91
|
payload,
|
|
60
92
|
});
|
|
61
93
|
console.log(JSON.stringify(res, null, 2));
|
|
@@ -85,6 +117,102 @@ events
|
|
|
85
117
|
const res = await requireClient().appendEventsBatch(opts.team, body);
|
|
86
118
|
console.log(JSON.stringify(res, null, 2));
|
|
87
119
|
});
|
|
120
|
+
events
|
|
121
|
+
.command("import-openclaw-history")
|
|
122
|
+
.description("POST /interaction-history/import — OpenClaw v1 JSON 幂等入库")
|
|
123
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
124
|
+
.requiredOption("-f, --file <path>", "interaction-history.json 路径")
|
|
125
|
+
.option("--dry-run", "只校验 schema/slot/幂等,不写库")
|
|
126
|
+
.option("--no-auto-mark", "关闭 auto_metrics 阈值补 mark")
|
|
127
|
+
.action(async (opts) => {
|
|
128
|
+
const fs = await import("node:fs/promises");
|
|
129
|
+
const raw = await fs.readFile(opts.file, "utf8");
|
|
130
|
+
let parsed;
|
|
131
|
+
try {
|
|
132
|
+
parsed = JSON.parse(raw);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
console.error("Invalid JSON file");
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
const res = await requireClient().importInteractionHistory(opts.team, {
|
|
139
|
+
...parsed,
|
|
140
|
+
sourceFile: opts.file,
|
|
141
|
+
dryRun: Boolean(opts.dryRun),
|
|
142
|
+
autoMarkThreshold: !opts.noAutoMark,
|
|
143
|
+
});
|
|
144
|
+
console.log(JSON.stringify(res, null, 2));
|
|
145
|
+
});
|
|
146
|
+
const styleMarks = program.command("style-marks").description("风格反馈标记");
|
|
147
|
+
styleMarks
|
|
148
|
+
.command("list")
|
|
149
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
150
|
+
.option("--account <uuid>", "socialAccountId")
|
|
151
|
+
.option("--extract-status <st>", "pending | extracted | rejected | skipped | failed")
|
|
152
|
+
.option("--approved <bool>", "true | false")
|
|
153
|
+
.option("-n, --limit <n>", "limit", "50")
|
|
154
|
+
.option("--offset <n>", "offset", "0")
|
|
155
|
+
.action(async (opts) => {
|
|
156
|
+
const res = await requireClient().listStyleMarks(opts.team, {
|
|
157
|
+
socialAccountId: opts.account,
|
|
158
|
+
extractStatus: opts.extractStatus,
|
|
159
|
+
approved: opts.approved === undefined ? undefined : opts.approved === "true",
|
|
160
|
+
limit: Number(opts.limit),
|
|
161
|
+
offset: Number(opts.offset),
|
|
162
|
+
});
|
|
163
|
+
console.log(JSON.stringify(res, null, 2));
|
|
164
|
+
});
|
|
165
|
+
styleMarks
|
|
166
|
+
.command("approve")
|
|
167
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
168
|
+
.requiredOption("--id <styleMarkId>", "Style mark UUID")
|
|
169
|
+
.option("--note <text>", "Reviewer note")
|
|
170
|
+
.action(async (opts) => {
|
|
171
|
+
const res = await requireClient().patchStyleMark(opts.team, opts.id, {
|
|
172
|
+
approved: true,
|
|
173
|
+
extractStatus: "pending",
|
|
174
|
+
note: opts.note,
|
|
175
|
+
});
|
|
176
|
+
console.log(JSON.stringify(res, null, 2));
|
|
177
|
+
});
|
|
178
|
+
styleMarks
|
|
179
|
+
.command("reject")
|
|
180
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
181
|
+
.requiredOption("--id <styleMarkId>", "Style mark UUID")
|
|
182
|
+
.option("--note <text>", "Reviewer note")
|
|
183
|
+
.action(async (opts) => {
|
|
184
|
+
const res = await requireClient().patchStyleMark(opts.team, opts.id, {
|
|
185
|
+
approved: false,
|
|
186
|
+
extractStatus: "rejected",
|
|
187
|
+
note: opts.note,
|
|
188
|
+
});
|
|
189
|
+
console.log(JSON.stringify(res, null, 2));
|
|
190
|
+
});
|
|
191
|
+
styleMarks
|
|
192
|
+
.command("extract")
|
|
193
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
194
|
+
.requiredOption("--id <styleMarkId>", "Style mark UUID")
|
|
195
|
+
.option("--dry-run", "只预览提取结果")
|
|
196
|
+
.action(async (opts) => {
|
|
197
|
+
const res = await requireClient().extractStyleMark(opts.team, opts.id, {
|
|
198
|
+
dryRun: Boolean(opts.dryRun),
|
|
199
|
+
});
|
|
200
|
+
console.log(JSON.stringify(res, null, 2));
|
|
201
|
+
});
|
|
202
|
+
const curator = program.command("curator").description("风格沉淀 curator");
|
|
203
|
+
curator
|
|
204
|
+
.command("run")
|
|
205
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
206
|
+
.requiredOption("--account <ref>", "账号 UUID 或 handle/slot")
|
|
207
|
+
.option("--dry-run", "只预览")
|
|
208
|
+
.option("-n, --limit <n>", "最多处理 pending marks", "50")
|
|
209
|
+
.action(async (opts) => {
|
|
210
|
+
const res = await requireClient().runStyleCurator(opts.team, opts.account, {
|
|
211
|
+
dryRun: Boolean(opts.dryRun),
|
|
212
|
+
limit: Number(opts.limit),
|
|
213
|
+
});
|
|
214
|
+
console.log(JSON.stringify(res, null, 2));
|
|
215
|
+
});
|
|
88
216
|
// --- dashboard ---
|
|
89
217
|
const dashboard = program.command("dashboard").description("运营总览");
|
|
90
218
|
dashboard
|
|
@@ -604,6 +732,28 @@ compliance
|
|
|
604
732
|
const res = await requireClient().upsertAccountRiskProfile(opts.team, opts.account, body);
|
|
605
733
|
console.log(JSON.stringify(res, null, 2));
|
|
606
734
|
});
|
|
735
|
+
compliance
|
|
736
|
+
.command("rule-caches-list")
|
|
737
|
+
.description("GET /subreddit-rule-caches")
|
|
738
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
739
|
+
.option("--include-expired", "include expired caches")
|
|
740
|
+
.action(async (opts) => {
|
|
741
|
+
const res = await requireClient().listSubredditRuleCaches(opts.team, {
|
|
742
|
+
includeExpired: Boolean(opts.includeExpired),
|
|
743
|
+
});
|
|
744
|
+
console.log(JSON.stringify(res, null, 2));
|
|
745
|
+
});
|
|
746
|
+
compliance
|
|
747
|
+
.command("rule-caches-upsert")
|
|
748
|
+
.description("PUT /subreddit-rule-caches/:subreddit")
|
|
749
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
750
|
+
.requiredOption("--sub <name>", "subreddit name")
|
|
751
|
+
.requiredOption("-j, --json <json>", "{ rulesMarkdown, sourceUrl?, expiresInDays? }")
|
|
752
|
+
.action(async (opts) => {
|
|
753
|
+
const body = JSON.parse(opts.json);
|
|
754
|
+
const res = await requireClient().upsertSubredditRuleCache(opts.team, opts.sub, body);
|
|
755
|
+
console.log(JSON.stringify(res, null, 2));
|
|
756
|
+
});
|
|
607
757
|
// --- openclaw ---
|
|
608
758
|
program
|
|
609
759
|
.command("openclaw-ingest")
|
|
@@ -716,8 +866,14 @@ agentTeams
|
|
|
716
866
|
.command("list")
|
|
717
867
|
.description("GET /v1/agent-teams")
|
|
718
868
|
.action(async () => {
|
|
719
|
-
|
|
720
|
-
|
|
869
|
+
await withPermissionGate({
|
|
870
|
+
command: "agent-teams list",
|
|
871
|
+
resource: "agentTeam",
|
|
872
|
+
action: "list",
|
|
873
|
+
}, async () => {
|
|
874
|
+
const res = await requireClient().listAgentTeams();
|
|
875
|
+
console.log(JSON.stringify(res, null, 2));
|
|
876
|
+
});
|
|
721
877
|
});
|
|
722
878
|
agentTeams
|
|
723
879
|
.command("workspace-docs")
|
|
@@ -726,30 +882,71 @@ agentTeams
|
|
|
726
882
|
.option("--kind <kind>", "file|daily_report|weekly_report|ops_record")
|
|
727
883
|
.option("-n, --limit <n>", "limit", "50")
|
|
728
884
|
.action(async (opts) => {
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
885
|
+
await withPermissionGate({
|
|
886
|
+
command: "agent-teams workspace-docs",
|
|
887
|
+
resource: "agentTeam",
|
|
888
|
+
action: "read",
|
|
889
|
+
teamId: opts.team,
|
|
890
|
+
}, async () => {
|
|
891
|
+
const res = await requireClient().listWorkspaceDocuments(opts.team, {
|
|
892
|
+
kind: opts.kind,
|
|
893
|
+
limit: Number(opts.limit),
|
|
894
|
+
});
|
|
895
|
+
console.log(JSON.stringify(res, null, 2));
|
|
732
896
|
});
|
|
733
|
-
console.log(JSON.stringify(res, null, 2));
|
|
734
897
|
});
|
|
735
898
|
agentTeams
|
|
736
899
|
.command("create")
|
|
737
900
|
.description("POST /v1/agent-teams — 新建团队(admin only)")
|
|
738
901
|
.requiredOption("--slug <slug>", "团队 slug(小写字母、数字、横线)")
|
|
739
902
|
.requiredOption("--name <name>", "团队名称")
|
|
740
|
-
.
|
|
741
|
-
|
|
742
|
-
|
|
903
|
+
.option("--dry-run", "预览", false)
|
|
904
|
+
.option("--apply", "执行写入", false)
|
|
905
|
+
.action(async (opts) => {
|
|
906
|
+
await withPermissionGate({
|
|
907
|
+
command: "agent-teams create",
|
|
908
|
+
resource: "agentTeam",
|
|
909
|
+
action: "create",
|
|
910
|
+
}, async () => {
|
|
911
|
+
if (!assertApplyOrDryRun(opts, {
|
|
912
|
+
command: "agent-teams create",
|
|
913
|
+
danger: "admin",
|
|
914
|
+
summary: { slug: opts.slug, name: opts.name },
|
|
915
|
+
})) {
|
|
916
|
+
return;
|
|
917
|
+
}
|
|
918
|
+
const res = await requireClient().createTeam({
|
|
919
|
+
slug: opts.slug,
|
|
920
|
+
name: opts.name,
|
|
921
|
+
});
|
|
922
|
+
console.log(JSON.stringify(res, null, 2));
|
|
923
|
+
});
|
|
743
924
|
});
|
|
744
925
|
agentTeams
|
|
745
926
|
.command("update")
|
|
746
927
|
.description("PATCH /v1/agent-teams/:teamId — 更新团队(admin only)")
|
|
747
928
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
748
929
|
.requiredOption("-j, --json <json>", "{ slug?, name? }")
|
|
749
|
-
.
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
930
|
+
.option("--dry-run", "预览", false)
|
|
931
|
+
.option("--apply", "执行写入", false)
|
|
932
|
+
.action(async (opts) => {
|
|
933
|
+
await withPermissionGate({
|
|
934
|
+
command: "agent-teams update",
|
|
935
|
+
resource: "agentTeam",
|
|
936
|
+
action: "update",
|
|
937
|
+
teamId: opts.team,
|
|
938
|
+
}, async () => {
|
|
939
|
+
const body = JSON.parse(opts.json);
|
|
940
|
+
if (!assertApplyOrDryRun(opts, {
|
|
941
|
+
command: "agent-teams update",
|
|
942
|
+
danger: "admin",
|
|
943
|
+
summary: { teamId: opts.team, ...body },
|
|
944
|
+
})) {
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
const res = await requireClient().updateTeam(opts.team, body);
|
|
948
|
+
console.log(JSON.stringify(res, null, 2));
|
|
949
|
+
});
|
|
753
950
|
});
|
|
754
951
|
agentTeams
|
|
755
952
|
.command("add-member")
|
|
@@ -757,12 +954,32 @@ agentTeams
|
|
|
757
954
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
758
955
|
.requiredOption("--user <userId>", "User UUID")
|
|
759
956
|
.requiredOption("--role <role>", "admin|manager|supervisor|senior|internal|client")
|
|
760
|
-
.
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
957
|
+
.option("--dry-run", "预览", false)
|
|
958
|
+
.option("--apply", "执行写入", false)
|
|
959
|
+
.action(async (opts) => {
|
|
960
|
+
await withPermissionGate({
|
|
961
|
+
command: "agent-teams add-member",
|
|
962
|
+
resource: "agentTeam",
|
|
963
|
+
action: "update",
|
|
964
|
+
teamId: opts.team,
|
|
965
|
+
}, async () => {
|
|
966
|
+
if (!assertApplyOrDryRun(opts, {
|
|
967
|
+
command: "agent-teams add-member",
|
|
968
|
+
danger: "admin",
|
|
969
|
+
summary: {
|
|
970
|
+
teamId: opts.team,
|
|
971
|
+
userId: opts.user,
|
|
972
|
+
role: opts.role,
|
|
973
|
+
},
|
|
974
|
+
})) {
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
const res = await requireClient().addTeamMember(opts.team, {
|
|
978
|
+
userId: opts.user,
|
|
979
|
+
role: opts.role,
|
|
980
|
+
});
|
|
981
|
+
console.log(JSON.stringify(res, null, 2));
|
|
764
982
|
});
|
|
765
|
-
console.log(JSON.stringify(res, null, 2));
|
|
766
983
|
});
|
|
767
984
|
// --- intelligence (subreddit-intelligence) ---
|
|
768
985
|
const intel = program.command("intelligence").description("板块情报与 KOL 挖掘");
|
|
@@ -903,6 +1120,192 @@ intel
|
|
|
903
1120
|
const res = await requireClient().dispatchTaskFromHotPost(opts.team, body);
|
|
904
1121
|
console.log(JSON.stringify(res, null, 2));
|
|
905
1122
|
});
|
|
1123
|
+
intel
|
|
1124
|
+
.command("tier-rules-list")
|
|
1125
|
+
.description("GET .../subreddit-intelligence/tier-rules")
|
|
1126
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1127
|
+
.option("--tier <tier>", "tier filter")
|
|
1128
|
+
.option("--enabled <bool>", "true|false")
|
|
1129
|
+
.option("-n, --limit <n>", "limit", "50")
|
|
1130
|
+
.option("--offset <n>", "offset", "0")
|
|
1131
|
+
.action(async (opts) => {
|
|
1132
|
+
const res = await requireClient().listTierRules(opts.team, {
|
|
1133
|
+
tier: opts.tier,
|
|
1134
|
+
enabled: opts.enabled === undefined
|
|
1135
|
+
? undefined
|
|
1136
|
+
: opts.enabled === "true" || opts.enabled === "1",
|
|
1137
|
+
limit: Number(opts.limit),
|
|
1138
|
+
offset: Number(opts.offset),
|
|
1139
|
+
});
|
|
1140
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1141
|
+
});
|
|
1142
|
+
intel
|
|
1143
|
+
.command("tier-rules-create")
|
|
1144
|
+
.description("POST .../subreddit-intelligence/tier-rules")
|
|
1145
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1146
|
+
.requiredOption("-j, --json <json>", "tier rule body")
|
|
1147
|
+
.action(async (opts) => {
|
|
1148
|
+
const body = JSON.parse(opts.json);
|
|
1149
|
+
const res = await requireClient().createTierRule(opts.team, body);
|
|
1150
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1151
|
+
});
|
|
1152
|
+
intel
|
|
1153
|
+
.command("tier-rules-update")
|
|
1154
|
+
.description("PATCH .../subreddit-intelligence/tier-rules/:id")
|
|
1155
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1156
|
+
.requiredOption("--id <uuid>", "Tier rule UUID")
|
|
1157
|
+
.requiredOption("-j, --json <json>", "update body")
|
|
1158
|
+
.action(async (opts) => {
|
|
1159
|
+
const body = JSON.parse(opts.json);
|
|
1160
|
+
const res = await requireClient().updateTierRule(opts.team, opts.id, body);
|
|
1161
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1162
|
+
});
|
|
1163
|
+
intel
|
|
1164
|
+
.command("tier-rules-delete")
|
|
1165
|
+
.description("DELETE .../subreddit-intelligence/tier-rules/:id")
|
|
1166
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1167
|
+
.requiredOption("--id <uuid>", "Tier rule UUID")
|
|
1168
|
+
.action(async (opts) => {
|
|
1169
|
+
const res = await requireClient().deleteTierRule(opts.team, opts.id);
|
|
1170
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1171
|
+
});
|
|
1172
|
+
intel
|
|
1173
|
+
.command("runs-list")
|
|
1174
|
+
.description("GET .../subreddit-intelligence/runs")
|
|
1175
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1176
|
+
.option("--dimension <d>", "industry|keywords|tier")
|
|
1177
|
+
.option("--status <s>", "queued|running|succeeded|failed")
|
|
1178
|
+
.option("-n, --limit <n>", "limit", "50")
|
|
1179
|
+
.option("--offset <n>", "offset", "0")
|
|
1180
|
+
.action(async (opts) => {
|
|
1181
|
+
const res = await requireClient().listIntelRuns(opts.team, {
|
|
1182
|
+
dimension: opts.dimension,
|
|
1183
|
+
status: opts.status,
|
|
1184
|
+
limit: Number(opts.limit),
|
|
1185
|
+
offset: Number(opts.offset),
|
|
1186
|
+
});
|
|
1187
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1188
|
+
});
|
|
1189
|
+
intel
|
|
1190
|
+
.command("kol-profiles-list")
|
|
1191
|
+
.description("GET .../subreddit-intelligence/kol-profiles")
|
|
1192
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1193
|
+
.option("--platform <p>", "reddit")
|
|
1194
|
+
.option("-n, --limit <n>", "limit", "50")
|
|
1195
|
+
.option("--offset <n>", "offset", "0")
|
|
1196
|
+
.action(async (opts) => {
|
|
1197
|
+
const res = await requireClient().listKolProfiles(opts.team, {
|
|
1198
|
+
platform: opts.platform,
|
|
1199
|
+
limit: Number(opts.limit),
|
|
1200
|
+
offset: Number(opts.offset),
|
|
1201
|
+
});
|
|
1202
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1203
|
+
});
|
|
1204
|
+
intel
|
|
1205
|
+
.command("kol-profiles-get")
|
|
1206
|
+
.description("GET .../subreddit-intelligence/kol-profiles/:id")
|
|
1207
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1208
|
+
.requiredOption("--id <uuid>", "KOL profile UUID")
|
|
1209
|
+
.action(async (opts) => {
|
|
1210
|
+
const res = await requireClient().getKolProfile(opts.team, opts.id);
|
|
1211
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1212
|
+
});
|
|
1213
|
+
intel
|
|
1214
|
+
.command("fetch-by-industry")
|
|
1215
|
+
.description("POST .../subreddit-intelligence/hot-posts/fetch-by-industry")
|
|
1216
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1217
|
+
.requiredOption("-j, --json <json>", "fetch body")
|
|
1218
|
+
.action(async (opts) => {
|
|
1219
|
+
const body = JSON.parse(opts.json);
|
|
1220
|
+
const res = await requireClient().fetchHotPostsByIndustry(opts.team, body);
|
|
1221
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1222
|
+
});
|
|
1223
|
+
intel
|
|
1224
|
+
.command("fetch-by-tier")
|
|
1225
|
+
.description("POST .../subreddit-intelligence/hot-posts/fetch-by-tier")
|
|
1226
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1227
|
+
.requiredOption("-j, --json <json>", "fetch body")
|
|
1228
|
+
.action(async (opts) => {
|
|
1229
|
+
const body = JSON.parse(opts.json);
|
|
1230
|
+
const res = await requireClient().fetchHotPostsByTier(opts.team, body);
|
|
1231
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1232
|
+
});
|
|
1233
|
+
intel
|
|
1234
|
+
.command("fetch-by-keywords")
|
|
1235
|
+
.description("POST .../subreddit-intelligence/insights/fetch-by-keywords")
|
|
1236
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1237
|
+
.requiredOption("-j, --json <json>", "fetch body")
|
|
1238
|
+
.action(async (opts) => {
|
|
1239
|
+
const body = JSON.parse(opts.json);
|
|
1240
|
+
const res = await requireClient().fetchInsightsByKeywords(opts.team, body);
|
|
1241
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1242
|
+
});
|
|
1243
|
+
intel
|
|
1244
|
+
.command("brand-mention-radar")
|
|
1245
|
+
.description("POST .../subreddit-intelligence/insights/brand-mention-radar")
|
|
1246
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1247
|
+
.requiredOption("-j, --json <json>", "{ keywords, minScore?, minComments? }")
|
|
1248
|
+
.action(async (opts) => {
|
|
1249
|
+
const body = JSON.parse(opts.json);
|
|
1250
|
+
const res = await requireClient().getBrandMentionRadar(opts.team, body);
|
|
1251
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1252
|
+
});
|
|
1253
|
+
intel
|
|
1254
|
+
.command("opportunity-map")
|
|
1255
|
+
.description("POST .../subreddit-intelligence/insights/opportunity-map")
|
|
1256
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1257
|
+
.requiredOption("-j, --json <json>", "{ keywords, minScore?, minComments? }")
|
|
1258
|
+
.action(async (opts) => {
|
|
1259
|
+
const body = JSON.parse(opts.json);
|
|
1260
|
+
const res = await requireClient().getOpportunityMap(opts.team, body);
|
|
1261
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1262
|
+
});
|
|
1263
|
+
intel
|
|
1264
|
+
.command("sov")
|
|
1265
|
+
.description("POST .../subreddit-intelligence/insights/sov")
|
|
1266
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1267
|
+
.requiredOption("-j, --json <json>", "{ brandKeywords, competitorKeywords, minScore?, minComments? }")
|
|
1268
|
+
.action(async (opts) => {
|
|
1269
|
+
const body = JSON.parse(opts.json);
|
|
1270
|
+
const res = await requireClient().getInsightsSov(opts.team, body);
|
|
1271
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1272
|
+
});
|
|
1273
|
+
intel
|
|
1274
|
+
.command("sentiment-intent")
|
|
1275
|
+
.description("GET .../subreddit-intelligence/insights/sentiment-intent")
|
|
1276
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1277
|
+
.action(async (opts) => {
|
|
1278
|
+
const res = await requireClient().getSentimentIntent(opts.team);
|
|
1279
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1280
|
+
});
|
|
1281
|
+
intel
|
|
1282
|
+
.command("campaign-lift")
|
|
1283
|
+
.description("POST .../subreddit-intelligence/insights/campaign-lift")
|
|
1284
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1285
|
+
.requiredOption("-j, --json <json>", "{ keywords, campaignStartAt, campaignEndAt, windowDays?, minScore?, minComments? }")
|
|
1286
|
+
.action(async (opts) => {
|
|
1287
|
+
const body = JSON.parse(opts.json);
|
|
1288
|
+
const res = await requireClient().getCampaignLift(opts.team, body);
|
|
1289
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1290
|
+
});
|
|
1291
|
+
intel
|
|
1292
|
+
.command("snapshot-latest")
|
|
1293
|
+
.description("GET .../subreddit-intelligence/insights/snapshot-latest")
|
|
1294
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1295
|
+
.action(async (opts) => {
|
|
1296
|
+
const res = await requireClient().getLatestInsightSnapshot(opts.team);
|
|
1297
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1298
|
+
});
|
|
1299
|
+
intel
|
|
1300
|
+
.command("snapshot-save")
|
|
1301
|
+
.description("POST .../subreddit-intelligence/insights/snapshot")
|
|
1302
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1303
|
+
.requiredOption("-j, --json <json>", "{ payload: { ... } }")
|
|
1304
|
+
.action(async (opts) => {
|
|
1305
|
+
const body = JSON.parse(opts.json);
|
|
1306
|
+
const res = await requireClient().saveInsightSnapshot(opts.team, body);
|
|
1307
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1308
|
+
});
|
|
906
1309
|
// --- users ---
|
|
907
1310
|
const usersCmd = program.command("users").description("团队成员管理");
|
|
908
1311
|
usersCmd
|
|
@@ -910,8 +1313,28 @@ usersCmd
|
|
|
910
1313
|
.description("GET /users")
|
|
911
1314
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
912
1315
|
.action(async (opts) => {
|
|
913
|
-
|
|
914
|
-
|
|
1316
|
+
await withPermissionGate({
|
|
1317
|
+
command: "users list",
|
|
1318
|
+
resource: "systemMember",
|
|
1319
|
+
action: "list",
|
|
1320
|
+
teamId: opts.team,
|
|
1321
|
+
}, async () => {
|
|
1322
|
+
const res = await requireClient().listUsers(opts.team);
|
|
1323
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1324
|
+
});
|
|
1325
|
+
});
|
|
1326
|
+
usersCmd
|
|
1327
|
+
.command("system-list")
|
|
1328
|
+
.description("GET /v1/system/users")
|
|
1329
|
+
.action(async () => {
|
|
1330
|
+
await withPermissionGate({
|
|
1331
|
+
command: "users system-list",
|
|
1332
|
+
resource: "systemMember",
|
|
1333
|
+
action: "list",
|
|
1334
|
+
}, async () => {
|
|
1335
|
+
const res = await requireClient().listSystemUsers();
|
|
1336
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1337
|
+
});
|
|
915
1338
|
});
|
|
916
1339
|
usersCmd
|
|
917
1340
|
.command("create")
|
|
@@ -919,9 +1342,40 @@ usersCmd
|
|
|
919
1342
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
920
1343
|
.requiredOption("-j, --json <json>", "{ email, password, role? }")
|
|
921
1344
|
.action(async (opts) => {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1345
|
+
await withPermissionGate({
|
|
1346
|
+
command: "users create",
|
|
1347
|
+
resource: "systemMember",
|
|
1348
|
+
action: "create",
|
|
1349
|
+
teamId: opts.team,
|
|
1350
|
+
}, async () => {
|
|
1351
|
+
const body = JSON.parse(opts.json);
|
|
1352
|
+
const res = await requireClient().createUser(opts.team, body);
|
|
1353
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1354
|
+
});
|
|
1355
|
+
});
|
|
1356
|
+
usersCmd
|
|
1357
|
+
.command("system-create")
|
|
1358
|
+
.description("POST /v1/system/users")
|
|
1359
|
+
.requiredOption("-j, --json <json>", "{ email, name, password?, role? }")
|
|
1360
|
+
.option("--dry-run", "预览", false)
|
|
1361
|
+
.option("--apply", "执行写入", false)
|
|
1362
|
+
.action(async (opts) => {
|
|
1363
|
+
await withPermissionGate({
|
|
1364
|
+
command: "users system-create",
|
|
1365
|
+
resource: "systemMember",
|
|
1366
|
+
action: "create",
|
|
1367
|
+
}, async () => {
|
|
1368
|
+
const body = JSON.parse(opts.json);
|
|
1369
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1370
|
+
command: "users system-create",
|
|
1371
|
+
danger: "write",
|
|
1372
|
+
summary: { email: body.email, name: body.name },
|
|
1373
|
+
})) {
|
|
1374
|
+
return;
|
|
1375
|
+
}
|
|
1376
|
+
const res = await requireClient().createSystemUser(body);
|
|
1377
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1378
|
+
});
|
|
925
1379
|
});
|
|
926
1380
|
// --- brand-members ---
|
|
927
1381
|
const brandMembersCmd = program.command("brand-members").description("品牌成员");
|
|
@@ -930,17 +1384,63 @@ brandMembersCmd
|
|
|
930
1384
|
.description("GET /system/brand-members (preferred)")
|
|
931
1385
|
.option("--user <uuid>", "userId 过滤")
|
|
932
1386
|
.action(async (opts) => {
|
|
933
|
-
|
|
934
|
-
|
|
1387
|
+
await withPermissionGate({
|
|
1388
|
+
command: "brand-members system-list",
|
|
1389
|
+
resource: "systemBrand",
|
|
1390
|
+
action: "list",
|
|
1391
|
+
}, async () => {
|
|
1392
|
+
const res = await requireClient().listSystemBrandMembers({
|
|
1393
|
+
userId: opts.user,
|
|
1394
|
+
});
|
|
1395
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1396
|
+
});
|
|
935
1397
|
});
|
|
936
1398
|
brandMembersCmd
|
|
937
1399
|
.command("system-create")
|
|
938
1400
|
.description("POST /system/brand-members (preferred)")
|
|
939
1401
|
.requiredOption("-j, --json <json>", "{ brandId, userId, role? }")
|
|
940
|
-
.
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
1402
|
+
.option("--dry-run", "预览", false)
|
|
1403
|
+
.option("--apply", "执行写入", false)
|
|
1404
|
+
.action(async (opts) => {
|
|
1405
|
+
await withPermissionGate({
|
|
1406
|
+
command: "brand-members system-create",
|
|
1407
|
+
resource: "brandMember",
|
|
1408
|
+
action: "create",
|
|
1409
|
+
}, async () => {
|
|
1410
|
+
const body = JSON.parse(opts.json);
|
|
1411
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1412
|
+
command: "brand-members system-create",
|
|
1413
|
+
danger: "write",
|
|
1414
|
+
summary: body,
|
|
1415
|
+
})) {
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
const res = await requireClient().createSystemBrandMember(body);
|
|
1419
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1420
|
+
});
|
|
1421
|
+
});
|
|
1422
|
+
brandMembersCmd
|
|
1423
|
+
.command("system-remove")
|
|
1424
|
+
.description("DELETE /system/brand-members/:memberId")
|
|
1425
|
+
.requiredOption("--id <memberId>", "BrandMember UUID")
|
|
1426
|
+
.option("--dry-run", "预览", false)
|
|
1427
|
+
.option("--apply", "执行写入", false)
|
|
1428
|
+
.action(async (opts) => {
|
|
1429
|
+
await withPermissionGate({
|
|
1430
|
+
command: "brand-members system-remove",
|
|
1431
|
+
resource: "brandMember",
|
|
1432
|
+
action: "delete",
|
|
1433
|
+
}, async () => {
|
|
1434
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1435
|
+
command: "brand-members system-remove",
|
|
1436
|
+
danger: "delete",
|
|
1437
|
+
summary: { memberId: opts.id },
|
|
1438
|
+
})) {
|
|
1439
|
+
return;
|
|
1440
|
+
}
|
|
1441
|
+
await requireClient().deleteSystemBrandMember(opts.id);
|
|
1442
|
+
console.log("OK");
|
|
1443
|
+
});
|
|
944
1444
|
});
|
|
945
1445
|
brandMembersCmd
|
|
946
1446
|
.command("list")
|
|
@@ -948,18 +1448,43 @@ brandMembersCmd
|
|
|
948
1448
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
949
1449
|
.option("--user <uuid>", "userId 过滤")
|
|
950
1450
|
.action(async (opts) => {
|
|
951
|
-
|
|
952
|
-
|
|
1451
|
+
await withPermissionGate({
|
|
1452
|
+
command: "brand-members list",
|
|
1453
|
+
resource: "brandMember",
|
|
1454
|
+
action: "list",
|
|
1455
|
+
teamId: opts.team,
|
|
1456
|
+
}, async () => {
|
|
1457
|
+
const res = await requireClient().listBrandMembers(opts.team, {
|
|
1458
|
+
userId: opts.user,
|
|
1459
|
+
});
|
|
1460
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1461
|
+
});
|
|
953
1462
|
});
|
|
954
1463
|
brandMembersCmd
|
|
955
1464
|
.command("create")
|
|
956
1465
|
.description("POST /brand-members")
|
|
957
1466
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
958
1467
|
.requiredOption("-j, --json <json>", "{ brandId, userId, role? }")
|
|
959
|
-
.
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1468
|
+
.option("--dry-run", "预览", false)
|
|
1469
|
+
.option("--apply", "执行写入", false)
|
|
1470
|
+
.action(async (opts) => {
|
|
1471
|
+
await withPermissionGate({
|
|
1472
|
+
command: "brand-members create",
|
|
1473
|
+
resource: "brandMember",
|
|
1474
|
+
action: "create",
|
|
1475
|
+
teamId: opts.team,
|
|
1476
|
+
}, async () => {
|
|
1477
|
+
const body = JSON.parse(opts.json);
|
|
1478
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1479
|
+
command: "brand-members create",
|
|
1480
|
+
danger: "write",
|
|
1481
|
+
summary: { teamId: opts.team, ...body },
|
|
1482
|
+
})) {
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
const res = await requireClient().createBrandMember(opts.team, body);
|
|
1486
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1487
|
+
});
|
|
963
1488
|
});
|
|
964
1489
|
// --- notification-channels ---
|
|
965
1490
|
const notifChannels = program
|
|
@@ -1023,56 +1548,147 @@ apiKeys
|
|
|
1023
1548
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1024
1549
|
.option("-n, --limit <n>", "limit", "50")
|
|
1025
1550
|
.action(async (opts) => {
|
|
1026
|
-
|
|
1027
|
-
|
|
1551
|
+
await withPermissionGate({
|
|
1552
|
+
command: "api-keys list",
|
|
1553
|
+
resource: "apiKey",
|
|
1554
|
+
action: "list",
|
|
1555
|
+
teamId: opts.team,
|
|
1556
|
+
}, async () => {
|
|
1557
|
+
const res = await requireClient().listApiKeys(opts.team, {
|
|
1558
|
+
limit: Number(opts.limit),
|
|
1559
|
+
});
|
|
1560
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1561
|
+
});
|
|
1028
1562
|
});
|
|
1029
1563
|
apiKeys
|
|
1030
1564
|
.command("create")
|
|
1031
1565
|
.description("POST /api-keys")
|
|
1032
1566
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1033
1567
|
.requiredOption("-j, --json <json>", "{ name, role, visibleCampaignIds? }")
|
|
1034
|
-
.
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1568
|
+
.option("--dry-run", "预览", false)
|
|
1569
|
+
.option("--apply", "执行写入", false)
|
|
1570
|
+
.action(async (opts) => {
|
|
1571
|
+
await withPermissionGate({
|
|
1572
|
+
command: "api-keys create",
|
|
1573
|
+
resource: "apiKey",
|
|
1574
|
+
action: "create",
|
|
1575
|
+
teamId: opts.team,
|
|
1576
|
+
}, async () => {
|
|
1577
|
+
const body = JSON.parse(opts.json);
|
|
1578
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1579
|
+
command: "api-keys create",
|
|
1580
|
+
danger: "secret",
|
|
1581
|
+
summary: { teamId: opts.team, ...body },
|
|
1582
|
+
})) {
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
const res = await requireClient().createApiKey(opts.team, body);
|
|
1586
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1587
|
+
});
|
|
1038
1588
|
});
|
|
1039
1589
|
apiKeys
|
|
1040
1590
|
.command("delete")
|
|
1041
1591
|
.description("DELETE /api-keys/:id")
|
|
1042
1592
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1043
1593
|
.requiredOption("--key <uuid>", "API Key UUID")
|
|
1044
|
-
.
|
|
1045
|
-
|
|
1046
|
-
|
|
1594
|
+
.option("--dry-run", "预览", false)
|
|
1595
|
+
.option("--apply", "执行写入", false)
|
|
1596
|
+
.action(async (opts) => {
|
|
1597
|
+
await withPermissionGate({
|
|
1598
|
+
command: "api-keys delete",
|
|
1599
|
+
resource: "apiKey",
|
|
1600
|
+
action: "delete",
|
|
1601
|
+
teamId: opts.team,
|
|
1602
|
+
}, async () => {
|
|
1603
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1604
|
+
command: "api-keys delete",
|
|
1605
|
+
danger: "delete",
|
|
1606
|
+
summary: { teamId: opts.team, keyId: opts.key },
|
|
1607
|
+
})) {
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
await requireClient().deleteApiKey(opts.team, opts.key);
|
|
1611
|
+
console.log("OK");
|
|
1612
|
+
});
|
|
1047
1613
|
});
|
|
1048
1614
|
apiKeys
|
|
1049
1615
|
.command("rotate")
|
|
1050
1616
|
.description("POST /api-keys/:id/rotate")
|
|
1051
1617
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1052
1618
|
.requiredOption("--key <uuid>", "API Key UUID")
|
|
1053
|
-
.
|
|
1054
|
-
|
|
1055
|
-
|
|
1619
|
+
.option("--dry-run", "预览", false)
|
|
1620
|
+
.option("--apply", "执行写入", false)
|
|
1621
|
+
.action(async (opts) => {
|
|
1622
|
+
await withPermissionGate({
|
|
1623
|
+
command: "api-keys rotate",
|
|
1624
|
+
resource: "apiKey",
|
|
1625
|
+
action: "update",
|
|
1626
|
+
teamId: opts.team,
|
|
1627
|
+
}, async () => {
|
|
1628
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1629
|
+
command: "api-keys rotate",
|
|
1630
|
+
danger: "secret",
|
|
1631
|
+
summary: { teamId: opts.team, keyId: opts.key },
|
|
1632
|
+
})) {
|
|
1633
|
+
return;
|
|
1634
|
+
}
|
|
1635
|
+
const res = await requireClient().rotateApiKey(opts.team, opts.key);
|
|
1636
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1637
|
+
});
|
|
1056
1638
|
});
|
|
1057
1639
|
apiKeys
|
|
1058
1640
|
.command("batch-revoke")
|
|
1059
1641
|
.description("POST /api-keys/batch/revoke")
|
|
1060
1642
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1061
|
-
.requiredOption("-j, --json <json>", '{ "
|
|
1062
|
-
.
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1643
|
+
.requiredOption("-j, --json <json>", '{ "apiKeyIds": ["uuid1","uuid2"] }')
|
|
1644
|
+
.option("--dry-run", "预览", false)
|
|
1645
|
+
.option("--apply", "执行写入", false)
|
|
1646
|
+
.action(async (opts) => {
|
|
1647
|
+
await withPermissionGate({
|
|
1648
|
+
command: "api-keys batch-revoke",
|
|
1649
|
+
resource: "apiKey",
|
|
1650
|
+
action: "delete",
|
|
1651
|
+
teamId: opts.team,
|
|
1652
|
+
}, async () => {
|
|
1653
|
+
const body = JSON.parse(opts.json);
|
|
1654
|
+
const apiKeyIds = body.apiKeyIds ?? body.ids ?? [];
|
|
1655
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1656
|
+
command: "api-keys batch-revoke",
|
|
1657
|
+
danger: "delete",
|
|
1658
|
+
summary: { teamId: opts.team, apiKeyIds },
|
|
1659
|
+
})) {
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
const res = await requireClient().batchRevokeApiKeys(opts.team, apiKeyIds);
|
|
1663
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1664
|
+
});
|
|
1066
1665
|
});
|
|
1067
1666
|
apiKeys
|
|
1068
1667
|
.command("batch-rotate")
|
|
1069
1668
|
.description("POST /api-keys/batch/rotate")
|
|
1070
1669
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1071
|
-
.requiredOption("-j, --json <json>", '{ "
|
|
1072
|
-
.
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1670
|
+
.requiredOption("-j, --json <json>", '{ "apiKeyIds": ["uuid1","uuid2"] }')
|
|
1671
|
+
.option("--dry-run", "预览", false)
|
|
1672
|
+
.option("--apply", "执行写入", false)
|
|
1673
|
+
.action(async (opts) => {
|
|
1674
|
+
await withPermissionGate({
|
|
1675
|
+
command: "api-keys batch-rotate",
|
|
1676
|
+
resource: "apiKey",
|
|
1677
|
+
action: "update",
|
|
1678
|
+
teamId: opts.team,
|
|
1679
|
+
}, async () => {
|
|
1680
|
+
const body = JSON.parse(opts.json);
|
|
1681
|
+
const apiKeyIds = body.apiKeyIds ?? body.ids ?? [];
|
|
1682
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1683
|
+
command: "api-keys batch-rotate",
|
|
1684
|
+
danger: "secret",
|
|
1685
|
+
summary: { teamId: opts.team, apiKeyIds },
|
|
1686
|
+
})) {
|
|
1687
|
+
return;
|
|
1688
|
+
}
|
|
1689
|
+
const res = await requireClient().batchRotateApiKeys(opts.team, apiKeyIds);
|
|
1690
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1691
|
+
});
|
|
1076
1692
|
});
|
|
1077
1693
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1078
1694
|
// Group C: scattered endpoint additions
|
|
@@ -1127,9 +1743,16 @@ program
|
|
|
1127
1743
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1128
1744
|
.requiredOption("-j, --json <json>", "权限矩阵更新 body")
|
|
1129
1745
|
.action(async (opts) => {
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1746
|
+
await withPermissionGate({
|
|
1747
|
+
command: "permissions-update",
|
|
1748
|
+
resource: "permissionMatrix",
|
|
1749
|
+
action: "update",
|
|
1750
|
+
teamId: opts.team,
|
|
1751
|
+
}, async () => {
|
|
1752
|
+
const body = JSON.parse(opts.json);
|
|
1753
|
+
const res = await requireClient().updatePermissionsMatrix(opts.team, body);
|
|
1754
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1755
|
+
});
|
|
1133
1756
|
});
|
|
1134
1757
|
graph
|
|
1135
1758
|
.command("drilldown")
|
|
@@ -1144,6 +1767,114 @@ graph
|
|
|
1144
1767
|
});
|
|
1145
1768
|
console.log(JSON.stringify(res, null, 2));
|
|
1146
1769
|
});
|
|
1770
|
+
graph
|
|
1771
|
+
.command("v2-ego")
|
|
1772
|
+
.description("GET /graph/v2/ego")
|
|
1773
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1774
|
+
.option("--node-id <uuid>", "graph nodeId")
|
|
1775
|
+
.option("--node-ref <id>", "nodeRefId (e.g. account UUID)")
|
|
1776
|
+
.option("--node-type <type>", "account|team|campaign")
|
|
1777
|
+
.option("--relation-type <type>", "relationType filter")
|
|
1778
|
+
.option("--edge-kind <kind>", "edgeKind filter")
|
|
1779
|
+
.option("-n, --limit <n>", "limit", "100")
|
|
1780
|
+
.action(async (opts) => {
|
|
1781
|
+
const res = await requireClient().getGraphV2Ego(opts.team, {
|
|
1782
|
+
nodeId: opts.nodeId,
|
|
1783
|
+
nodeRefId: opts.nodeRef,
|
|
1784
|
+
nodeType: opts.nodeType,
|
|
1785
|
+
relationType: opts.relationType,
|
|
1786
|
+
edgeKind: opts.edgeKind,
|
|
1787
|
+
limit: Number(opts.limit),
|
|
1788
|
+
});
|
|
1789
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1790
|
+
});
|
|
1791
|
+
graph
|
|
1792
|
+
.command("v2-timeline")
|
|
1793
|
+
.description("GET /graph/v2/timeline")
|
|
1794
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1795
|
+
.option("--node-id <uuid>", "graph nodeId")
|
|
1796
|
+
.option("--node-ref <id>", "nodeRefId")
|
|
1797
|
+
.option("--node-type <type>", "account|team|campaign")
|
|
1798
|
+
.option("--from <iso>", "from ISO time")
|
|
1799
|
+
.option("--to <iso>", "to ISO time")
|
|
1800
|
+
.option("-n, --limit <n>", "limit", "100")
|
|
1801
|
+
.action(async (opts) => {
|
|
1802
|
+
const res = await requireClient().getGraphV2Timeline(opts.team, {
|
|
1803
|
+
nodeId: opts.nodeId,
|
|
1804
|
+
nodeRefId: opts.nodeRef,
|
|
1805
|
+
nodeType: opts.nodeType,
|
|
1806
|
+
from: opts.from,
|
|
1807
|
+
to: opts.to,
|
|
1808
|
+
limit: Number(opts.limit),
|
|
1809
|
+
});
|
|
1810
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1811
|
+
});
|
|
1812
|
+
graph
|
|
1813
|
+
.command("v2-explain")
|
|
1814
|
+
.description("GET /graph/v2/explain/:edgeId")
|
|
1815
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1816
|
+
.requiredOption("--edge-id <id>", "edge UUID")
|
|
1817
|
+
.action(async (opts) => {
|
|
1818
|
+
const res = await requireClient().getGraphV2Explain(opts.team, opts.edgeId);
|
|
1819
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1820
|
+
});
|
|
1821
|
+
graph
|
|
1822
|
+
.command("v2-path")
|
|
1823
|
+
.description("GET /graph/v2/path")
|
|
1824
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1825
|
+
.option("--source-id <uuid>", "source nodeId")
|
|
1826
|
+
.option("--source-ref <id>", "source nodeRefId")
|
|
1827
|
+
.option("--source-type <type>", "source nodeType")
|
|
1828
|
+
.option("--target-id <uuid>", "target nodeId")
|
|
1829
|
+
.option("--target-ref <id>", "target nodeRefId")
|
|
1830
|
+
.option("--target-type <type>", "target nodeType")
|
|
1831
|
+
.option("--max-depth <n>", "maxDepth", "4")
|
|
1832
|
+
.option("-n, --limit <n>", "limit", "50")
|
|
1833
|
+
.action(async (opts) => {
|
|
1834
|
+
const res = await requireClient().getGraphV2Path(opts.team, {
|
|
1835
|
+
sourceNodeId: opts.sourceId,
|
|
1836
|
+
sourceNodeRefId: opts.sourceRef,
|
|
1837
|
+
sourceNodeType: opts.sourceType,
|
|
1838
|
+
targetNodeId: opts.targetId,
|
|
1839
|
+
targetNodeRefId: opts.targetRef,
|
|
1840
|
+
targetNodeType: opts.targetType,
|
|
1841
|
+
maxDepth: Number(opts.maxDepth),
|
|
1842
|
+
limit: Number(opts.limit),
|
|
1843
|
+
});
|
|
1844
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1845
|
+
});
|
|
1846
|
+
graph
|
|
1847
|
+
.command("v2-clusters")
|
|
1848
|
+
.description("GET /graph/v2/clusters")
|
|
1849
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1850
|
+
.option("--scope-ref <id>", "scopeNodeRefId")
|
|
1851
|
+
.option("--scope-type <type>", "scopeNodeType", "account")
|
|
1852
|
+
.option("--min-risk <n>", "minRiskScore", "60")
|
|
1853
|
+
.option("-n, --limit <n>", "limit", "100")
|
|
1854
|
+
.action(async (opts) => {
|
|
1855
|
+
const res = await requireClient().getGraphV2Clusters(opts.team, {
|
|
1856
|
+
scopeNodeRefId: opts.scopeRef,
|
|
1857
|
+
scopeNodeType: opts.scopeType,
|
|
1858
|
+
minRiskScore: Number(opts.minRisk),
|
|
1859
|
+
limit: Number(opts.limit),
|
|
1860
|
+
});
|
|
1861
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1862
|
+
});
|
|
1863
|
+
graph
|
|
1864
|
+
.command("v2-impact")
|
|
1865
|
+
.description("GET /graph/v2/impact")
|
|
1866
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1867
|
+
.option("--campaign-ref <id>", "campaignNodeRefId")
|
|
1868
|
+
.option("--min-reuse <n>", "minReuseCount", "2")
|
|
1869
|
+
.option("-n, --limit <n>", "limit", "100")
|
|
1870
|
+
.action(async (opts) => {
|
|
1871
|
+
const res = await requireClient().getGraphV2Impact(opts.team, {
|
|
1872
|
+
campaignNodeRefId: opts.campaignRef,
|
|
1873
|
+
minReuseCount: Number(opts.minReuse),
|
|
1874
|
+
limit: Number(opts.limit),
|
|
1875
|
+
});
|
|
1876
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1877
|
+
});
|
|
1147
1878
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1148
1879
|
// Group D: add filter params to existing list commands
|
|
1149
1880
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -1380,9 +2111,25 @@ brandMembersCmd
|
|
|
1380
2111
|
.description("DELETE /brand-members/:memberId")
|
|
1381
2112
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1382
2113
|
.requiredOption("-m, --member <memberId>", "BrandMember UUID")
|
|
1383
|
-
.
|
|
1384
|
-
|
|
1385
|
-
|
|
2114
|
+
.option("--dry-run", "预览", false)
|
|
2115
|
+
.option("--apply", "执行写入", false)
|
|
2116
|
+
.action(async (opts) => {
|
|
2117
|
+
await withPermissionGate({
|
|
2118
|
+
command: "brand-members remove",
|
|
2119
|
+
resource: "brandMember",
|
|
2120
|
+
action: "delete",
|
|
2121
|
+
teamId: opts.team,
|
|
2122
|
+
}, async () => {
|
|
2123
|
+
if (!assertApplyOrDryRun(opts, {
|
|
2124
|
+
command: "brand-members remove",
|
|
2125
|
+
danger: "delete",
|
|
2126
|
+
summary: { teamId: opts.team, memberId: opts.member },
|
|
2127
|
+
})) {
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
const res = await requireClient().deleteBrandMember(opts.team, opts.member);
|
|
2131
|
+
console.log(JSON.stringify(res, null, 2));
|
|
2132
|
+
});
|
|
1386
2133
|
});
|
|
1387
2134
|
// Only parse when run directly (not when imported for testing)
|
|
1388
2135
|
if (!process.env["VITEST"]) {
|