@chainpatrol/cli 0.13.0 → 0.14.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/{breakdown-B6QUGYI6.js → breakdown-K5Q6TDKE.js} +1 -1
  3. package/dist/{check-P5JJJNEX.js → check-NJH7BUIM.js} +1 -1
  4. package/dist/{chunk-BQDMAEQU.js → chunk-6X5BFFXA.js} +27 -0
  5. package/dist/{chunk-DJVB73FL.js → chunk-BIKTBRER.js} +1 -1
  6. package/dist/{chunk-OSRFCVBT.js → chunk-YZEMTNCX.js} +26 -2
  7. package/dist/cli.js +41 -37
  8. package/dist/{configs-update-3ZVXGADV.js → configs-update-ON67NMXA.js} +1 -1
  9. package/dist/{create-DFH2G7RH.js → create-W5S2ZPVC.js} +1 -1
  10. package/dist/{drift-T74V2C3J.js → drift-ZOLDE24A.js} +1 -1
  11. package/dist/{found-NXAWEIEK.js → found-M5MX4VXK.js} +1 -1
  12. package/dist/{healthcheck-HZYEIIOR.js → healthcheck-PFYP6PNW.js} +1 -1
  13. package/dist/{list-PBAXQSL5.js → list-5TMGSHFC.js} +1 -1
  14. package/dist/{list-QUFY4IP7.js → list-AHWEE3YZ.js} +1 -1
  15. package/dist/{list-IYJBKE2F.js → list-B5KLEFHA.js} +1 -1
  16. package/dist/{list-ZFPML3ZR.js → list-FV5IYCG4.js} +1 -1
  17. package/dist/{list-23QQYAV5.js → list-GKC7PEU5.js} +1 -1
  18. package/dist/{list-YCT6DYDI.js → list-HGYBEYOV.js} +1 -1
  19. package/dist/{list-YRJPN4PI.js → list-ISP5JL4M.js} +1 -1
  20. package/dist/{list-WX5G3IOF.js → list-LNNIEVB4.js} +2 -2
  21. package/dist/{list-WJQT2XLE.js → list-TUMVDWIZ.js} +1 -1
  22. package/dist/{list-I7SBGA52.js → list-VP5IY2SZ.js} +1 -1
  23. package/dist/{list-EIVPGMWJ.js → list-Z7RJWZH5.js} +1 -1
  24. package/dist/{list-json-T2Z7NV7G.js → list-json-QC6X25LI.js} +1 -1
  25. package/dist/{organization-GS5LFIWO.js → organization-VSQQAGBS.js} +40 -23
  26. package/dist/{run-CJGN6TX4.js → run-HRFJORDS.js} +1 -1
  27. package/dist/{run-BHEXWBAS.js → run-KDNQIR5E.js} +2 -2
  28. package/dist/{run-MSO6IXEV.js → run-VHFXOCGS.js} +1 -1
  29. package/dist/{search-WPSKQGUS.js → search-NOSQW75S.js} +1 -1
  30. package/dist/{setup-skill-TPMTULHJ.js → setup-skill-Q6PCFIDE.js} +1 -1
  31. package/dist/{snapshot-ZWAHEG64.js → snapshot-55UV26NQ.js} +1 -1
  32. package/dist/{summary-CV657J5V.js → summary-QUCLZYX6.js} +1 -1
  33. package/dist/{validate-SM5DD7I2.js → validate-TPG47SYS.js} +1 -1
  34. package/dist/{whoami-DH5HVLFN.js → whoami-Y3ZJGN4O.js} +1 -1
  35. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,34 @@
1
1
  # @chainpatrol/cli
2
2
 
3
+ ## 0.14.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 339729c: Add an `--include` filter to `chainpatrol metrics organization` (backed
8
+ by a new optional `include` field on `GET /organization/metrics`) so
9
+ callers can ask for just the metrics they care about instead of paying
10
+ for the full ~11-aggregate fan-out.
11
+
12
+ `--include` (or `include: [...]` in the JSON body) accepts a comma list
13
+ of any of: `reports`, `newThreats`, `threatsWatchlisted`,
14
+ `takedownsFiled`, `takedownsCompleted`, `domainThreats`,
15
+ `twitterThreats`, `telegramThreats`, `otherThreats`, `blockedByType`,
16
+ `blockedByDay`. The server runs only those Prisma aggregates;
17
+ unrequested fields come back as `null`. Omitting `--include` (or
18
+ passing an empty list) preserves the existing "compute everything"
19
+ behavior.
20
+
21
+ This is the targeted follow-up to the 3-month-default change for the
22
+ same 503 issue: agents that only need takedown counts can now skip the
23
+ nine other counts entirely, keeping each call well inside the platform
24
+ function cap and cheap enough to run repeatedly per org.
25
+
26
+ The response schema's metric fields and `blockedByType`/`blockedByDay`
27
+ arrays are now nullable to express "not included" — TypeScript callers
28
+ of the CLI's `OrganizationMetricsResult` that previously read
29
+ `result.metrics.reports` as `number` will need `?? 0` (or to opt in to
30
+ `--include` and check for `null` explicitly).
31
+
3
32
  ## 0.13.0
