@chainpatrol/cli 0.5.0 → 0.6.0

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
@@ -1,5 +1,18 @@
1
1
  # @chainpatrol/cli
2
2
 
3
+ ## 0.6.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 575b671: Add `chainpatrol orgs list` — lists organizations accessible to the caller along with each org's subscription status and active/automated service flags (reporting, reviewing, protection, takedowns, detection, dark web monitoring). Filter flags compose with AND and are applied server-side:
8
+
9
+ - `--subscription-status <list>` — comma-separated `PROSPECT`, `TRIAL`, `ACTIVE`, `INTEGRATION` (`INACTIVE` is intentionally not reachable through this filter)
10
+ - `--service-active <list>` / `--service-inactive <list>` — services that must be active / inactive
11
+ - `--service-automated <list>` / `--service-manual <list>` — services whose automation must be on / off (`reporting`, `reviewing`, `protection`, `takedowns`)
12
+ - `--query <text>` — partial name match
13
+
14
+ Use cases: "which ACTIVE customers have takedowns enabled but automation off", "which prospects don't have detection turned on yet". The bundled CLI skill gets a new `orgs list` section under the `queues snapshot` heading.
15
+
3
16
  ## 0.5.0
4
17
 
5
18
  ### Minor Changes
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-MXUZR2BV.js";
7
+ } from "./chunk-LLWKCA3H.js";
8
8
  import "./chunk-EEG7T6WT.js";
9
9
  import "./chunk-TFCNKBRC.js";
10
10
  import "./chunk-U73SABXK.js";
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-MXUZR2BV.js";
11
+ } from "./chunk-LLWKCA3H.js";
12
12
  import {
13
13
  DateTime
14
14
  } from "./chunk-TFCNKBRC.js";
@@ -147,6 +147,9 @@ function createApiClient(options) {
147
147
  },
148
148
  runHealthcheck(endpoint, input) {
149
149
  return request(endpoint, input);
150
+ },
151
+ getUserOrgs(input) {
152
+ return request("/user/orgs", input);
150
153
  }
151
154
  };
152
155
  }
@@ -82,7 +82,9 @@ description: |
82
82
  "am I logged in", "list configs", "use the cli", "list reports",
83
83
  "customer reports", "reports reported by customer", "find detection gaps",
84
84
  "org healthcheck", "organization health check", "audit my org",
85
- "what's wrong with org", "review org setup".
85
+ "what's wrong with org", "review org setup", "list orgs", "list organizations",
86
+ "orgs with takedowns off", "automation off across orgs",
87
+ "which customers have X enabled", "service toggles by org".
86
88
  allowed-tools:
87
89
  - Bash
88
90
  - Read
@@ -443,6 +445,51 @@ Guide. Key signals in the response:
443
445
 
