@geoly-ai/social-hub-cli 0.0.11 → 0.0.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.
- package/CHANGELOG.md +15 -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 +723 -58
- 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 +16 -19
- package/dist/register-extensions.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 +49 -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 +21 -6
- package/skills/social-hub-ops-runtime/SKILL.md +10 -5
- package/skills/social-hub-posts/SKILL.md +54 -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";
|
|
@@ -341,8 +343,35 @@ accounts
|
|
|
341
343
|
});
|
|
342
344
|
// --- brands & campaigns ---
|
|
343
345
|
const brands = program.command("brands").description("品牌");
|
|
346
|
+
brands
|
|
347
|
+
.command("system-list")
|
|
348
|
+
.description("GET /system/brands (preferred)")
|
|
349
|
+
.action(async () => {
|
|
350
|
+
const res = await requireClient().listSystemBrands();
|
|
351
|
+
console.log(JSON.stringify(res, null, 2));
|
|
352
|
+
});
|
|
353
|
+
brands
|
|
354
|
+
.command("system-create")
|
|
355
|
+
.description("POST /system/brands (preferred)")
|
|
356
|
+
.requiredOption("-j, --json <json>", "{ name, slug? }")
|
|
357
|
+
.action(async (opts) => {
|
|
358
|
+
let body;
|
|
359
|
+
try {
|
|
360
|
+
body = JSON.parse(opts.json);
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
console.error("Invalid -j / --json");
|
|
364
|
+
process.exit(1);
|
|
365
|
+
}
|
|
366
|
+
const res = await requireClient().createSystemBrand({
|
|
367
|
+
name: String(body.name ?? ""),
|
|
368
|
+
slug: body.slug,
|
|
369
|
+
});
|
|
370
|
+
console.log(JSON.stringify(res, null, 2));
|
|
371
|
+
});
|
|
344
372
|
brands
|
|
345
373
|
.command("list")
|
|
374
|
+
.description("GET /teams/:teamId/brands (compat)")
|
|
346
375
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
347
376
|
.action(async (opts) => {
|
|
348
377
|
const res = await requireClient().listBrands(opts.team);
|
|
@@ -577,6 +606,28 @@ compliance
|
|
|
577
606
|
const res = await requireClient().upsertAccountRiskProfile(opts.team, opts.account, body);
|
|
578
607
|
console.log(JSON.stringify(res, null, 2));
|
|
579
608
|
});
|
|
609
|
+
compliance
|
|
610
|
+
.command("rule-caches-list")
|
|
611
|
+
.description("GET /subreddit-rule-caches")
|
|
612
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
613
|
+
.option("--include-expired", "include expired caches")
|
|
614
|
+
.action(async (opts) => {
|
|
615
|
+
const res = await requireClient().listSubredditRuleCaches(opts.team, {
|
|
616
|
+
includeExpired: Boolean(opts.includeExpired),
|
|
617
|
+
});
|
|
618
|
+
console.log(JSON.stringify(res, null, 2));
|
|
619
|
+
});
|
|
620
|
+
compliance
|
|
621
|
+
.command("rule-caches-upsert")
|
|
622
|
+
.description("PUT /subreddit-rule-caches/:subreddit")
|
|
623
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
624
|
+
.requiredOption("--sub <name>", "subreddit name")
|
|
625
|
+
.requiredOption("-j, --json <json>", "{ rulesMarkdown, sourceUrl?, expiresInDays? }")
|
|
626
|
+
.action(async (opts) => {
|
|
627
|
+
const body = JSON.parse(opts.json);
|
|
628
|
+
const res = await requireClient().upsertSubredditRuleCache(opts.team, opts.sub, body);
|
|
629
|
+
console.log(JSON.stringify(res, null, 2));
|
|
630
|
+
});
|
|
580
631
|
// --- openclaw ---
|
|
581
632
|
program
|
|
582
633
|
.command("openclaw-ingest")
|
|
@@ -689,8 +740,14 @@ agentTeams
|
|
|
689
740
|
.command("list")
|
|
690
741
|
.description("GET /v1/agent-teams")
|
|
691
742
|
.action(async () => {
|
|
692
|
-
|
|
693
|
-
|
|
743
|
+
await withPermissionGate({
|
|
744
|
+
command: "agent-teams list",
|
|
745
|
+
resource: "agentTeam",
|
|
746
|
+
action: "list",
|
|
747
|
+
}, async () => {
|
|
748
|
+
const res = await requireClient().listAgentTeams();
|
|
749
|
+
console.log(JSON.stringify(res, null, 2));
|
|
750
|
+
});
|
|
694
751
|
});
|
|
695
752
|
agentTeams
|
|
696
753
|
.command("workspace-docs")
|
|
@@ -699,30 +756,71 @@ agentTeams
|
|
|
699
756
|
.option("--kind <kind>", "file|daily_report|weekly_report|ops_record")
|
|
700
757
|
.option("-n, --limit <n>", "limit", "50")
|
|
701
758
|
.action(async (opts) => {
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
759
|
+
await withPermissionGate({
|
|
760
|
+
command: "agent-teams workspace-docs",
|
|
761
|
+
resource: "agentTeam",
|
|
762
|
+
action: "read",
|
|
763
|
+
teamId: opts.team,
|
|
764
|
+
}, async () => {
|
|
765
|
+
const res = await requireClient().listWorkspaceDocuments(opts.team, {
|
|
766
|
+
kind: opts.kind,
|
|
767
|
+
limit: Number(opts.limit),
|
|
768
|
+
});
|
|
769
|
+
console.log(JSON.stringify(res, null, 2));
|
|
705
770
|
});
|
|
706
|
-
console.log(JSON.stringify(res, null, 2));
|
|
707
771
|
});
|
|
708
772
|
agentTeams
|
|
709
773
|
.command("create")
|
|
710
774
|
.description("POST /v1/agent-teams — 新建团队(admin only)")
|
|
711
775
|
.requiredOption("--slug <slug>", "团队 slug(小写字母、数字、横线)")
|
|
712
776
|
.requiredOption("--name <name>", "团队名称")
|
|
713
|
-
.
|
|
714
|
-
|
|
715
|
-
|
|
777
|
+
.option("--dry-run", "预览", false)
|
|
778
|
+
.option("--apply", "执行写入", false)
|
|
779
|
+
.action(async (opts) => {
|
|
780
|
+
await withPermissionGate({
|
|
781
|
+
command: "agent-teams create",
|
|
782
|
+
resource: "agentTeam",
|
|
783
|
+
action: "create",
|
|
784
|
+
}, async () => {
|
|
785
|
+
if (!assertApplyOrDryRun(opts, {
|
|
786
|
+
command: "agent-teams create",
|
|
787
|
+
danger: "admin",
|
|
788
|
+
summary: { slug: opts.slug, name: opts.name },
|
|
789
|
+
})) {
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
const res = await requireClient().createTeam({
|
|
793
|
+
slug: opts.slug,
|
|
794
|
+
name: opts.name,
|
|
795
|
+
});
|
|
796
|
+
console.log(JSON.stringify(res, null, 2));
|
|
797
|
+
});
|
|
716
798
|
});
|
|
717
799
|
agentTeams
|
|
718
800
|
.command("update")
|
|
719
801
|
.description("PATCH /v1/agent-teams/:teamId — 更新团队(admin only)")
|
|
720
802
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
721
803
|
.requiredOption("-j, --json <json>", "{ slug?, name? }")
|
|
722
|
-
.
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
804
|
+
.option("--dry-run", "预览", false)
|
|
805
|
+
.option("--apply", "执行写入", false)
|
|
806
|
+
.action(async (opts) => {
|
|
807
|
+
await withPermissionGate({
|
|
808
|
+
command: "agent-teams update",
|
|
809
|
+
resource: "agentTeam",
|
|
810
|
+
action: "update",
|
|
811
|
+
teamId: opts.team,
|
|
812
|
+
}, async () => {
|
|
813
|
+
const body = JSON.parse(opts.json);
|
|
814
|
+
if (!assertApplyOrDryRun(opts, {
|
|
815
|
+
command: "agent-teams update",
|
|
816
|
+
danger: "admin",
|
|
817
|
+
summary: { teamId: opts.team, ...body },
|
|
818
|
+
})) {
|
|
819
|
+
return;
|
|
820
|
+
}
|
|
821
|
+
const res = await requireClient().updateTeam(opts.team, body);
|
|
822
|
+
console.log(JSON.stringify(res, null, 2));
|
|
823
|
+
});
|
|
726
824
|
});
|
|
727
825
|
agentTeams
|
|
728
826
|
.command("add-member")
|
|
@@ -730,12 +828,32 @@ agentTeams
|
|
|
730
828
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
731
829
|
.requiredOption("--user <userId>", "User UUID")
|
|
732
830
|
.requiredOption("--role <role>", "admin|manager|supervisor|senior|internal|client")
|
|
733
|
-
.
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
831
|
+
.option("--dry-run", "预览", false)
|
|
832
|
+
.option("--apply", "执行写入", false)
|
|
833
|
+
.action(async (opts) => {
|
|
834
|
+
await withPermissionGate({
|
|
835
|
+
command: "agent-teams add-member",
|
|
836
|
+
resource: "agentTeam",
|
|
837
|
+
action: "update",
|
|
838
|
+
teamId: opts.team,
|
|
839
|
+
}, async () => {
|
|
840
|
+
if (!assertApplyOrDryRun(opts, {
|
|
841
|
+
command: "agent-teams add-member",
|
|
842
|
+
danger: "admin",
|
|
843
|
+
summary: {
|
|
844
|
+
teamId: opts.team,
|
|
845
|
+
userId: opts.user,
|
|
846
|
+
role: opts.role,
|
|
847
|
+
},
|
|
848
|
+
})) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
const res = await requireClient().addTeamMember(opts.team, {
|
|
852
|
+
userId: opts.user,
|
|
853
|
+
role: opts.role,
|
|
854
|
+
});
|
|
855
|
+
console.log(JSON.stringify(res, null, 2));
|
|
737
856
|
});
|
|
738
|
-
console.log(JSON.stringify(res, null, 2));
|
|
739
857
|
});
|
|
740
858
|
// --- intelligence (subreddit-intelligence) ---
|
|
741
859
|
const intel = program.command("intelligence").description("板块情报与 KOL 挖掘");
|
|
@@ -876,6 +994,192 @@ intel
|
|
|
876
994
|
const res = await requireClient().dispatchTaskFromHotPost(opts.team, body);
|
|
877
995
|
console.log(JSON.stringify(res, null, 2));
|
|
878
996
|
});
|
|
997
|
+
intel
|
|
998
|
+
.command("tier-rules-list")
|
|
999
|
+
.description("GET .../subreddit-intelligence/tier-rules")
|
|
1000
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1001
|
+
.option("--tier <tier>", "tier filter")
|
|
1002
|
+
.option("--enabled <bool>", "true|false")
|
|
1003
|
+
.option("-n, --limit <n>", "limit", "50")
|
|
1004
|
+
.option("--offset <n>", "offset", "0")
|
|
1005
|
+
.action(async (opts) => {
|
|
1006
|
+
const res = await requireClient().listTierRules(opts.team, {
|
|
1007
|
+
tier: opts.tier,
|
|
1008
|
+
enabled: opts.enabled === undefined
|
|
1009
|
+
? undefined
|
|
1010
|
+
: opts.enabled === "true" || opts.enabled === "1",
|
|
1011
|
+
limit: Number(opts.limit),
|
|
1012
|
+
offset: Number(opts.offset),
|
|
1013
|
+
});
|
|
1014
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1015
|
+
});
|
|
1016
|
+
intel
|
|
1017
|
+
.command("tier-rules-create")
|
|
1018
|
+
.description("POST .../subreddit-intelligence/tier-rules")
|
|
1019
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1020
|
+
.requiredOption("-j, --json <json>", "tier rule body")
|
|
1021
|
+
.action(async (opts) => {
|
|
1022
|
+
const body = JSON.parse(opts.json);
|
|
1023
|
+
const res = await requireClient().createTierRule(opts.team, body);
|
|
1024
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1025
|
+
});
|
|
1026
|
+
intel
|
|
1027
|
+
.command("tier-rules-update")
|
|
1028
|
+
.description("PATCH .../subreddit-intelligence/tier-rules/:id")
|
|
1029
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1030
|
+
.requiredOption("--id <uuid>", "Tier rule UUID")
|
|
1031
|
+
.requiredOption("-j, --json <json>", "update body")
|
|
1032
|
+
.action(async (opts) => {
|
|
1033
|
+
const body = JSON.parse(opts.json);
|
|
1034
|
+
const res = await requireClient().updateTierRule(opts.team, opts.id, body);
|
|
1035
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1036
|
+
});
|
|
1037
|
+
intel
|
|
1038
|
+
.command("tier-rules-delete")
|
|
1039
|
+
.description("DELETE .../subreddit-intelligence/tier-rules/:id")
|
|
1040
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1041
|
+
.requiredOption("--id <uuid>", "Tier rule UUID")
|
|
1042
|
+
.action(async (opts) => {
|
|
1043
|
+
const res = await requireClient().deleteTierRule(opts.team, opts.id);
|
|
1044
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1045
|
+
});
|
|
1046
|
+
intel
|
|
1047
|
+
.command("runs-list")
|
|
1048
|
+
.description("GET .../subreddit-intelligence/runs")
|
|
1049
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1050
|
+
.option("--dimension <d>", "industry|keywords|tier")
|
|
1051
|
+
.option("--status <s>", "queued|running|succeeded|failed")
|
|
1052
|
+
.option("-n, --limit <n>", "limit", "50")
|
|
1053
|
+
.option("--offset <n>", "offset", "0")
|
|
1054
|
+
.action(async (opts) => {
|
|
1055
|
+
const res = await requireClient().listIntelRuns(opts.team, {
|
|
1056
|
+
dimension: opts.dimension,
|
|
1057
|
+
status: opts.status,
|
|
1058
|
+
limit: Number(opts.limit),
|
|
1059
|
+
offset: Number(opts.offset),
|
|
1060
|
+
});
|
|
1061
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1062
|
+
});
|
|
1063
|
+
intel
|
|
1064
|
+
.command("kol-profiles-list")
|
|
1065
|
+
.description("GET .../subreddit-intelligence/kol-profiles")
|
|
1066
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1067
|
+
.option("--platform <p>", "reddit")
|
|
1068
|
+
.option("-n, --limit <n>", "limit", "50")
|
|
1069
|
+
.option("--offset <n>", "offset", "0")
|
|
1070
|
+
.action(async (opts) => {
|
|
1071
|
+
const res = await requireClient().listKolProfiles(opts.team, {
|
|
1072
|
+
platform: opts.platform,
|
|
1073
|
+
limit: Number(opts.limit),
|
|
1074
|
+
offset: Number(opts.offset),
|
|
1075
|
+
});
|
|
1076
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1077
|
+
});
|
|
1078
|
+
intel
|
|
1079
|
+
.command("kol-profiles-get")
|
|
1080
|
+
.description("GET .../subreddit-intelligence/kol-profiles/:id")
|
|
1081
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1082
|
+
.requiredOption("--id <uuid>", "KOL profile UUID")
|
|
1083
|
+
.action(async (opts) => {
|
|
1084
|
+
const res = await requireClient().getKolProfile(opts.team, opts.id);
|
|
1085
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1086
|
+
});
|
|
1087
|
+
intel
|
|
1088
|
+
.command("fetch-by-industry")
|
|
1089
|
+
.description("POST .../subreddit-intelligence/hot-posts/fetch-by-industry")
|
|
1090
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1091
|
+
.requiredOption("-j, --json <json>", "fetch body")
|
|
1092
|
+
.action(async (opts) => {
|
|
1093
|
+
const body = JSON.parse(opts.json);
|
|
1094
|
+
const res = await requireClient().fetchHotPostsByIndustry(opts.team, body);
|
|
1095
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1096
|
+
});
|
|
1097
|
+
intel
|
|
1098
|
+
.command("fetch-by-tier")
|
|
1099
|
+
.description("POST .../subreddit-intelligence/hot-posts/fetch-by-tier")
|
|
1100
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1101
|
+
.requiredOption("-j, --json <json>", "fetch body")
|
|
1102
|
+
.action(async (opts) => {
|
|
1103
|
+
const body = JSON.parse(opts.json);
|
|
1104
|
+
const res = await requireClient().fetchHotPostsByTier(opts.team, body);
|
|
1105
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1106
|
+
});
|
|
1107
|
+
intel
|
|
1108
|
+
.command("fetch-by-keywords")
|
|
1109
|
+
.description("POST .../subreddit-intelligence/insights/fetch-by-keywords")
|
|
1110
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1111
|
+
.requiredOption("-j, --json <json>", "fetch body")
|
|
1112
|
+
.action(async (opts) => {
|
|
1113
|
+
const body = JSON.parse(opts.json);
|
|
1114
|
+
const res = await requireClient().fetchInsightsByKeywords(opts.team, body);
|
|
1115
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1116
|
+
});
|
|
1117
|
+
intel
|
|
1118
|
+
.command("brand-mention-radar")
|
|
1119
|
+
.description("POST .../subreddit-intelligence/insights/brand-mention-radar")
|
|
1120
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1121
|
+
.requiredOption("-j, --json <json>", "{ keywords, minScore?, minComments? }")
|
|
1122
|
+
.action(async (opts) => {
|
|
1123
|
+
const body = JSON.parse(opts.json);
|
|
1124
|
+
const res = await requireClient().getBrandMentionRadar(opts.team, body);
|
|
1125
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1126
|
+
});
|
|
1127
|
+
intel
|
|
1128
|
+
.command("opportunity-map")
|
|
1129
|
+
.description("POST .../subreddit-intelligence/insights/opportunity-map")
|
|
1130
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1131
|
+
.requiredOption("-j, --json <json>", "{ keywords, minScore?, minComments? }")
|
|
1132
|
+
.action(async (opts) => {
|
|
1133
|
+
const body = JSON.parse(opts.json);
|
|
1134
|
+
const res = await requireClient().getOpportunityMap(opts.team, body);
|
|
1135
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1136
|
+
});
|
|
1137
|
+
intel
|
|
1138
|
+
.command("sov")
|
|
1139
|
+
.description("POST .../subreddit-intelligence/insights/sov")
|
|
1140
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1141
|
+
.requiredOption("-j, --json <json>", "{ brandKeywords, competitorKeywords, minScore?, minComments? }")
|
|
1142
|
+
.action(async (opts) => {
|
|
1143
|
+
const body = JSON.parse(opts.json);
|
|
1144
|
+
const res = await requireClient().getInsightsSov(opts.team, body);
|
|
1145
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1146
|
+
});
|
|
1147
|
+
intel
|
|
1148
|
+
.command("sentiment-intent")
|
|
1149
|
+
.description("GET .../subreddit-intelligence/insights/sentiment-intent")
|
|
1150
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1151
|
+
.action(async (opts) => {
|
|
1152
|
+
const res = await requireClient().getSentimentIntent(opts.team);
|
|
1153
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1154
|
+
});
|
|
1155
|
+
intel
|
|
1156
|
+
.command("campaign-lift")
|
|
1157
|
+
.description("POST .../subreddit-intelligence/insights/campaign-lift")
|
|
1158
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1159
|
+
.requiredOption("-j, --json <json>", "{ keywords, campaignStartAt, campaignEndAt, windowDays?, minScore?, minComments? }")
|
|
1160
|
+
.action(async (opts) => {
|
|
1161
|
+
const body = JSON.parse(opts.json);
|
|
1162
|
+
const res = await requireClient().getCampaignLift(opts.team, body);
|
|
1163
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1164
|
+
});
|
|
1165
|
+
intel
|
|
1166
|
+
.command("snapshot-latest")
|
|
1167
|
+
.description("GET .../subreddit-intelligence/insights/snapshot-latest")
|
|
1168
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1169
|
+
.action(async (opts) => {
|
|
1170
|
+
const res = await requireClient().getLatestInsightSnapshot(opts.team);
|
|
1171
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1172
|
+
});
|
|
1173
|
+
intel
|
|
1174
|
+
.command("snapshot-save")
|
|
1175
|
+
.description("POST .../subreddit-intelligence/insights/snapshot")
|
|
1176
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1177
|
+
.requiredOption("-j, --json <json>", "{ payload: { ... } }")
|
|
1178
|
+
.action(async (opts) => {
|
|
1179
|
+
const body = JSON.parse(opts.json);
|
|
1180
|
+
const res = await requireClient().saveInsightSnapshot(opts.team, body);
|
|
1181
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1182
|
+
});
|
|
879
1183
|
// --- users ---
|
|
880
1184
|
const usersCmd = program.command("users").description("团队成员管理");
|
|
881
1185
|
usersCmd
|
|
@@ -883,8 +1187,28 @@ usersCmd
|
|
|
883
1187
|
.description("GET /users")
|
|
884
1188
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
885
1189
|
.action(async (opts) => {
|
|
886
|
-
|
|
887
|
-
|
|
1190
|
+
await withPermissionGate({
|
|
1191
|
+
command: "users list",
|
|
1192
|
+
resource: "systemMember",
|
|
1193
|
+
action: "list",
|
|
1194
|
+
teamId: opts.team,
|
|
1195
|
+
}, async () => {
|
|
1196
|
+
const res = await requireClient().listUsers(opts.team);
|
|
1197
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1198
|
+
});
|
|
1199
|
+
});
|
|
1200
|
+
usersCmd
|
|
1201
|
+
.command("system-list")
|
|
1202
|
+
.description("GET /v1/system/users")
|
|
1203
|
+
.action(async () => {
|
|
1204
|
+
await withPermissionGate({
|
|
1205
|
+
command: "users system-list",
|
|
1206
|
+
resource: "systemMember",
|
|
1207
|
+
action: "list",
|
|
1208
|
+
}, async () => {
|
|
1209
|
+
const res = await requireClient().listSystemUsers();
|
|
1210
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1211
|
+
});
|
|
888
1212
|
});
|
|
889
1213
|
usersCmd
|
|
890
1214
|
.command("create")
|
|
@@ -892,30 +1216,149 @@ usersCmd
|
|
|
892
1216
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
893
1217
|
.requiredOption("-j, --json <json>", "{ email, password, role? }")
|
|
894
1218
|
.action(async (opts) => {
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1219
|
+
await withPermissionGate({
|
|
1220
|
+
command: "users create",
|
|
1221
|
+
resource: "systemMember",
|
|
1222
|
+
action: "create",
|
|
1223
|
+
teamId: opts.team,
|
|
1224
|
+
}, async () => {
|
|
1225
|
+
const body = JSON.parse(opts.json);
|
|
1226
|
+
const res = await requireClient().createUser(opts.team, body);
|
|
1227
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1228
|
+
});
|
|
1229
|
+
});
|
|
1230
|
+
usersCmd
|
|
1231
|
+
.command("system-create")
|
|
1232
|
+
.description("POST /v1/system/users")
|
|
1233
|
+
.requiredOption("-j, --json <json>", "{ email, name, password?, role? }")
|
|
1234
|
+
.option("--dry-run", "预览", false)
|
|
1235
|
+
.option("--apply", "执行写入", false)
|
|
1236
|
+
.action(async (opts) => {
|
|
1237
|
+
await withPermissionGate({
|
|
1238
|
+
command: "users system-create",
|
|
1239
|
+
resource: "systemMember",
|
|
1240
|
+
action: "create",
|
|
1241
|
+
}, async () => {
|
|
1242
|
+
const body = JSON.parse(opts.json);
|
|
1243
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1244
|
+
command: "users system-create",
|
|
1245
|
+
danger: "write",
|
|
1246
|
+
summary: { email: body.email, name: body.name },
|
|
1247
|
+
})) {
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
const res = await requireClient().createSystemUser(body);
|
|
1251
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1252
|
+
});
|
|
898
1253
|
});
|
|
899
1254
|
// --- brand-members ---
|
|
900
1255
|
const brandMembersCmd = program.command("brand-members").description("品牌成员");
|
|
1256
|
+
brandMembersCmd
|
|
1257
|
+
.command("system-list")
|
|
1258
|
+
.description("GET /system/brand-members (preferred)")
|
|
1259
|
+
.option("--user <uuid>", "userId 过滤")
|
|
1260
|
+
.action(async (opts) => {
|
|
1261
|
+
await withPermissionGate({
|
|
1262
|
+
command: "brand-members system-list",
|
|
1263
|
+
resource: "systemBrand",
|
|
1264
|
+
action: "list",
|
|
1265
|
+
}, async () => {
|
|
1266
|
+
const res = await requireClient().listSystemBrandMembers({
|
|
1267
|
+
userId: opts.user,
|
|
1268
|
+
});
|
|
1269
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1270
|
+
});
|
|
1271
|
+
});
|
|
1272
|
+
brandMembersCmd
|
|
1273
|
+
.command("system-create")
|
|
1274
|
+
.description("POST /system/brand-members (preferred)")
|
|
1275
|
+
.requiredOption("-j, --json <json>", "{ brandId, userId, role? }")
|
|
1276
|
+
.option("--dry-run", "预览", false)
|
|
1277
|
+
.option("--apply", "执行写入", false)
|
|
1278
|
+
.action(async (opts) => {
|
|
1279
|
+
await withPermissionGate({
|
|
1280
|
+
command: "brand-members system-create",
|
|
1281
|
+
resource: "brandMember",
|
|
1282
|
+
action: "create",
|
|
1283
|
+
}, async () => {
|
|
1284
|
+
const body = JSON.parse(opts.json);
|
|
1285
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1286
|
+
command: "brand-members system-create",
|
|
1287
|
+
danger: "write",
|
|
1288
|
+
summary: body,
|
|
1289
|
+
})) {
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
const res = await requireClient().createSystemBrandMember(body);
|
|
1293
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1294
|
+
});
|
|
1295
|
+
});
|
|
1296
|
+
brandMembersCmd
|
|
1297
|
+
.command("system-remove")
|
|
1298
|
+
.description("DELETE /system/brand-members/:memberId")
|
|
1299
|
+
.requiredOption("--id <memberId>", "BrandMember UUID")
|
|
1300
|
+
.option("--dry-run", "预览", false)
|
|
1301
|
+
.option("--apply", "执行写入", false)
|
|
1302
|
+
.action(async (opts) => {
|
|
1303
|
+
await withPermissionGate({
|
|
1304
|
+
command: "brand-members system-remove",
|
|
1305
|
+
resource: "brandMember",
|
|
1306
|
+
action: "delete",
|
|
1307
|
+
}, async () => {
|
|
1308
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1309
|
+
command: "brand-members system-remove",
|
|
1310
|
+
danger: "delete",
|
|
1311
|
+
summary: { memberId: opts.id },
|
|
1312
|
+
})) {
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
await requireClient().deleteSystemBrandMember(opts.id);
|
|
1316
|
+
console.log("OK");
|
|
1317
|
+
});
|
|
1318
|
+
});
|
|
901
1319
|
brandMembersCmd
|
|
902
1320
|
.command("list")
|
|
903
|
-
.description("GET /brand-members")
|
|
1321
|
+
.description("GET /teams/:teamId/brand-members (compat)")
|
|
904
1322
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
905
1323
|
.option("--user <uuid>", "userId 过滤")
|
|
906
1324
|
.action(async (opts) => {
|
|
907
|
-
|
|
908
|
-
|
|
1325
|
+
await withPermissionGate({
|
|
1326
|
+
command: "brand-members list",
|
|
1327
|
+
resource: "brandMember",
|
|
1328
|
+
action: "list",
|
|
1329
|
+
teamId: opts.team,
|
|
1330
|
+
}, async () => {
|
|
1331
|
+
const res = await requireClient().listBrandMembers(opts.team, {
|
|
1332
|
+
userId: opts.user,
|
|
1333
|
+
});
|
|
1334
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1335
|
+
});
|
|
909
1336
|
});
|
|
910
1337
|
brandMembersCmd
|
|
911
1338
|
.command("create")
|
|
912
1339
|
.description("POST /brand-members")
|
|
913
1340
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
914
1341
|
.requiredOption("-j, --json <json>", "{ brandId, userId, role? }")
|
|
915
|
-
.
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
1342
|
+
.option("--dry-run", "预览", false)
|
|
1343
|
+
.option("--apply", "执行写入", false)
|
|
1344
|
+
.action(async (opts) => {
|
|
1345
|
+
await withPermissionGate({
|
|
1346
|
+
command: "brand-members create",
|
|
1347
|
+
resource: "brandMember",
|
|
1348
|
+
action: "create",
|
|
1349
|
+
teamId: opts.team,
|
|
1350
|
+
}, async () => {
|
|
1351
|
+
const body = JSON.parse(opts.json);
|
|
1352
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1353
|
+
command: "brand-members create",
|
|
1354
|
+
danger: "write",
|
|
1355
|
+
summary: { teamId: opts.team, ...body },
|
|
1356
|
+
})) {
|
|
1357
|
+
return;
|
|
1358
|
+
}
|
|
1359
|
+
const res = await requireClient().createBrandMember(opts.team, body);
|
|
1360
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1361
|
+
});
|
|
919
1362
|
});
|
|
920
1363
|
// --- notification-channels ---
|
|
921
1364
|
const notifChannels = program
|
|
@@ -979,56 +1422,147 @@ apiKeys
|
|
|
979
1422
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
980
1423
|
.option("-n, --limit <n>", "limit", "50")
|
|
981
1424
|
.action(async (opts) => {
|
|
982
|
-
|
|
983
|
-
|
|
1425
|
+
await withPermissionGate({
|
|
1426
|
+
command: "api-keys list",
|
|
1427
|
+
resource: "apiKey",
|
|
1428
|
+
action: "list",
|
|
1429
|
+
teamId: opts.team,
|
|
1430
|
+
}, async () => {
|
|
1431
|
+
const res = await requireClient().listApiKeys(opts.team, {
|
|
1432
|
+
limit: Number(opts.limit),
|
|
1433
|
+
});
|
|
1434
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1435
|
+
});
|
|
984
1436
|
});
|
|
985
1437
|
apiKeys
|
|
986
1438
|
.command("create")
|
|
987
1439
|
.description("POST /api-keys")
|
|
988
1440
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
989
1441
|
.requiredOption("-j, --json <json>", "{ name, role, visibleCampaignIds? }")
|
|
990
|
-
.
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1442
|
+
.option("--dry-run", "预览", false)
|
|
1443
|
+
.option("--apply", "执行写入", false)
|
|
1444
|
+
.action(async (opts) => {
|
|
1445
|
+
await withPermissionGate({
|
|
1446
|
+
command: "api-keys create",
|
|
1447
|
+
resource: "apiKey",
|
|
1448
|
+
action: "create",
|
|
1449
|
+
teamId: opts.team,
|
|
1450
|
+
}, async () => {
|
|
1451
|
+
const body = JSON.parse(opts.json);
|
|
1452
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1453
|
+
command: "api-keys create",
|
|
1454
|
+
danger: "secret",
|
|
1455
|
+
summary: { teamId: opts.team, ...body },
|
|
1456
|
+
})) {
|
|
1457
|
+
return;
|
|
1458
|
+
}
|
|
1459
|
+
const res = await requireClient().createApiKey(opts.team, body);
|
|
1460
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1461
|
+
});
|
|
994
1462
|
});
|
|
995
1463
|
apiKeys
|
|
996
1464
|
.command("delete")
|
|
997
1465
|
.description("DELETE /api-keys/:id")
|
|
998
1466
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
999
1467
|
.requiredOption("--key <uuid>", "API Key UUID")
|
|
1000
|
-
.
|
|
1001
|
-
|
|
1002
|
-
|
|
1468
|
+
.option("--dry-run", "预览", false)
|
|
1469
|
+
.option("--apply", "执行写入", false)
|
|
1470
|
+
.action(async (opts) => {
|
|
1471
|
+
await withPermissionGate({
|
|
1472
|
+
command: "api-keys delete",
|
|
1473
|
+
resource: "apiKey",
|
|
1474
|
+
action: "delete",
|
|
1475
|
+
teamId: opts.team,
|
|
1476
|
+
}, async () => {
|
|
1477
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1478
|
+
command: "api-keys delete",
|
|
1479
|
+
danger: "delete",
|
|
1480
|
+
summary: { teamId: opts.team, keyId: opts.key },
|
|
1481
|
+
})) {
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
await requireClient().deleteApiKey(opts.team, opts.key);
|
|
1485
|
+
console.log("OK");
|
|
1486
|
+
});
|
|
1003
1487
|
});
|
|
1004
1488
|
apiKeys
|
|
1005
1489
|
.command("rotate")
|
|
1006
1490
|
.description("POST /api-keys/:id/rotate")
|
|
1007
1491
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1008
1492
|
.requiredOption("--key <uuid>", "API Key UUID")
|
|
1009
|
-
.
|
|
1010
|
-
|
|
1011
|
-
|
|
1493
|
+
.option("--dry-run", "预览", false)
|
|
1494
|
+
.option("--apply", "执行写入", false)
|
|
1495
|
+
.action(async (opts) => {
|
|
1496
|
+
await withPermissionGate({
|
|
1497
|
+
command: "api-keys rotate",
|
|
1498
|
+
resource: "apiKey",
|
|
1499
|
+
action: "update",
|
|
1500
|
+
teamId: opts.team,
|
|
1501
|
+
}, async () => {
|
|
1502
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1503
|
+
command: "api-keys rotate",
|
|
1504
|
+
danger: "secret",
|
|
1505
|
+
summary: { teamId: opts.team, keyId: opts.key },
|
|
1506
|
+
})) {
|
|
1507
|
+
return;
|
|
1508
|
+
}
|
|
1509
|
+
const res = await requireClient().rotateApiKey(opts.team, opts.key);
|
|
1510
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1511
|
+
});
|
|
1012
1512
|
});
|
|
1013
1513
|
apiKeys
|
|
1014
1514
|
.command("batch-revoke")
|
|
1015
1515
|
.description("POST /api-keys/batch/revoke")
|
|
1016
1516
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1017
|
-
.requiredOption("-j, --json <json>", '{ "
|
|
1018
|
-
.
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1517
|
+
.requiredOption("-j, --json <json>", '{ "apiKeyIds": ["uuid1","uuid2"] }')
|
|
1518
|
+
.option("--dry-run", "预览", false)
|
|
1519
|
+
.option("--apply", "执行写入", false)
|
|
1520
|
+
.action(async (opts) => {
|
|
1521
|
+
await withPermissionGate({
|
|
1522
|
+
command: "api-keys batch-revoke",
|
|
1523
|
+
resource: "apiKey",
|
|
1524
|
+
action: "delete",
|
|
1525
|
+
teamId: opts.team,
|
|
1526
|
+
}, async () => {
|
|
1527
|
+
const body = JSON.parse(opts.json);
|
|
1528
|
+
const apiKeyIds = body.apiKeyIds ?? body.ids ?? [];
|
|
1529
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1530
|
+
command: "api-keys batch-revoke",
|
|
1531
|
+
danger: "delete",
|
|
1532
|
+
summary: { teamId: opts.team, apiKeyIds },
|
|
1533
|
+
})) {
|
|
1534
|
+
return;
|
|
1535
|
+
}
|
|
1536
|
+
const res = await requireClient().batchRevokeApiKeys(opts.team, apiKeyIds);
|
|
1537
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1538
|
+
});
|
|
1022
1539
|
});
|
|
1023
1540
|
apiKeys
|
|
1024
1541
|
.command("batch-rotate")
|
|
1025
1542
|
.description("POST /api-keys/batch/rotate")
|
|
1026
1543
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1027
|
-
.requiredOption("-j, --json <json>", '{ "
|
|
1028
|
-
.
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1544
|
+
.requiredOption("-j, --json <json>", '{ "apiKeyIds": ["uuid1","uuid2"] }')
|
|
1545
|
+
.option("--dry-run", "预览", false)
|
|
1546
|
+
.option("--apply", "执行写入", false)
|
|
1547
|
+
.action(async (opts) => {
|
|
1548
|
+
await withPermissionGate({
|
|
1549
|
+
command: "api-keys batch-rotate",
|
|
1550
|
+
resource: "apiKey",
|
|
1551
|
+
action: "update",
|
|
1552
|
+
teamId: opts.team,
|
|
1553
|
+
}, async () => {
|
|
1554
|
+
const body = JSON.parse(opts.json);
|
|
1555
|
+
const apiKeyIds = body.apiKeyIds ?? body.ids ?? [];
|
|
1556
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1557
|
+
command: "api-keys batch-rotate",
|
|
1558
|
+
danger: "secret",
|
|
1559
|
+
summary: { teamId: opts.team, apiKeyIds },
|
|
1560
|
+
})) {
|
|
1561
|
+
return;
|
|
1562
|
+
}
|
|
1563
|
+
const res = await requireClient().batchRotateApiKeys(opts.team, apiKeyIds);
|
|
1564
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1565
|
+
});
|
|
1032
1566
|
});
|
|
1033
1567
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1034
1568
|
// Group C: scattered endpoint additions
|
|
@@ -1083,9 +1617,16 @@ program
|
|
|
1083
1617
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1084
1618
|
.requiredOption("-j, --json <json>", "权限矩阵更新 body")
|
|
1085
1619
|
.action(async (opts) => {
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1620
|
+
await withPermissionGate({
|
|
1621
|
+
command: "permissions-update",
|
|
1622
|
+
resource: "permissionMatrix",
|
|
1623
|
+
action: "update",
|
|
1624
|
+
teamId: opts.team,
|
|
1625
|
+
}, async () => {
|
|
1626
|
+
const body = JSON.parse(opts.json);
|
|
1627
|
+
const res = await requireClient().updatePermissionsMatrix(opts.team, body);
|
|
1628
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1629
|
+
});
|
|
1089
1630
|
});
|
|
1090
1631
|
graph
|
|
1091
1632
|
.command("drilldown")
|
|
@@ -1100,6 +1641,114 @@ graph
|
|
|
1100
1641
|
});
|
|
1101
1642
|
console.log(JSON.stringify(res, null, 2));
|
|
1102
1643
|
});
|
|
1644
|
+
graph
|
|
1645
|
+
.command("v2-ego")
|
|
1646
|
+
.description("GET /graph/v2/ego")
|
|
1647
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1648
|
+
.option("--node-id <uuid>", "graph nodeId")
|
|
1649
|
+
.option("--node-ref <id>", "nodeRefId (e.g. account UUID)")
|
|
1650
|
+
.option("--node-type <type>", "account|team|campaign")
|
|
1651
|
+
.option("--relation-type <type>", "relationType filter")
|
|
1652
|
+
.option("--edge-kind <kind>", "edgeKind filter")
|
|
1653
|
+
.option("-n, --limit <n>", "limit", "100")
|
|
1654
|
+
.action(async (opts) => {
|
|
1655
|
+
const res = await requireClient().getGraphV2Ego(opts.team, {
|
|
1656
|
+
nodeId: opts.nodeId,
|
|
1657
|
+
nodeRefId: opts.nodeRef,
|
|
1658
|
+
nodeType: opts.nodeType,
|
|
1659
|
+
relationType: opts.relationType,
|
|
1660
|
+
edgeKind: opts.edgeKind,
|
|
1661
|
+
limit: Number(opts.limit),
|
|
1662
|
+
});
|
|
1663
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1664
|
+
});
|
|
1665
|
+
graph
|
|
1666
|
+
.command("v2-timeline")
|
|
1667
|
+
.description("GET /graph/v2/timeline")
|
|
1668
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1669
|
+
.option("--node-id <uuid>", "graph nodeId")
|
|
1670
|
+
.option("--node-ref <id>", "nodeRefId")
|
|
1671
|
+
.option("--node-type <type>", "account|team|campaign")
|
|
1672
|
+
.option("--from <iso>", "from ISO time")
|
|
1673
|
+
.option("--to <iso>", "to ISO time")
|
|
1674
|
+
.option("-n, --limit <n>", "limit", "100")
|
|
1675
|
+
.action(async (opts) => {
|
|
1676
|
+
const res = await requireClient().getGraphV2Timeline(opts.team, {
|
|
1677
|
+
nodeId: opts.nodeId,
|
|
1678
|
+
nodeRefId: opts.nodeRef,
|
|
1679
|
+
nodeType: opts.nodeType,
|
|
1680
|
+
from: opts.from,
|
|
1681
|
+
to: opts.to,
|
|
1682
|
+
limit: Number(opts.limit),
|
|
1683
|
+
});
|
|
1684
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1685
|
+
});
|
|
1686
|
+
graph
|
|
1687
|
+
.command("v2-explain")
|
|
1688
|
+
.description("GET /graph/v2/explain/:edgeId")
|
|
1689
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1690
|
+
.requiredOption("--edge-id <id>", "edge UUID")
|
|
1691
|
+
.action(async (opts) => {
|
|
1692
|
+
const res = await requireClient().getGraphV2Explain(opts.team, opts.edgeId);
|
|
1693
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1694
|
+
});
|
|
1695
|
+
graph
|
|
1696
|
+
.command("v2-path")
|
|
1697
|
+
.description("GET /graph/v2/path")
|
|
1698
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1699
|
+
.option("--source-id <uuid>", "source nodeId")
|
|
1700
|
+
.option("--source-ref <id>", "source nodeRefId")
|
|
1701
|
+
.option("--source-type <type>", "source nodeType")
|
|
1702
|
+
.option("--target-id <uuid>", "target nodeId")
|
|
1703
|
+
.option("--target-ref <id>", "target nodeRefId")
|
|
1704
|
+
.option("--target-type <type>", "target nodeType")
|
|
1705
|
+
.option("--max-depth <n>", "maxDepth", "4")
|
|
1706
|
+
.option("-n, --limit <n>", "limit", "50")
|
|
1707
|
+
.action(async (opts) => {
|
|
1708
|
+
const res = await requireClient().getGraphV2Path(opts.team, {
|
|
1709
|
+
sourceNodeId: opts.sourceId,
|
|
1710
|
+
sourceNodeRefId: opts.sourceRef,
|
|
1711
|
+
sourceNodeType: opts.sourceType,
|
|
1712
|
+
targetNodeId: opts.targetId,
|
|
1713
|
+
targetNodeRefId: opts.targetRef,
|
|
1714
|
+
targetNodeType: opts.targetType,
|
|
1715
|
+
maxDepth: Number(opts.maxDepth),
|
|
1716
|
+
limit: Number(opts.limit),
|
|
1717
|
+
});
|
|
1718
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1719
|
+
});
|
|
1720
|
+
graph
|
|
1721
|
+
.command("v2-clusters")
|
|
1722
|
+
.description("GET /graph/v2/clusters")
|
|
1723
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1724
|
+
.option("--scope-ref <id>", "scopeNodeRefId")
|
|
1725
|
+
.option("--scope-type <type>", "scopeNodeType", "account")
|
|
1726
|
+
.option("--min-risk <n>", "minRiskScore", "60")
|
|
1727
|
+
.option("-n, --limit <n>", "limit", "100")
|
|
1728
|
+
.action(async (opts) => {
|
|
1729
|
+
const res = await requireClient().getGraphV2Clusters(opts.team, {
|
|
1730
|
+
scopeNodeRefId: opts.scopeRef,
|
|
1731
|
+
scopeNodeType: opts.scopeType,
|
|
1732
|
+
minRiskScore: Number(opts.minRisk),
|
|
1733
|
+
limit: Number(opts.limit),
|
|
1734
|
+
});
|
|
1735
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1736
|
+
});
|
|
1737
|
+
graph
|
|
1738
|
+
.command("v2-impact")
|
|
1739
|
+
.description("GET /graph/v2/impact")
|
|
1740
|
+
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1741
|
+
.option("--campaign-ref <id>", "campaignNodeRefId")
|
|
1742
|
+
.option("--min-reuse <n>", "minReuseCount", "2")
|
|
1743
|
+
.option("-n, --limit <n>", "limit", "100")
|
|
1744
|
+
.action(async (opts) => {
|
|
1745
|
+
const res = await requireClient().getGraphV2Impact(opts.team, {
|
|
1746
|
+
campaignNodeRefId: opts.campaignRef,
|
|
1747
|
+
minReuseCount: Number(opts.minReuse),
|
|
1748
|
+
limit: Number(opts.limit),
|
|
1749
|
+
});
|
|
1750
|
+
console.log(JSON.stringify(res, null, 2));
|
|
1751
|
+
});
|
|
1103
1752
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
1104
1753
|
// Group D: add filter params to existing list commands
|
|
1105
1754
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -1336,9 +1985,25 @@ brandMembersCmd
|
|
|
1336
1985
|
.description("DELETE /brand-members/:memberId")
|
|
1337
1986
|
.requiredOption("-t, --team <teamId>", "Team UUID")
|
|
1338
1987
|
.requiredOption("-m, --member <memberId>", "BrandMember UUID")
|
|
1339
|
-
.
|
|
1340
|
-
|
|
1341
|
-
|
|
1988
|
+
.option("--dry-run", "预览", false)
|
|
1989
|
+
.option("--apply", "执行写入", false)
|
|
1990
|
+
.action(async (opts) => {
|
|
1991
|
+
await withPermissionGate({
|
|
1992
|
+
command: "brand-members remove",
|
|
1993
|
+
resource: "brandMember",
|
|
1994
|
+
action: "delete",
|
|
1995
|
+
teamId: opts.team,
|
|
1996
|
+
}, async () => {
|
|
1997
|
+
if (!assertApplyOrDryRun(opts, {
|
|
1998
|
+
command: "brand-members remove",
|
|
1999
|
+
danger: "delete",
|
|
2000
|
+
summary: { teamId: opts.team, memberId: opts.member },
|
|
2001
|
+
})) {
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
2004
|
+
const res = await requireClient().deleteBrandMember(opts.team, opts.member);
|
|
2005
|
+
console.log(JSON.stringify(res, null, 2));
|
|
2006
|
+
});
|
|
1342
2007
|
});
|
|
1343
2008
|
// Only parse when run directly (not when imported for testing)
|
|
1344
2009
|
if (!process.env["VITEST"]) {
|