4
33
 
5
34
  ### Minor Changes
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VFT3TD3E.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-OSRFCVBT.js";
10
+ } from "./chunk-YZEMTNCX.js";
11
11
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -700,6 +700,33 @@ the full customer-facing dashboard slice (reports, new threats,
700
700
  watchlisted, takedowns filed/completed, plus per-type and per-day
701
701
  breakdowns).
702
702
 
703
+ #### \`--include\` \u2014 only compute the metrics you actually need
704
+
705
+ \`metrics organization\` runs one Prisma aggregate per requested field.
706
+ By default all 11 are computed in parallel. Pass \`--include\` (or
707
+ \`include: [\u2026]\` in the JSON body) with the comma-separated subset you
708
+ care about to skip the rest \u2014 the unrequested fields come back as
709
+ \`null\` instead of a number, and the server never runs those queries:
710
+
711
+ \`\`\`bash
712
+ # Only takedowns \u2014 one of the cheap shapes
713
+ chainpatrol --json metrics organization --org <slug> \\
714
+ --include takedownsFiled,takedownsCompleted
715
+
716
+ # Time series only, no scalar counts
717
+ chainpatrol --json metrics organization --org <slug> --include blockedByDay
718
+ \`\`\`
719
+
720
+ Allowed values: \`reports\`, \`newThreats\`, \`threatsWatchlisted\`,
721
+ \`takedownsFiled\`, \`takedownsCompleted\`, \`domainThreats\`,
722
+ \`twitterThreats\`, \`telegramThreats\`, \`otherThreats\`,
723
+ \`blockedByType\`, \`blockedByDay\`.
724
+
725
+ Use this whenever the user's question is specific ("how many takedowns
726
+ did we file last month?"). It is the lowest-effort way to make a
727
+ metrics call cheap enough to run repeatedly. Pair it with an explicit
728
+ date window for the best behavior.
729
+
703
730
  #### "Across all orgs" via an API key \u2014 what's actually possible
704
731
 
705
732
  A ChainPatrol API key is scoped to a single organization. The
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import {
13
13
  DateTime
14
14
  } from "./chunk-TFCNKBRC.js";
@@ -45,6 +45,19 @@ var TAKEDOWN_SORT_KEYS = [
45
45
  "assigneeId",
46
46
  "brandId"
47
47
  ];
48
+ var ORGANIZATION_METRICS_INCLUDE_FIELDS = [
49
+ "reports",
50
+ "newThreats",
51
+ "threatsWatchlisted",
52
+ "takedownsFiled",
53
+ "takedownsCompleted",
54
+ "domainThreats",
55
+ "twitterThreats",
56
+ "telegramThreats",
57
+ "otherThreats",
58
+ "blockedByType",
59
+ "blockedByDay"
60
+ ];
48
61
  var REQUEST_TIMEOUT_MS = 3e4;
49
62
  function defaultGetCredential() {
50
63
  const envKey = process.env.CHAINPATROL_API_KEY;
@@ -71,7 +84,16 @@ function createApiClient(options) {
71
84
  const headers = buildAuthHeaders();
72
85
  const search = new URLSearchParams();
73
86
  for (const [key, value] of Object.entries(query)) {
74
- if (value !== void 0 && value !== "") {
87
+ if (value === void 0) continue;
88
+ if (Array.isArray(value)) {
89
+ for (const item of value) {
90
+ if (item !== void 0 && item !== "") {
91
+ search.append(key, item);
92
+ }
93
+ }
94
+ continue;
95
+ }
96
+ if (value !== "") {
75
97
  search.set(key, value);
76
98
  }
77
99
  }
@@ -258,7 +280,8 @@ function createApiClient(options) {
258
280
  organizationSlug: input.organizationSlug,
259
281
  brandSlug: input.brandSlug,
260
282
  startDate: parseIsoDateString(input.startDate),
261
- endDate: parseIsoDateString(input.endDate)
283
+ endDate: parseIsoDateString(input.endDate),
284
+ include: input.include
262
285
  });
263
286
  },
264
287
  listHealthchecks() {
@@ -378,5 +401,6 @@ export {
378
401
  TAKEDOWN_STATUSES,
379
402
  LIVENESS_STATUSES,
380
403
  TAKEDOWN_SORT_KEYS,
404
+ ORGANIZATION_METRICS_INCLUDE_FIELDS,
381
405
  createApiClient
382
406
  };
package/dist/cli.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  getCliVersion,
14
14
  isSkillInstalled,
15
15
  readInstalledSkillVersion
16
- } from "./chunk-BQDMAEQU.js";
16
+ } from "./chunk-6X5BFFXA.js";
17
17
  import "./chunk-NQT77IB7.js";
18
18
  import {
19
19
  DateTime
@@ -263,19 +263,21 @@ var HELP = {
263
263
  ]
264
264
  },
265
265
  "metrics organization": {
266
- description: "Get organization metrics via the documented GET /organization/metrics endpoint. Works with both API key auth (org derived from key) and session auth (pass --org). Defaults to the trailing 3 months if no date range is given.",
267
- usage: "chainpatrol metrics organization [--org <slug>] [--from ISO --to ISO|--this-week]",
266
+ description: "Get organization metrics via the documented GET /organization/metrics endpoint. Works with both API key auth (org derived from key) and session auth (pass --org). Defaults to the trailing 3 months if no date range is given. Use --include to skip aggregates the caller doesn't need \u2014 fields not included come back as null.",
267
+ usage: "chainpatrol metrics organization [--org <slug>] [--from ISO --to ISO|--this-week] [--include <list>]",
268
268
  options: [
269
269
  "--org <slug> Organization slug (omit when using an API key)",
270
270
  "--brand-slug <slug> Filter by brand slug (requires brands-metrics flag)",
271
271
  "--from <iso> Start date (ISO 8601, e.g. 2026-01-01)",
272
272
  "--to <iso> End date (ISO 8601)",
273
- "--this-week Use the current ISO week (overrides default)"
273
+ "--this-week Use the current ISO week (overrides default)",
274
+ "--include <list> Comma list of fields to compute: reports,newThreats,threatsWatchlisted,takedownsFiled,takedownsCompleted,domainThreats,twitterThreats,telegramThreats,otherThreats,blockedByType,blockedByDay (omit for all)"
274
275
  ],
275
276
  examples: [
276
- "chainpatrol metrics organization --org acme # defaults to last 3 months",
277
+ "chainpatrol metrics organization --org acme # defaults to last 3 months, all metrics",
278
+ "chainpatrol metrics organization --org acme --include takedownsFiled,takedownsCompleted",
277
279
  "chainpatrol metrics organization --org acme --from 2026-01-01 --to 2026-02-01",
278
- "CHAINPATROL_API_KEY=... chainpatrol metrics organization --json"
280
+ "CHAINPATROL_API_KEY=... chainpatrol metrics organization --json --include reports"
279
281
  ]
280
282
  },
281
283
  "metrics summary": {
@@ -858,6 +860,7 @@ ${getTopLevelHelp()}
858
860
  to: { type: "string" },
859
861
  thisWeek: { type: "boolean", default: false },
860
862
  by: { type: "string" },
863
+ include: { type: "string" },
861
864
  brand: { type: "string" },
862
865
  enable: { type: "boolean", default: false },
863
866
  disable: { type: "boolean", default: false },
@@ -1031,12 +1034,12 @@ function parseAttachmentUrls() {
1031
1034
  }
1032
1035
  async function handleConfigsList(org) {
1033
1036
  if (jsonMode) {
1034
- const { listConfigsJson } = await import("./list-json-T2Z7NV7G.js");
1037
+ const { listConfigsJson } = await import("./list-json-QC6X25LI.js");
1035
1038
  await listConfigsJson({ org });
1036
1039
  return;
1037
1040
  }
1038
1041
  const { render } = await import("ink");
1039
- const { default: ConfigsList } = await import("./list-IYJBKE2F.js");
1042
+ const { default: ConfigsList } = await import("./list-B5KLEFHA.js");
1040
1043
  const { default: React } = await import("react");
1041
1044
  render(React.createElement(ConfigsList, { org }));
1042
1045
  }
@@ -1136,7 +1139,7 @@ async function main() {
1136
1139
  const positionals = cli.input.slice(2);
1137
1140
  const flagAssets = parseAssetInputs();
1138
1141
  const contents = [...positionals, ...flagAssets];
1139
- const { runAssetCheck } = await import("./check-P5JJJNEX.js");
1142
+ const { runAssetCheck } = await import("./check-NJH7BUIM.js");
1140
1143
  await runAssetCheck({
1141
1144
  contents,
1142
1145
  json: jsonMode,
@@ -1146,7 +1149,7 @@ async function main() {
1146
1149
  break;
1147
1150
  }
1148
1151
  if (subcommand === "list") {
1149
- const { runAssetList } = await import("./list-PBAXQSL5.js");
1152
+ const { runAssetList } = await import("./list-5TMGSHFC.js");
1150
1153
  await runAssetList({
1151
1154
  type: cli.flags.type ?? cli.flags.assetType,
1152
1155
  status: cli.flags.assetStatus ?? cli.flags.status,
@@ -1167,7 +1170,7 @@ async function main() {
1167
1170
  case "detections": {
1168
1171
  const org = await resolveOrg();
1169
1172
  if (subcommand === "healthcheck") {
1170
- const { runDetectionsHealthcheck } = await import("./healthcheck-HZYEIIOR.js");
1173
+ const { runDetectionsHealthcheck } = await import("./healthcheck-PFYP6PNW.js");
1171
1174
  await runDetectionsHealthcheck({
1172
1175
  org,
1173
1176
  source: cli.flags.source,
@@ -1182,7 +1185,7 @@ async function main() {
1182
1185
  break;
1183
1186
  }
1184
1187
  if (subcommand === "validate") {
1185
- const { runDetectionsValidate } = await import("./validate-SM5DD7I2.js");
1188
+ const { runDetectionsValidate } = await import("./validate-TPG47SYS.js");
1186
1189
  await runDetectionsValidate({
1187
1190
  org,
1188
1191
  source: cli.flags.source,
@@ -1197,7 +1200,7 @@ async function main() {
1197
1200
  break;
1198
1201
  }
1199
1202
  if (subcommand === "drift") {
1200
- const { runDetectionsDrift } = await import("./drift-T74V2C3J.js");
1203
+ const { runDetectionsDrift } = await import("./drift-ZOLDE24A.js");
1201
1204
  await runDetectionsDrift({
1202
1205
  org,
1203
1206
  source: cli.flags.source,
@@ -1211,7 +1214,7 @@ async function main() {
1211
1214
  break;
1212
1215
  }
1213
1216
  if (subcommand === "run") {
1214
- const { runDetectionsRun } = await import("./run-MSO6IXEV.js");
1217
+ const { runDetectionsRun } = await import("./run-VHFXOCGS.js");
1215
1218
  await runDetectionsRun({
1216
1219
  org,
1217
1220
  configId: cli.flags.configId,
@@ -1225,7 +1228,7 @@ async function main() {
1225
1228
  break;
1226
1229
  }
1227
1230
  if (subcommand === "list") {
1228
- const { runDetectionsList } = await import("./list-QUFY4IP7.js");
1231
+ const { runDetectionsList } = await import("./list-AHWEE3YZ.js");
1229
1232
  await runDetectionsList({
1230
1233
  org,
1231
1234
  cursor: cli.flags.cursor,
@@ -1254,7 +1257,7 @@ async function main() {
1254
1257
  break;
1255
1258
  }
1256
1259
  if (action === "run") {
1257
- const { runDetectionsRun } = await import("./run-MSO6IXEV.js");
1260
+ const { runDetectionsRun } = await import("./run-VHFXOCGS.js");
1258
1261
  await runDetectionsRun({
1259
1262
  org,
1260
1263
  configId: cli.flags.configId,
@@ -1272,7 +1275,7 @@ async function main() {
1272
1275
  throw new Error("detections configs update requires --config-id");
1273
1276
  }
1274
1277
  const configPatch = getConfigPatchFromSetFlags();
1275
- const { runDetectionsConfigsUpdate } = await import("./configs-update-3ZVXGADV.js");
1278
+ const { runDetectionsConfigsUpdate } = await import("./configs-update-ON67NMXA.js");
1276
1279
  await runDetectionsConfigsUpdate({
1277
1280
  org,
1278
1281
  configId: cli.flags.configId,
@@ -1306,13 +1309,14 @@ async function main() {
1306
1309
  case "metrics": {
1307
1310
  if (subcommand === "organization" || subcommand === "org") {
1308
1311
  const org2 = await tryResolveOrg();
1309
- const { runMetricsOrganization } = await import("./organization-GS5LFIWO.js");
1312
+ const { runMetricsOrganization } = await import("./organization-VSQQAGBS.js");
1310
1313
  await runMetricsOrganization({
1311
1314
  org: org2,
1312
1315
  brandSlug: cli.flags.brandSlug,
1313
1316
  from: cli.flags.from,
1314
1317
  to: cli.flags.to,
1315
1318
  thisWeek: cli.flags.thisWeek,
1319
+ include: parseCsvList(cli.flags.include),
1316
1320
  json: jsonMode,
1317
1321
  outputFormat: cliContext.outputFormat
1318
1322
  });
@@ -1320,7 +1324,7 @@ async function main() {
1320
1324
  }
1321
1325
  const org = await resolveOrg();
1322
1326
  if (subcommand === "summary") {
1323
- const { runMetricsSummary } = await import("./summary-CV657J5V.js");
1327
+ const { runMetricsSummary } = await import("./summary-QUCLZYX6.js");
1324
1328
  await runMetricsSummary({
1325
1329
  org,
1326
1330
  from: cli.flags.from,
@@ -1333,7 +1337,7 @@ async function main() {
1333
1337
  break;
1334
1338
  }
1335
1339
  if (subcommand === "found") {
1336
- const { runMetricsFound } = await import("./found-NXAWEIEK.js");
1340
+ const { runMetricsFound } = await import("./found-M5MX4VXK.js");
1337
1341
  await runMetricsFound({
1338
1342
  org,
1339
1343
  from: cli.flags.from,
@@ -1350,7 +1354,7 @@ async function main() {
1350
1354
  if (!by || !["day", "type", "brand"].includes(by)) {
1351
1355
  throw new Error("metrics breakdown requires --by <day|type|brand>");
1352
1356
  }
1353
- const { runMetricsBreakdown } = await import("./breakdown-B6QUGYI6.js");
1357
+ const { runMetricsBreakdown } = await import("./breakdown-K5Q6TDKE.js");
1354
1358
  await runMetricsBreakdown({
1355
1359
  org,
1356
1360
  by,
@@ -1371,7 +1375,7 @@ async function main() {
1371
1375
  case "reports": {
1372
1376
  if (subcommand === "list") {
1373
1377
  const org = await resolveOrg();
1374
- const { runReportsList } = await import("./list-ZFPML3ZR.js");
1378
+ const { runReportsList } = await import("./list-FV5IYCG4.js");
1375
1379
  await runReportsList({
1376
1380
  org,
1377
1381
  limit: cli.flags.limit,
@@ -1399,7 +1403,7 @@ async function main() {
1399
1403
  }
1400
1404
  if (subcommand === "create") {
1401
1405
  const org = await tryResolveOrg();
1402
- const { runReportsCreate } = await import("./create-DFH2G7RH.js");
1406
+ const { runReportsCreate } = await import("./create-W5S2ZPVC.js");
1403
1407
  await runReportsCreate({
1404
1408
  org,
1405
1409
  title: cli.flags.title,
@@ -1420,7 +1424,7 @@ async function main() {
1420
1424
  const positionals = cli.input.slice(2);
1421
1425
  const flagAssets = parseAssetInputs();
1422
1426
  const assetContents = [...positionals, ...flagAssets];
1423
- const { runReportsSearch } = await import("./search-WPSKQGUS.js");
1427
+ const { runReportsSearch } = await import("./search-NOSQW75S.js");
1424
1428
  await runReportsSearch({
1425
1429
  assetContents,
1426
1430
  reportedByCustomer: cli.flags.reportedByCustomer,
@@ -1436,7 +1440,7 @@ async function main() {
1436
1440
  }
1437
1441
  case "threats": {
1438
1442
  if (subcommand === "list") {
1439
- const { runThreatsList } = await import("./list-YCT6DYDI.js");
1443
+ const { runThreatsList } = await import("./list-HGYBEYOV.js");
1440
1444
  await runThreatsList({
1441
1445
  query: cli.flags.query,
1442
1446
  from: cli.flags.from,
@@ -1459,7 +1463,7 @@ async function main() {
1459
1463
  case "takedowns": {
1460
1464
  if (subcommand === "list") {
1461
1465
  const org = await tryResolveOrg();
1462
- const { runTakedownsList } = await import("./list-WJQT2XLE.js");
1466
+ const { runTakedownsList } = await import("./list-TUMVDWIZ.js");
1463
1467
  await runTakedownsList({
1464
1468
  org,
1465
1469
  query: cli.flags.query,
@@ -1490,7 +1494,7 @@ async function main() {
1490
1494
  }
1491
1495
  case "queues": {
1492
1496
  if (subcommand === "snapshot") {
1493
- const { runQueuesSnapshot } = await import("./snapshot-ZWAHEG64.js");
1497
+ const { runQueuesSnapshot } = await import("./snapshot-55UV26NQ.js");
1494
1498
  await runQueuesSnapshot({
1495
1499
  org: cli.flags.org,
1496
1500
  all: cli.flags.all,
@@ -1508,7 +1512,7 @@ async function main() {
1508
1512
  }
1509
1513
  case "orgs": {
1510
1514
  if (subcommand === "list") {
1511
- const { runOrgsList } = await import("./list-YRJPN4PI.js");
1515
+ const { runOrgsList } = await import("./list-ISP5JL4M.js");
1512
1516
  await runOrgsList({
1513
1517
  query: cli.flags.query,
1514
1518
  subscriptionStatus: cli.flags.subscriptionStatus,
@@ -1523,7 +1527,7 @@ async function main() {
1523
1527
  }
1524
1528
  if (subcommand === "assets") {
1525
1529
  if (action === "list") {
1526
- const { runOrgsAssetsList } = await import("./list-EIVPGMWJ.js");
1530
+ const { runOrgsAssetsList } = await import("./list-Z7RJWZH5.js");
1527
1531
  await runOrgsAssetsList({
1528
1532
  type: cli.flags.type ?? cli.flags.assetType,
1529
1533
  groupId: cli.flags.groupId,
@@ -1539,7 +1543,7 @@ async function main() {
1539
1543
  }
1540
1544
  if (subcommand === "asset-groups") {
1541
1545
  if (action === "list") {
1542
- const { runOrgsAssetGroupsList } = await import("./list-I7SBGA52.js");
1546
+ const { runOrgsAssetGroupsList } = await import("./list-VP5IY2SZ.js");
1543
1547
  await runOrgsAssetGroupsList({
1544
1548
  json: jsonMode,
1545
1549
  outputFormat: cliContext.outputFormat
@@ -1554,7 +1558,7 @@ async function main() {
1554
1558
  );
1555
1559
  }
1556
1560
  case "whoami": {
1557
- const { runWhoami } = await import("./whoami-DH5HVLFN.js");
1561
+ const { runWhoami } = await import("./whoami-Y3ZJGN4O.js");
1558
1562
  await runWhoami({
1559
1563
  json: jsonMode,
1560
1564
  outputFormat: cliContext.outputFormat
@@ -1563,7 +1567,7 @@ async function main() {
1563
1567
  }
1564
1568
  case "healthchecks": {
1565
1569
  if (subcommand === "list") {
1566
- const { runHealthchecksList } = await import("./list-23QQYAV5.js");
1570
+ const { runHealthchecksList } = await import("./list-GKC7PEU5.js");
1567
1571
  await runHealthchecksList({
1568
1572
  json: jsonMode,
1569
1573
  outputFormat: cliContext.outputFormat
@@ -1577,7 +1581,7 @@ async function main() {
1577
1581
  thresholds.minResults = cli.flags.minResults;
1578
1582
  if (cli.flags.lookbackHours !== void 0)
1579
1583
  thresholds.lookbackHours = cli.flags.lookbackHours;
1580
- const { runHealthchecksRun } = await import("./run-CJGN6TX4.js");
1584
+ const { runHealthchecksRun } = await import("./run-HRFJORDS.js");
1581
1585
  await runHealthchecksRun({
1582
1586
  org,
1583
1587
  id: action,
@@ -1595,7 +1599,7 @@ async function main() {
1595
1599
  }
1596
1600
  case "presets": {
1597
1601
  if (subcommand === "list") {
1598
- const { runPresetsList } = await import("./list-WX5G3IOF.js");
1602
+ const { runPresetsList } = await import("./list-LNNIEVB4.js");
1599
1603
  await runPresetsList({ outputFormat: cliContext.outputFormat });
1600
1604
  break;
1601
1605
  }
@@ -1606,7 +1610,7 @@ async function main() {
1606
1610
  );
1607
1611
  }
1608
1612
  const org = await resolveOrg();
1609
- const { runPresetsRun } = await import("./run-BHEXWBAS.js");
1613
+ const { runPresetsRun } = await import("./run-KDNQIR5E.js");
1610
1614
  await runPresetsRun({
1611
1615
  presetId: action,
1612
1616
  org,
@@ -1623,12 +1627,12 @@ async function main() {
1623
1627
  case "setup":
1624
1628
  case "install":
1625
1629
  case "i": {
1626
- const { setupSkill } = await import("./setup-skill-TPMTULHJ.js");
1630
+ const { setupSkill } = await import("./setup-skill-Q6PCFIDE.js");
1627
1631
  setupSkill({ json: jsonMode, cloud: cli.flags.cloud });
1628
1632
  break;
1629
1633
  }
1630
1634
  case "uninstall": {
1631
- const { uninstallSkill } = await import("./setup-skill-TPMTULHJ.js");
1635
+ const { uninstallSkill } = await import("./setup-skill-Q6PCFIDE.js");
1632
1636
  uninstallSkill({ json: jsonMode });
1633
1637
  break;
1634
1638
  }
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VFT3TD3E.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-OSRFCVBT.js";
10
+ } from "./chunk-YZEMTNCX.js";
11
11
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VFT3TD3E.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-OSRFCVBT.js";
10
+ } from "./chunk-YZEMTNCX.js";
11
11
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -12,7 +12,7 @@ import {
12
12
  LIVENESS_FILTER_VALUES,
13
13
  WATCHLIST_FILTER_VALUES,
14
14
  createApiClient
15
- } from "./chunk-OSRFCVBT.js";
15
+ } from "./chunk-YZEMTNCX.js";
16
16
  import "./chunk-EGWK6SRQ.js";
17
17
  import "./chunk-TFCNKBRC.js";
18
18
  import "./chunk-U73SABXK.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-JCMWDZYY.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-OSRFCVBT.js";
7
+ } from "./chunk-YZEMTNCX.js";
8
8
  import {
9
9
  AuthCorruptedError,
10
10
  AuthExpiredError,
@@ -10,7 +10,7 @@ import {
10
10
  REPORT_REVIEW_DECISIONS,
11
11
  REPORT_STATUSES,
12
12
  createApiClient
13
- } from "./chunk-OSRFCVBT.js";
13
+ } from "./chunk-YZEMTNCX.js";
14
14
  import "./chunk-EGWK6SRQ.js";
15
15
  import "./chunk-TFCNKBRC.js";
16
16
  import "./chunk-U73SABXK.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-OSRFCVBT.js";
7
+ } from "./chunk-YZEMTNCX.js";
8
8
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.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-DJVB73FL.js";
3
+ } from "./chunk-BIKTBRER.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-OSRFCVBT.js";
9
+ import "./chunk-YZEMTNCX.js";
10
10
  import "./chunk-EGWK6SRQ.js";
11
11
  import "./chunk-TFCNKBRC.js";
12
12
  import "./chunk-U73SABXK.js";
@@ -11,7 +11,7 @@ import {
11
11
  TAKEDOWN_SORT_KEYS,
12
12
  TAKEDOWN_STATUSES,
13
13
  createApiClient
14
- } from "./chunk-OSRFCVBT.js";
14
+ } from "./chunk-YZEMTNCX.js";
15
15
  import "./chunk-EGWK6SRQ.js";
16
16
  import "./chunk-TFCNKBRC.js";
17
17
  import "./chunk-U73SABXK.js";
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-OSRFCVBT.js";
7
+ } from "./chunk-YZEMTNCX.js";
8
8
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApiClient
3
- } from "./chunk-OSRFCVBT.js";
3
+ } from "./chunk-YZEMTNCX.js";
4
4
  import "./chunk-EGWK6SRQ.js";
5
5
  import "./chunk-TFCNKBRC.js";
6
6
  import "./chunk-U73SABXK.js";
@@ -6,13 +6,28 @@ import {
6
6
  toCsvRows
7
7
  } from "./chunk-VFT3TD3E.js";
8
8
  import {
9
+ ORGANIZATION_METRICS_INCLUDE_FIELDS,
9
10
  createApiClient
10
- } from "./chunk-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
11
12
  import "./chunk-EGWK6SRQ.js";
12
13
  import "./chunk-TFCNKBRC.js";
13
14
  import "./chunk-U73SABXK.js";
14
15
 
15
16
  // src/commands/metrics/organization.ts
17
+ var INCLUDE_VALUES = new Set(ORGANIZATION_METRICS_INCLUDE_FIELDS);
18
+ function parseIncludeList(raw) {
19
+ if (!raw || raw.length === 0) return void 0;
20
+ const fields = [];
21
+ for (const value of raw) {
22
+ if (!INCLUDE_VALUES.has(value)) {
23
+ throw new Error(
24
+ `Unknown --include value: '${value}'. Allowed: ${ORGANIZATION_METRICS_INCLUDE_FIELDS.join(", ")}`
25
+ );
26
+ }
27
+ fields.push(value);
28
+ }
29
+ return fields;
30
+ }
16
31
  async function runMetricsOrganization(options) {
17
32
  const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
18
33
  const client = options.apiClient ?? createApiClient();
@@ -21,30 +36,37 @@ async function runMetricsOrganization(options) {
21
36
  to: options.to,
22
37
  thisWeek: options.thisWeek
23
38
  });
39
+ const include = parseIncludeList(options.include);
24
40
  const input = {
25
41
  organizationSlug: options.org,
26
42
  brandSlug: options.brandSlug,
27
43
  startDate: range.startDate,
28
- endDate: range.endDate
44
+ endDate: range.endDate,
45
+ include
29
46
  };
30
47
  const result = await client.getOrganizationMetrics(input);
31
48
  const label = options.org ?? "your organization";
49
+ const includeLabel = include ? ` (include: ${include.join(", ")})` : "";
50
+ const metricLines = [
51
+ ["Reports", result.metrics.reports],
52
+ ["New threats", result.metrics.newThreats],
53
+ ["Watchlisted threats", result.metrics.threatsWatchlisted],
54
+ ["Takedowns filed", result.metrics.takedownsFiled],
55
+ ["Takedowns completed", result.metrics.takedownsCompleted],
56
+ ["Domain threats", result.metrics.domainThreats],
57
+ ["Twitter threats", result.metrics.twitterThreats],
58
+ ["Telegram threats", result.metrics.telegramThreats],
59
+ ["Other threats", result.metrics.otherThreats]
60
+ ];
61
+ const populatedMetricLines = metricLines.filter(([, value]) => value !== null);
32
62
  printOutput({
33
63
  outputFormat,
34
- json: { ...result, range },
64
+ json: { ...result, range, include: include ?? null },
35
65
  markdown: [
36
- `# Organization Metrics (${label}) \u2014 ${range.label}`,
66
+ `# Organization Metrics (${label}) \u2014 ${range.label}${includeLabel}`,
37
67
  "",