444
446
  Use \`--all\` to snapshot every org you have access to instead of a single slug.
445
447
 
448
+ ### \`orgs list\` \u2014 List organizations with subscription status and service toggles
449
+
450
+ Returns every organization the caller can see, with each org's
451
+ subscription status (\`PROSPECT\`, \`TRIAL\`, \`ACTIVE\`, \`INTEGRATION\`) and
452
+ which services are active and automated. Use it to answer questions like
453
+ "which customers have takedowns enabled but automation off?" or "which
454
+ prospects don't have detection turned on yet?" \u2014 filters compose with AND
455
+ and are applied server-side, so one call returns the final list.
456
+
457
+ \`\`\`bash
458
+ chainpatrol --json orgs list \\
459
+ --subscription-status ACTIVE \\
460
+ --service-active takedowns \\
461
+ --service-manual takedowns
462
+ \`\`\`
463
+
464
+ Filter flags (all optional, all comma-separated lists):
465
+
466
+ - \`--query <text>\` partial name match (substring, case-insensitive)
467
+ - \`--subscription-status <list>\` one or more of \`PROSPECT\`, \`TRIAL\`,
468
+ \`ACTIVE\`, \`INTEGRATION\`. \`INACTIVE\` is intentionally not reachable
469
+ through this filter \u2014 \`orgs list\` only ever returns live customers.
470
+ - \`--service-active <list>\` services that must be active
471
+ - \`--service-inactive <list>\` services that must be inactive
472
+ - \`--service-automated <list>\` services whose automation must be ON
473
+ (\`reporting\`, \`reviewing\`, \`protection\`, \`takedowns\` \u2014 the four
474
+ with an automation toggle)
475
+ - \`--service-manual <list>\` services whose automation must be OFF
476
+ (same four)
477
+
478
+ Service names: \`reporting\`, \`reviewing\`, \`protection\`, \`takedowns\`,
479
+ \`detection\`, \`darkWebMonitoring\`.
480
+
481
+ Customers see only orgs they're a member of. Staff/superuser sessions see
482
+ every matching org. The response is the same in both cases; visibility is
483
+ enforced server-side.
484
+
485
+ #### Use case: finding service configuration gaps across the customer base
486
+
487
+ When the user asks something like "which customers are paying us but don't
488
+ have takedowns automated yet?" or "any orgs running detection without
489
+ takedowns?", reach for \`orgs list\` \u2014 it's the only command that exposes
490
+ service flags across multiple orgs in one call. Run it in \`--json\` mode
491
+ and summarize patterns by service or by subscription tier.
492
+
446
493
  ### \`metrics summary | found | breakdown\` \u2014 Org metrics for spike/drop analysis
447
494
 
448
495
  \`\`\`bash
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  getCliVersion,
14
14
  isSkillInstalled,
15
15
  readInstalledSkillVersion
16
- } from "./chunk-BSK4YHFA.js";
16
+ } from "./chunk-XOXQPUR6.js";
17
17
  import "./chunk-IUZB3DQW.js";
18
18
  import {
19
19
  DateTime
@@ -281,6 +281,31 @@ var HELP = {
281
281
  "--window-hours <n> Hours used for staleness windows"
282
282
  ]
283
283
  },
284
+ orgs: {
285
+ description: "List organizations accessible to you with subscription status and service toggles.",
286
+ usage: "chainpatrol orgs list [filters]",
287
+ examples: [
288
+ "chainpatrol orgs list",
289
+ "chainpatrol orgs list --subscription-status ACTIVE --service-active takedowns --service-manual takedowns"
290
+ ]
291
+ },
292
+ "orgs list": {
293
+ description: "List organizations with their subscription status and per-service active/automated flags. All filters compose with AND.",
294
+ usage: "chainpatrol orgs list [filters]",
295
+ options: [
296
+ "--query <text> Partial name match (substring, case-insensitive)",
297
+ "--subscription-status <l> Comma-separated list (PROSPECT,TRIAL,ACTIVE,INTEGRATION)",
298
+ "--service-active <l> Comma-separated services that must be active",
299
+ "--service-inactive <l> Comma-separated services that must be inactive",
300
+ "--service-automated <l> Services whose automation must be ON (reporting,reviewing,protection,takedowns)",
301
+ "--service-manual <l> Services whose automation must be OFF (reporting,reviewing,protection,takedowns)"
302
+ ],
303
+ examples: [
304
+ "chainpatrol orgs list --subscription-status ACTIVE",
305
+ "chainpatrol orgs list --service-active takedowns --service-manual takedowns",
306
+ "chainpatrol --json orgs list --subscription-status ACTIVE,TRIAL --service-active detection"
307
+ ]
308
+ },
284
309
  healthchecks: {
285
310
  description: "Run organization healthchecks via the public API. Use `list` to see every available check (including planned ones not yet implemented on the backend) and `run` to execute one or all implemented checks.",
286
311
  usage: "chainpatrol healthchecks <list|run <id>|run --all>",
@@ -369,6 +394,7 @@ function getTopLevelHelp() {
369
394
  " metrics Query organization metrics and breakdowns",
370
395
  " reports Create and list reports from terminal",
371
396
  " queues Snapshot operations review/takedown queues",
397
+ " orgs List organizations and filter by status/services",
372
398
  " presets Run saved workflows for common jobs",
373
399
  " setup Install Claude Code skill and shell completions",
374
400
  " uninstall Remove Claude Code skill and shell completions",
@@ -538,6 +564,7 @@ var COMMANDS = [
538
564
  "metrics",
539
565
  "reports",
540
566
  "queues",
567
+ "orgs",
541
568
  "presets",
542
569
  "setup",
543
570
  "uninstall",
@@ -614,6 +641,12 @@ ${getTopLevelHelp()}
614
641
  contactInfo: { type: "string" },
615
642
  externalSubmissionLink: { type: "string" },
616
643
  payloadFile: { type: "string" },
644
+ subscriptionStatus: { type: "string" },
645
+ serviceActive: { type: "string" },
646
+ serviceInactive: { type: "string" },
647
+ serviceAutomated: { type: "string" },
648
+ serviceManual: { type: "string" },
649
+ query: { type: "string" },
617
650
  help: { type: "boolean", shortFlag: "h" },
618
651
  version: { type: "boolean", shortFlag: "V" },
619
652
  quiet: { type: "boolean", default: false, shortFlag: "q" },
@@ -717,12 +750,12 @@ function parseAttachmentUrls() {
717
750
  }
718
751
  async function handleConfigsList(org) {
719
752
  if (jsonMode) {
720
- const { listConfigsJson } = await import("./list-json-WTMYLZGY.js");
753
+ const { listConfigsJson } = await import("./list-json-LEKCCWQU.js");
721
754
  await listConfigsJson({ org });
722
755
  return;
723
756
  }
724
757
  const { render } = await import("ink");
725
- const { default: ConfigsList } = await import("./list-GEMCFDD5.js");
758
+ const { default: ConfigsList } = await import("./list-5ENZAOFL.js");
726
759
  const { default: React } = await import("react");
727
760
  render(React.createElement(ConfigsList, { org }));
728
761
  }
@@ -820,7 +853,7 @@ async function main() {
820
853
  case "detections": {
821
854
  const org = await resolveOrg();
822
855
  if (subcommand === "healthcheck") {
823
- const { runDetectionsHealthcheck } = await import("./healthcheck-KAONRGSS.js");
856
+ const { runDetectionsHealthcheck } = await import("./healthcheck-AQUXVKAO.js");
824
857
  await runDetectionsHealthcheck({
825
858
  org,
826
859
  source: cli.flags.source,
@@ -835,7 +868,7 @@ async function main() {
835
868
  break;
836
869
  }
837
870
  if (subcommand === "validate") {
838
- const { runDetectionsValidate } = await import("./validate-BJFEKI2N.js");
871
+ const { runDetectionsValidate } = await import("./validate-27RUCN7R.js");
839
872
  await runDetectionsValidate({
840
873
  org,
841
874
  source: cli.flags.source,
@@ -850,7 +883,7 @@ async function main() {
850
883
  break;
851
884
  }
852
885
  if (subcommand === "drift") {
853
- const { runDetectionsDrift } = await import("./drift-DZ6A7JL5.js");
886
+ const { runDetectionsDrift } = await import("./drift-VOKQJ36G.js");
854
887
  await runDetectionsDrift({
855
888
  org,
856
889
  source: cli.flags.source,
@@ -864,7 +897,7 @@ async function main() {
864
897
  break;
865
898
  }
866
899
  if (subcommand === "run") {
867
- const { runDetectionsRun } = await import("./run-43CC5AXR.js");
900
+ const { runDetectionsRun } = await import("./run-OT2X46GT.js");
868
901
  await runDetectionsRun({
869
902
  org,
870
903
  configId: cli.flags.configId,
@@ -883,7 +916,7 @@ async function main() {
883
916
  break;
884
917
  }
885
918
  if (action === "run") {
886
- const { runDetectionsRun } = await import("./run-43CC5AXR.js");
919
+ const { runDetectionsRun } = await import("./run-OT2X46GT.js");
887
920
  await runDetectionsRun({
888
921
  org,
889
922
  configId: cli.flags.configId,
@@ -901,7 +934,7 @@ async function main() {
901
934
  throw new Error("detections configs update requires --config-id");
902
935
  }
903
936
  const configPatch = getConfigPatchFromSetFlags();
904
- const { runDetectionsConfigsUpdate } = await import("./configs-update-RPN32YTL.js");
937
+ const { runDetectionsConfigsUpdate } = await import("./configs-update-VROBC2HI.js");
905
938
  await runDetectionsConfigsUpdate({
906
939
  org,
907
940
  configId: cli.flags.configId,
@@ -928,7 +961,7 @@ async function main() {
928
961
  case "metrics": {
929
962
  const org = await resolveOrg();
930
963
  if (subcommand === "summary") {
931
- const { runMetricsSummary } = await import("./summary-6NCA7PDP.js");
964
+ const { runMetricsSummary } = await import("./summary-JOCABBCO.js");
932
965
  await runMetricsSummary({
933
966
  org,
934
967
  from: cli.flags.from,
@@ -940,7 +973,7 @@ async function main() {
940
973
  break;
941
974
  }
942
975
  if (subcommand === "found") {
943
- const { runMetricsFound } = await import("./found-AOPBSLRD.js");
976
+ const { runMetricsFound } = await import("./found-A5HRTJCJ.js");
944
977
  await runMetricsFound({
945
978
  org,
946
979
  from: cli.flags.from,
@@ -957,7 +990,7 @@ async function main() {
957
990
  if (!by || !["day", "type", "brand"].includes(by)) {
958
991
  throw new Error("metrics breakdown requires --by <day|type|brand>");
959
992
  }
960
- const { runMetricsBreakdown } = await import("./breakdown-EBSACUST.js");
993
+ const { runMetricsBreakdown } = await import("./breakdown-AX6QNTQH.js");
961
994
  await runMetricsBreakdown({
962
995
  org,
963
996
  by,
@@ -977,7 +1010,7 @@ async function main() {
977
1010
  case "reports": {
978
1011
  if (subcommand === "list") {
979
1012
  const org = await resolveOrg();
980
- const { runReportsList } = await import("./list-MWDFCHMJ.js");
1013
+ const { runReportsList } = await import("./list-CVFXTKNX.js");
981
1014
  await runReportsList({
982
1015
  org,
983
1016
  limit: cli.flags.limit,
@@ -993,7 +1026,7 @@ async function main() {
993
1026
  }
994
1027
  if (subcommand === "create") {
995
1028
  const org = await tryResolveOrg();
996
- const { runReportsCreate } = await import("./create-QP3M7EZM.js");
1029
+ const { runReportsCreate } = await import("./create-XTCUNT2C.js");
997
1030
  await runReportsCreate({
998
1031
  org,
999
1032
  title: cli.flags.title,
@@ -1017,7 +1050,7 @@ async function main() {
1017
1050
  }
1018
1051
  case "queues": {
1019
1052
  if (subcommand === "snapshot") {
1020
- const { runQueuesSnapshot } = await import("./snapshot-E3TPZOKT.js");
1053
+ const { runQueuesSnapshot } = await import("./snapshot-4QR4I67P.js");
1021
1054
  await runQueuesSnapshot({
1022
1055
  org: cli.flags.org,
1023
1056
  all: cli.flags.all,
@@ -1033,9 +1066,29 @@ async function main() {
1033
1066
  subcommand ? `Unknown subcommand: queues ${subcommand}${hint ? `. Did you mean "queues ${hint}"?` : ""}` : "Usage: chainpatrol queues snapshot [--org <slug>|--all]"
1034
1067
  );
1035
1068
  }
1069
+ case "orgs": {
1070
+ if (subcommand === "list") {
1071
+ const { runOrgsList } = await import("./list-CGRHTFAS.js");
1072
+ await runOrgsList({
1073
+ query: cli.flags.query,
1074
+ subscriptionStatus: cli.flags.subscriptionStatus,
1075
+ serviceActive: cli.flags.serviceActive,
1076
+ serviceInactive: cli.flags.serviceInactive,
1077
+ serviceAutomated: cli.flags.serviceAutomated,
1078
+ serviceManual: cli.flags.serviceManual,
1079
+ json: jsonMode,
1080
+ outputFormat: cliContext.outputFormat
1081
+ });
1082
+ break;
1083
+ }
1084
+ const hint = subcommand ? suggest(subcommand, ["list"]) : null;
1085
+ throw new Error(
1086
+ subcommand ? `Unknown subcommand: orgs ${subcommand}${hint ? `. Did you mean "orgs ${hint}"?` : ""}` : "Usage: chainpatrol orgs list [--subscription-status <list>] [--service-active <list>] [--service-automated <list>] ..."
1087
+ );
1088
+ }
1036
1089
  case "healthchecks": {
1037
1090
  if (subcommand === "list") {
1038
- const { runHealthchecksList } = await import("./list-UW63DIKX.js");
1091
+ const { runHealthchecksList } = await import("./list-PLZ67PNY.js");
1039
1092
  await runHealthchecksList({
1040
1093
  json: jsonMode,
1041
1094
  outputFormat: cliContext.outputFormat
@@ -1049,7 +1102,7 @@ async function main() {
1049
1102
  thresholds.minResults = cli.flags.minResults;
1050
1103
  if (cli.flags.lookbackHours !== void 0)
1051
1104
  thresholds.lookbackHours = cli.flags.lookbackHours;
1052
- const { runHealthchecksRun } = await import("./run-64SBCL4R.js");
1105
+ const { runHealthchecksRun } = await import("./run-MS5SA5YL.js");
1053
1106
  await runHealthchecksRun({
1054
1107
  org,
1055
1108
  id: action,
@@ -1067,7 +1120,7 @@ async function main() {
1067
1120
  }
1068
1121
  case "presets": {
1069
1122
  if (subcommand === "list") {
1070
- const { runPresetsList } = await import("./list-LN6NOZIJ.js");
1123
+ const { runPresetsList } = await import("./list-EYRN5JYC.js");
1071
1124
  await runPresetsList({ outputFormat: cliContext.outputFormat });
1072
1125
  break;
1073
1126
  }
@@ -1078,7 +1131,7 @@ async function main() {
1078
1131
  );
1079
1132
  }
1080
1133
  const org = await resolveOrg();
1081
- const { runPresetsRun } = await import("./run-MH5RYPWA.js");
1134
+ const { runPresetsRun } = await import("./run-YHDUUP66.js");
1082
1135
  await runPresetsRun({
1083
1136
  presetId: action,
1084
1137
  org,
@@ -1095,12 +1148,12 @@ async function main() {
1095
1148
  case "setup":
1096
1149
  case "install":
1097
1150
  case "i": {
1098
- const { setupSkill } = await import("./setup-skill-NQIZBJMR.js");
1151
+ const { setupSkill } = await import("./setup-skill-BTR2IZ4E.js");
1099
1152
  setupSkill({ json: jsonMode });
1100
1153
  break;
1101
1154
  }
1102
1155
  case "uninstall": {
1103
- const { uninstallSkill } = await import("./setup-skill-NQIZBJMR.js");
1156
+ const { uninstallSkill } = await import("./setup-skill-BTR2IZ4E.js");
1104
1157
  uninstallSkill({ json: jsonMode });
1105
1158
  break;
1106
1159
  }
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VFT3TD3E.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-MXUZR2BV.js";
10
+ } from "./chunk-LLWKCA3H.js";
11
11
  import "./chunk-EEG7T6WT.js";
12
12
  import "./chunk-TFCNKBRC.js";
13
13
  import "./chunk-U73SABXK.js";
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-MXUZR2BV.js";
11
+ } from "./chunk-LLWKCA3H.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-MXUZR2BV.js";
11
+ } from "./chunk-LLWKCA3H.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-MXUZR2BV.js";
7
+ } from "./chunk-LLWKCA3H.js";
8
8
  import "./chunk-EEG7T6WT.js";
9
9
  import {
10
10
  DateTime
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-MXUZR2BV.js";
11
+ } from "./chunk-LLWKCA3H.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-JCMWDZYY.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-MXUZR2BV.js";
7
+ } from "./chunk-LLWKCA3H.js";
8
8
  import {
9
9
  AuthCorruptedError,
10
10
  AuthExpiredError,
@@ -0,0 +1,182 @@
1
+ import {
2
+ CliExitError,
3
+ ExitCode
4
+ } from "./chunk-E2LAMILJ.js";
5
+ import {
6
+ printOutput,
7
+ toCsvRows
8
+ } from "./chunk-VFT3TD3E.js";
9
+ import {
10
+ createApiClient
11
+ } from "./chunk-LLWKCA3H.js";
12
+ import "./chunk-EEG7T6WT.js";
13
+ import "./chunk-TFCNKBRC.js";
14
+ import "./chunk-U73SABXK.js";
15
+
16
+ // src/commands/orgs/list.ts
17
+ var VALID_SUBSCRIPTION_STATUSES = /* @__PURE__ */ new Set([
18
+ "PROSPECT",
19
+ "TRIAL",
20
+ "ACTIVE",
21
+ "INTEGRATION"
22
+ ]);
23
+ var DUAL_SERVICES = ["reporting", "reviewing", "protection", "takedowns"];
24
+ var SINGLE_SERVICES = ["detection", "darkWebMonitoring"];
25
+ var ALL_SERVICES = [...DUAL_SERVICES, ...SINGLE_SERVICES];
26
+ function splitCsv(value) {
27
+ if (!value) return [];
28
+ return value.split(",").map((part) => part.trim()).filter(Boolean);
29
+ }
30
+ function parseSubscriptionStatuses(raw) {
31
+ const parts = splitCsv(raw);
32
+ if (parts.length === 0) return void 0;
33
+ const result = [];
34
+ for (const part of parts) {
35
+ const upper = part.toUpperCase();
36
+ if (!VALID_SUBSCRIPTION_STATUSES.has(upper)) {
37
+ throw new CliExitError(
38
+ `Unknown subscription status: '${part}'. Valid values: ${Array.from(VALID_SUBSCRIPTION_STATUSES).join(", ")}.`,
39
+ ExitCode.USAGE
40
+ );
41
+ }
42
+ result.push(upper);
43
+ }
44
+ return result;
45
+ }
46
+ function parseServiceList(raw, flagName) {
47
+ const parts = splitCsv(raw);
48
+ const result = [];
49
+ for (const part of parts) {
50
+ if (!ALL_SERVICES.includes(part)) {
51
+ throw new CliExitError(
52
+ `Unknown service '${part}' in --${flagName}. Valid services: ${ALL_SERVICES.join(", ")}.`,
53
+ ExitCode.USAGE
54
+ );
55
+ }
56
+ result.push(part);
57
+ }
58
+ return result;
59
+ }
60
+ function requireDualService(service, flagName) {
61
+ if (!DUAL_SERVICES.includes(service)) {
62
+ throw new CliExitError(
63
+ `Service '${service}' does not have an automation toggle and can't be used with --${flagName}. Use --service-active / --service-inactive instead.`,
64
+ ExitCode.USAGE
65
+ );
66
+ }
67
+ return service;
68
+ }
69
+ function buildServicesFilter(options) {
70
+ const filters = {};
71
+ const activeSetBy = /* @__PURE__ */ new Map();
72
+ const automatedSetBy = /* @__PURE__ */ new Map();
73
+ function setActive(service, active, flagName) {
74
+ const previous = activeSetBy.get(service);
75
+ if (previous && previous !== flagName) {
76
+ throw new CliExitError(
77
+ `Conflicting flags for service '${service}': --${previous} and --${flagName} can't both be passed.`,
78
+ ExitCode.USAGE
79
+ );
80
+ }
81
+ activeSetBy.set(service, flagName);
82
+ const existing = filters[service] ?? {};
83
+ filters[service] = { ...existing, active };
84
+ }
85
+ function setAutomated(service, automated, flagName) {
86
+ const previous = automatedSetBy.get(service);
87
+ if (previous && previous !== flagName) {
88
+ throw new CliExitError(
89
+ `Conflicting flags for service '${service}': --${previous} and --${flagName} can't both be passed.`,
90
+ ExitCode.USAGE
91
+ );
92
+ }
93
+ automatedSetBy.set(service, flagName);
94
+ const existing = filters[service] ?? {};
95
+ filters[service] = { ...existing, automated };
96
+ }
97
+ for (const service of parseServiceList(options.serviceActive, "service-active")) {
98
+ setActive(service, true, "service-active");
99
+ }
100
+ for (const service of parseServiceList(options.serviceInactive, "service-inactive")) {
101
+ setActive(service, false, "service-inactive");
102
+ }
103
+ for (const service of parseServiceList(options.serviceAutomated, "service-automated")) {
104
+ setAutomated(
105
+ requireDualService(service, "service-automated"),
106
+ true,
107
+ "service-automated"
108
+ );
109
+ }
110
+ for (const service of parseServiceList(options.serviceManual, "service-manual")) {
111
+ setAutomated(requireDualService(service, "service-manual"), false, "service-manual");
112
+ }
113
+ return Object.keys(filters).length > 0 ? filters : void 0;
114
+ }
115
+ function summarizeServices(org) {
116
+ const parts = [];
117
+ for (const service of DUAL_SERVICES) {
118
+ const entry = org.services[service];
119
+ if (!entry.active) continue;
120
+ parts.push(`${service}=${entry.automated ? "auto" : "manual"}`);
121
+ }
122
+ for (const service of SINGLE_SERVICES) {
123
+ if (org.services[service].active) parts.push(service);
124
+ }
125
+ return parts.length > 0 ? parts.join(" ") : "(no services enabled)";
126
+ }
127
+ async function runOrgsList(options) {
128
+ const client = options.apiClient ?? createApiClient();
129
+ const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
130
+ const input = {
131
+ query: options.query ?? "",
132
+ subscriptionStatus: parseSubscriptionStatuses(options.subscriptionStatus),
133
+ services: buildServicesFilter(options)
134
+ };
135
+ const result = await client.getUserOrgs(input);
136
+ const organizations = result.organizations;
137
+ printOutput({
138
+ outputFormat,
139
+ json: result,
140
+ markdown: [
141
+ `# Organizations (${organizations.length})`,
142
+ "",
143
+ ...organizations.map(
144
+ (org) => `- \`${org.slug}\` \u2014 ${org.name} \u2014 ${org.subscriptionStatus} \u2014 ${summarizeServices(org)}`
145
+ )
146
+ ].join("\n"),
147
+ csv: toCsvRows(
148
+ organizations.map((org) => ({
149
+ slug: org.slug,
150
+ name: org.name,
151
+ subscriptionStatus: org.subscriptionStatus,
152
+ reportingActive: org.services.reporting.active,
153
+ reportingAutomated: org.services.reporting.automated,
154
+ reviewingActive: org.services.reviewing.active,
155
+ reviewingAutomated: org.services.reviewing.automated,
156
+ protectionActive: org.services.protection.active,
157
+ protectionAutomated: org.services.protection.automated,
158
+ takedownsActive: org.services.takedowns.active,
159
+ takedownsAutomated: org.services.takedowns.automated,
160
+ detectionActive: org.services.detection.active,
161
+ darkWebMonitoringActive: org.services.darkWebMonitoring.active
162
+ }))
163
+ ),
164
+ human: () => {
165
+ if (organizations.length === 0) {
166
+ console.log("No organizations matched the filter.");
167
+ return;
168
+ }
169
+ console.log(`Found ${organizations.length} organization(s):`);
170
+ const slugWidth = Math.max(...organizations.map((org) => org.slug.length), 4);
171
+ const nameWidth = Math.max(...organizations.map((org) => org.name.length), 4);
172
+ for (const org of organizations) {
173
+ console.log(
174
+ ` ${org.slug.padEnd(slugWidth)} ${org.name.padEnd(nameWidth)} ${org.subscriptionStatus.padEnd(11)} ${summarizeServices(org)}`
175
+ );
176
+ }
177
+ }
178
+ });
179
+ }
180
+ export {
181
+ runOrgsList
182
+ };
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-MXUZR2BV.js";
11
+ } from "./chunk-LLWKCA3H.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  PRESETS
3
- } from "./chunk-PIYOWGBZ.js";
3
+ } from "./chunk-AGXMZFUU.js";
4
4
  import "./chunk-E2LAMILJ.js";
5
5
  import {
6
6
  printOutput,
7
7
  toCsvRows
8
8
  } from "./chunk-VFT3TD3E.js";
9
- import "./chunk-MXUZR2BV.js";
9
+ import "./chunk-LLWKCA3H.js";
10
10
  import "./chunk-EEG7T6WT.js";
11
11
  import "./chunk-TFCNKBRC.js";
12
12
  import "./chunk-U73SABXK.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-MXUZR2BV.js";
7
+ } from "./chunk-LLWKCA3H.js";
8
8
  import "./chunk-EEG7T6WT.js";
9
9
  import "./chunk-TFCNKBRC.js";
10
10
  import "./chunk-U73SABXK.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApiClient
3
- } from "./chunk-MXUZR2BV.js";
3
+ } from "./chunk-LLWKCA3H.js";
4
4
  import "./chunk-EEG7T6WT.js";
5
5
  import "./chunk-TFCNKBRC.js";
6
6
  import "./chunk-U73SABXK.js";
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-MXUZR2BV.js";
11
+ } from "./chunk-LLWKCA3H.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-MXUZR2BV.js";
11
+ } from "./chunk-LLWKCA3H.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  getPresetDefinition,
3
3
  runPreset
4
- } from "./chunk-PIYOWGBZ.js";
4
+ } from "./chunk-AGXMZFUU.js";
5
5
  import {
6
6
  CliExitError,
7
7
  ExitCode
8
8
  } from "./chunk-E2LAMILJ.js";