38
68
  `- Range: ${range.startDate} \u2192 ${range.endDate}`,
39
- `- Reports: ${result.metrics.reports}`,
40
- `- New threats: ${result.metrics.newThreats}`,
41
- `- Watchlisted threats: ${result.metrics.threatsWatchlisted}`,
42
- `- Takedowns filed: ${result.metrics.takedownsFiled}`,
43
- `- Takedowns completed: ${result.metrics.takedownsCompleted}`,
44
- `- Domain threats: ${result.metrics.domainThreats}`,
45
- `- Twitter threats: ${result.metrics.twitterThreats}`,
46
- `- Telegram threats: ${result.metrics.telegramThreats}`,
47
- `- Other threats: ${result.metrics.otherThreats}`
69
+ ...populatedMetricLines.map(([name, value]) => `- ${name}: ${value}`)
48
70
  ].join("\n"),
49
71
  csv: toCsvRows([
50
72
  {
@@ -63,17 +85,12 @@ async function runMetricsOrganization(options) {
63
85
  }
64
86
  ]),
65
87
  human: () => {
66
- console.log(`Organization metrics for ${label} \u2014 ${range.label}`);
88
+ console.log(`Organization metrics for ${label} \u2014 ${range.label}${includeLabel}`);
67
89
  console.log(`Range: ${range.startDate} \u2192 ${range.endDate}`);
68
- console.log(`Reports: ${result.metrics.reports}`);
69
- console.log(`New threats: ${result.metrics.newThreats}`);
70
- console.log(`Watchlisted threats: ${result.metrics.threatsWatchlisted}`);
71
- console.log(`Takedowns filed: ${result.metrics.takedownsFiled}`);
72
- console.log(`Takedowns completed: ${result.metrics.takedownsCompleted}`);
73
- console.log(
74
- `Threat breakdown: domains=${result.metrics.domainThreats}, twitter=${result.metrics.twitterThreats}, telegram=${result.metrics.telegramThreats}, other=${result.metrics.otherThreats}`
75
- );
76
- if (result.blockedByDay.length > 0) {
90
+ for (const [name, value] of populatedMetricLines) {
91
+ console.log(`${name}: ${value}`);
92
+ }
93
+ if (result.blockedByDay && result.blockedByDay.length > 0) {
77
94
  console.log("");
78
95
  console.log(`Blocked by day (${result.blockedByDay.length} entries):`);
79
96
  for (const entry of result.blockedByDay) {
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.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-DJVB73FL.js";
4
+ } from "./chunk-BIKTBRER.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-OSRFCVBT.js";
10
+ import "./chunk-YZEMTNCX.js";
11
11
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -6,7 +6,7 @@ import {
6
6
  readInstalledSkillVersion,
7
7
  setupSkill,
8
8
  uninstallSkill
9
- } from "./chunk-BQDMAEQU.js";
9
+ } from "./chunk-6X5BFFXA.js";
10
10
  import "./chunk-NQT77IB7.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VFT3TD3E.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-OSRFCVBT.js";
10
+ } from "./chunk-YZEMTNCX.js";
11
11
  import "./chunk-EGWK6SRQ.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-OSRFCVBT.js";
11
+ } from "./chunk-YZEMTNCX.js";
12
12
  import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-VFT3TD3E.js";
4
4
  import {
5
5
  createApiClient
6
- } from "./chunk-OSRFCVBT.js";
6
+ } from "./chunk-YZEMTNCX.js";
7
7
  import "./chunk-EGWK6SRQ.js";
8
8
  import "./chunk-TFCNKBRC.js";
9
9
  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.13.0",
5
+ "version": "0.14.0",
6
6
  "license": "UNLICENSED",
7
7
  "homepage": "https://chainpatrol.com/docs/cli",
8
8
  "keywords": [