9
9
  import "./chunk-VFT3TD3E.js";
10
- import "./chunk-MXUZR2BV.js";
10
+ import "./chunk-LLWKCA3H.js";
11
11
  import "./chunk-EEG7T6WT.js";
12
12
  import "./chunk-TFCNKBRC.js";
13
13
  import "./chunk-U73SABXK.js";
@@ -6,7 +6,7 @@ import {
6
6
  readInstalledSkillVersion,
7
7
  setupSkill,
8
8
  uninstallSkill
9
- } from "./chunk-BSK4YHFA.js";
9
+ } from "./chunk-XOXQPUR6.js";
10
10
  import "./chunk-IUZB3DQW.js";
11
11
  export {
12
12
  getBundledSkillContent,
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-MXUZR2BV.js";
11
+ } from "./chunk-LLWKCA3H.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-MXUZR2BV.js";
7
+ } from "./chunk-LLWKCA3H.js";
8
8
  import "./chunk-EEG7T6WT.js";
9
9
  import "./chunk-TFCNKBRC.js";
10
10
  import "./chunk-U73SABXK.js";
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-MXUZR2BV.js";
11
+ } from "./chunk-LLWKCA3H.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@chainpatrol/cli",
3
3
  "description": "The official ChainPatrol CLI — terminal interface for threat detection",
4
4
  "author": "Umar Ahmed <umar@chainpatrol.io>",
5
- "version": "0.5.0",
5
+ "version": "0.6.0",
6
6
  "license": "UNLICENSED",
7
7
  "homepage": "https://chainpatrol.com/docs/cli",
8
8
  "keywords": [