@chainpatrol/cli 0.1.0 → 0.2.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.
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-H7UKKLCV.js";
7
+ } from "./chunk-KMILLX3U.js";
8
8
  import "./chunk-EEG7T6WT.js";
9
9
  import "./chunk-U73SABXK.js";
10
10
 
@@ -9,7 +9,7 @@ import {
9
9
  import {
10
10
  DateTime,
11
11
  createApiClient
12
- } from "./chunk-H7UKKLCV.js";
12
+ } from "./chunk-KMILLX3U.js";
13
13
 
14
14
  // src/presets/index.ts
15
15
  var PRESETS = [
@@ -6560,7 +6560,8 @@ function createApiClient(options) {
6560
6560
  limit: input.limit ?? 10,
6561
6561
  cursor: input.cursor,
6562
6562
  status: input.status,
6563
- searchQuery: input.searchQuery
6563
+ searchQuery: input.searchQuery,
6564
+ reportedByCustomer: input.reportedByCustomer
6564
6565
  });
6565
6566
  }
6566
6567
  };
package/dist/cli.js CHANGED
@@ -241,11 +241,13 @@ var HELP = {
241
241
  description: "List recent reports for an organization.",
242
242
  usage: "chainpatrol reports list --org <slug> [options]",
243
243
  options: [
244
- "--org <slug> Organization slug",
245
- "--limit <n> Page size (1-20)",
246
- "--cursor <id> Pagination cursor",
247
- "--status <s> Filter by report status",
248
- "--search <q> Search query"
244
+ "--org <slug> Organization slug",
245
+ "--limit <n> Page size (1-20)",
246
+ "--cursor <id> Pagination cursor",
247
+ "--status <s> Filter by report status",
248
+ "--search <q> Search query",
249
+ "--reported-by-customer Show only customer-submitted reports",
250
+ "--no-reported-by-customer Show only non-customer reports"
249
251
  ]
250
252
  },
251
253
  queues: {
@@ -388,6 +390,11 @@ ${getTopLevelHelp()}
388
390
  `, {
389
391
  importMeta: import.meta,
390
392
  version: "0.1.0",
393
+ // Boolean flags without an explicit `default` should resolve to `undefined`
394
+ // when not passed (rather than meow's default of `false`), so handlers can
395
+ // distinguish "flag omitted" from "flag explicitly set to false". Flags that
396
+ // declare their own `default` below take precedence over this.
397
+ booleanDefault: void 0,
391
398
  flags: {
392
399
  json: { type: "boolean", default: false },
393
400
  output: { type: "string" },
@@ -418,6 +425,7 @@ ${getTopLevelHelp()}
418
425
  cursor: { type: "number" },
419
426
  status: { type: "string" },
420
427
  search: { type: "string" },
428
+ reportedByCustomer: { type: "boolean" },
421
429
  attachmentUrl: { type: "string" },
422
430
  contactInfo: { type: "string" },
423
431
  externalSubmissionLink: { type: "string" },
@@ -525,12 +533,12 @@ function parseAttachmentUrls() {
525
533
  }
526
534
  async function handleConfigsList(org) {
527
535
  if (jsonMode) {
528
- const { listConfigsJson } = await import("./list-json-TPBLJBD3.js");
536
+ const { listConfigsJson } = await import("./list-json-GTST5CRN.js");
529
537
  await listConfigsJson({ org });
530
538
  return;
531
539
  }
532
540
  const { render } = await import("ink");
533
- const { default: ConfigsList } = await import("./list-IBMM562A.js");
541
+ const { default: ConfigsList } = await import("./list-IZT5P7II.js");
534
542
  const { default: React } = await import("react");
535
543
  render(React.createElement(ConfigsList, { org }));
536
544
  }
@@ -617,7 +625,7 @@ async function main() {
617
625
  case "detections": {
618
626
  const org = await resolveOrg();
619
627
  if (subcommand === "healthcheck") {
620
- const { runDetectionsHealthcheck } = await import("./healthcheck-7DR5MGEQ.js");
628
+ const { runDetectionsHealthcheck } = await import("./healthcheck-URZRXICK.js");
621
629
  await runDetectionsHealthcheck({
622
630
  org,
623
631
  source: cli.flags.source,
@@ -632,7 +640,7 @@ async function main() {
632
640
  break;
633
641
  }
634
642
  if (subcommand === "validate") {
635
- const { runDetectionsValidate } = await import("./validate-PI7GPT5I.js");
643
+ const { runDetectionsValidate } = await import("./validate-VQKKFNBO.js");
636
644
  await runDetectionsValidate({
637
645
  org,
638
646
  source: cli.flags.source,
@@ -647,7 +655,7 @@ async function main() {
647
655
  break;
648
656
  }
649
657
  if (subcommand === "drift") {
650
- const { runDetectionsDrift } = await import("./drift-VRZKQC4P.js");
658
+ const { runDetectionsDrift } = await import("./drift-UDNSOP2S.js");
651
659
  await runDetectionsDrift({
652
660
  org,
653
661
  source: cli.flags.source,
@@ -661,7 +669,7 @@ async function main() {
661
669
  break;
662
670
  }
663
671
  if (subcommand === "run") {
664
- const { runDetectionsRun } = await import("./run-PABQKATZ.js");
672
+ const { runDetectionsRun } = await import("./run-V66OGZ3W.js");
665
673
  await runDetectionsRun({
666
674
  org,
667
675
  configId: cli.flags.configId,
@@ -680,7 +688,7 @@ async function main() {
680
688
  break;
681
689
  }
682
690
  if (action === "run") {
683
- const { runDetectionsRun } = await import("./run-PABQKATZ.js");
691
+ const { runDetectionsRun } = await import("./run-V66OGZ3W.js");
684
692
  await runDetectionsRun({
685
693
  org,
686
694
  configId: cli.flags.configId,
@@ -698,7 +706,7 @@ async function main() {
698
706
  throw new Error("detections configs update requires --config-id");
699
707
  }
700
708
  const configPatch = getConfigPatchFromSetFlags();
701
- const { runDetectionsConfigsUpdate } = await import("./configs-update-BK2S6AZ6.js");
709
+ const { runDetectionsConfigsUpdate } = await import("./configs-update-OVEREFCP.js");
702
710
  await runDetectionsConfigsUpdate({
703
711
  org,
704
712
  configId: cli.flags.configId,
@@ -725,7 +733,7 @@ async function main() {
725
733
  case "metrics": {
726
734
  const org = await resolveOrg();
727
735
  if (subcommand === "summary") {
728
- const { runMetricsSummary } = await import("./summary-YG5NYIOA.js");
736
+ const { runMetricsSummary } = await import("./summary-SUWVXO7Y.js");
729
737
  await runMetricsSummary({
730
738
  org,
731
739
  from: cli.flags.from,
@@ -737,7 +745,7 @@ async function main() {
737
745
  break;
738
746
  }
739
747
  if (subcommand === "found") {
740
- const { runMetricsFound } = await import("./found-4O3AISNI.js");
748
+ const { runMetricsFound } = await import("./found-SJVDAINM.js");
741
749
  await runMetricsFound({
742
750
  org,
743
751
  from: cli.flags.from,
@@ -754,7 +762,7 @@ async function main() {
754
762
  if (!by || !["day", "type", "brand"].includes(by)) {
755
763
  throw new Error("metrics breakdown requires --by <day|type|brand>");
756
764
  }
757
- const { runMetricsBreakdown } = await import("./breakdown-JVN66HY3.js");
765
+ const { runMetricsBreakdown } = await import("./breakdown-PBH5NX6P.js");
758
766
  await runMetricsBreakdown({
759
767
  org,
760
768
  by,
@@ -774,13 +782,14 @@ async function main() {
774
782
  case "reports": {
775
783
  if (subcommand === "list") {
776
784
  const org = await resolveOrg();
777
- const { runReportsList } = await import("./list-6L7XR4SZ.js");
785
+ const { runReportsList } = await import("./list-V5OVJ5NG.js");
778
786
  await runReportsList({
779
787
  org,
780
788
  limit: cli.flags.limit,
781
789
  cursor: cli.flags.cursor,
782
790
  status: cli.flags.status,
783
791
  searchQuery: cli.flags.search,
792
+ reportedByCustomer: cli.flags.reportedByCustomer,
784
793
  json: jsonMode,
785
794
  outputFormat: cliContext.outputFormat,
786
795
  explain: cliContext.explain
@@ -789,7 +798,7 @@ async function main() {
789
798
  }
790
799
  if (subcommand === "create") {
791
800
  const org = await tryResolveOrg();
792
- const { runReportsCreate } = await import("./create-4SQUBQI7.js");
801
+ const { runReportsCreate } = await import("./create-C7H3L7TU.js");
793
802
  await runReportsCreate({
794
803
  org,
795
804
  title: cli.flags.title,
@@ -813,7 +822,7 @@ async function main() {
813
822
  }
814
823
  case "queues": {
815
824
  if (subcommand === "snapshot") {
816
- const { runQueuesSnapshot } = await import("./snapshot-JEVDTE74.js");
825
+ const { runQueuesSnapshot } = await import("./snapshot-TZHS7XQX.js");
817
826
  await runQueuesSnapshot({
818
827
  org: cli.flags.org,
819
828
  all: cli.flags.all,
@@ -831,7 +840,7 @@ async function main() {
831
840
  }
832
841
  case "presets": {
833
842
  if (subcommand === "list") {
834
- const { runPresetsList } = await import("./list-HZAHEHDM.js");
843
+ const { runPresetsList } = await import("./list-3JBC4637.js");
835
844
  await runPresetsList({ outputFormat: cliContext.outputFormat });
836
845
  break;
837
846
  }
@@ -842,7 +851,7 @@ async function main() {
842
851
  );
843
852
  }
844
853
  const org = await resolveOrg();
845
- const { runPresetsRun } = await import("./run-U62KVNTH.js");
854
+ const { runPresetsRun } = await import("./run-MUMSMG4V.js");
846
855
  await runPresetsRun({
847
856
  presetId: action,
848
857
  org,
@@ -859,12 +868,12 @@ async function main() {
859
868
  case "setup":
860
869
  case "install":
861
870
  case "i": {
862
- const { setupSkill } = await import("./setup-skill-U24CJZ6T.js");
871
+ const { setupSkill } = await import("./setup-skill-SIWP3KFS.js");
863
872
  setupSkill({ json: jsonMode });
864
873
  break;
865
874
  }
866
875
  case "uninstall": {
867
- const { uninstallSkill } = await import("./setup-skill-U24CJZ6T.js");
876
+ const { uninstallSkill } = await import("./setup-skill-SIWP3KFS.js");
868
877
  uninstallSkill({ json: jsonMode });
869
878
  break;
870
879
  }
@@ -7,7 +7,7 @@ import {
7
7
  } from "./chunk-VFT3TD3E.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-H7UKKLCV.js";
10
+ } from "./chunk-KMILLX3U.js";
11
11
  import "./chunk-EEG7T6WT.js";
12
12
  import "./chunk-U73SABXK.js";
13
13
 
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-H7UKKLCV.js";
11
+ } from "./chunk-KMILLX3U.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-H7UKKLCV.js";
11
+ } from "./chunk-KMILLX3U.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
@@ -5,7 +5,7 @@ import {
5
5
  import {
6
6
  DateTime,
7
7
  createApiClient
8
- } from "./chunk-H7UKKLCV.js";
8
+ } from "./chunk-KMILLX3U.js";
9
9
  import "./chunk-EEG7T6WT.js";
10
10
  import "./chunk-U73SABXK.js";
11
11
 
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-H7UKKLCV.js";
11
+ } from "./chunk-KMILLX3U.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  PRESETS
3
- } from "./chunk-D2QGXYXZ.js";
3
+ } from "./chunk-6FIJ4NU4.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-H7UKKLCV.js";
9
+ import "./chunk-KMILLX3U.js";
10
10
  import "./chunk-EEG7T6WT.js";
11
11
  import "./chunk-U73SABXK.js";
12
12
 
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApiClient
3
- } from "./chunk-H7UKKLCV.js";
3
+ } from "./chunk-KMILLX3U.js";
4
4
  import {
5
5
  ErrorDisplay,
6
6
  Spinner
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-H7UKKLCV.js";
11
+ } from "./chunk-KMILLX3U.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
@@ -18,6 +18,7 @@ function toReportRow(report) {
18
18
  id: report.id,
19
19
  title: report.title,
20
20
  status: report.status ?? "TODO",
21
+ reportedByCustomer: report.reportedByCustomer,
21
22
  createdAt: report.createdAt,
22
23
  updatedAt: report.updatedAt,
23
24
  assets: report.proposals.length,
@@ -39,7 +40,8 @@ async function runReportsList(options) {
39
40
  limit,
40
41
  cursor: options.cursor,
41
42
  status: options.status,
42
- searchQuery: options.searchQuery
43
+ searchQuery: options.searchQuery,
44
+ reportedByCustomer: options.reportedByCustomer
43
45
  });
44
46
  const rows = result.reports.map(toReportRow);
45
47
  printOutput({
@@ -52,7 +54,8 @@ async function runReportsList(options) {
52
54
  limit,
53
55
  cursor: options.cursor,
54
56
  status: options.status,
55
- searchQuery: options.searchQuery
57
+ searchQuery: options.searchQuery,
58
+ reportedByCustomer: options.reportedByCustomer
56
59
  }
57
60
  } : void 0
58
61
  },
@@ -63,7 +66,7 @@ async function runReportsList(options) {
63
66
  `- Next cursor: ${result.nextCursor ?? "none"}`,
64
67
  "",
65
68
  ...rows.map(
66
- (row) => `- #${row.id} ${row.status} | ${row.title} | assets=${row.assets} | created=${row.createdAt}`
69
+ (row) => `- #${row.id} ${row.status}${row.reportedByCustomer ? " (customer)" : ""} | ${row.title} | assets=${row.assets} | created=${row.createdAt}`
67
70
  )
68
71
  ].join("\n"),
69
72
  csv: toCsvRows(rows),
@@ -74,8 +77,9 @@ async function runReportsList(options) {
74
77
  }
75
78
  console.log(`Reports for ${options.org} (returned ${result.reports.length})`);
76
79
  for (const row of rows) {
80
+ const customerTag = row.reportedByCustomer ? " (customer)" : "";
77
81
  console.log(
78
- `#${row.id} [${row.status}] ${row.title} assets=${row.assets} created=${row.createdAt}`
82
+ `#${row.id} [${row.status}]${customerTag} ${row.title} assets=${row.assets} created=${row.createdAt}`
79
83
  );
80
84
  }
81
85
  if (result.nextCursor !== null) {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createApiClient
3
- } from "./chunk-H7UKKLCV.js";
3
+ } from "./chunk-KMILLX3U.js";
4
4
  import "./chunk-EEG7T6WT.js";
5
5
  import "./chunk-U73SABXK.js";
6
6
 
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  getPresetDefinition,
3
3
  runPreset
4
- } from "./chunk-D2QGXYXZ.js";
4
+ } from "./chunk-6FIJ4NU4.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-H7UKKLCV.js";
10
+ import "./chunk-KMILLX3U.js";
11
11
  import "./chunk-EEG7T6WT.js";
12
12
  import "./chunk-U73SABXK.js";
13
13
 
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-H7UKKLCV.js";
11
+ } from "./chunk-KMILLX3U.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
@@ -13,9 +13,11 @@ var SKILL_CONTENT = `---
13
13
  name: chainpatrol
14
14
  description: |
15
15
  ChainPatrol CLI assistant. Helps use the chainpatrol CLI tool: login via device
16
- code flow, check auth status, list detection configs, and run CLI commands.
16
+ code flow, check auth status, list detection configs, list reports (including
17
+ customer-reported ones), and run CLI commands.
17
18
  Use when: "chainpatrol cli", "login to chainpatrol", "check detection configs",
18
- "am I logged in", "list configs", "use the cli".
19
+ "am I logged in", "list configs", "use the cli", "list reports",
20
+ "customer reports", "reports reported by customer", "find detection gaps".
19
21
  allowed-tools:
20
22
  - Bash
21
23
  - Read
@@ -83,6 +85,56 @@ The \`--org\` flag is saved for future commands. Once set, you can omit it:
83
85
  chainpatrol configs list
84
86
  \`\`\`
85
87
 
88
+ ### \`reports list\` \u2014 List recent reports for an organization
89
+
90
+ Returns the most recent reports submitted for an organization. Each report
91
+ includes a \`reportedByCustomer\` boolean indicating whether the report was
92
+ submitted by a customer of ChainPatrol (e.g. via API key, Slack/Telegram bot,
93
+ or another external integration) rather than by ChainPatrol's automated
94
+ detections or staff reviewers.
95
+
96
+ \`\`\`bash
97
+ chainpatrol reports list --org <slug>
98
+ \`\`\`
99
+
100
+ Common flags:
101
+ - \`--limit <n>\` page size (1-20)
102
+ - \`--cursor <id>\` pagination cursor (use \`nextCursor\` from a previous response)
103
+ - \`--status <s>\` filter by report status (e.g. \`TODO\`, \`IN_PROGRESS\`, \`DONE\`)
104
+ - \`--search <q>\` search query across title/description/asset content
105
+ - \`--reported-by-customer\` only show reports submitted by a customer
106
+ - \`--no-reported-by-customer\` only show reports NOT submitted by a customer
107
+ (i.e. found by ChainPatrol's automation or staff)
108
+
109
+ JSON mode is recommended when you want to analyze the data programmatically:
110
+ \`\`\`bash
111
+ chainpatrol --json reports list --org <slug> --reported-by-customer
112
+ \`\`\`
113
+
114
+ #### Use case: finding gaps in ChainPatrol detection
115
+
116
+ Customer-reported reports (\`reportedByCustomer=true\`) are a valuable signal
117
+ for finding gaps in ChainPatrol's automated detections and staff triage: each
118
+ one is a threat the customer found before ChainPatrol's own systems did. When
119
+ the user asks something like:
120
+
121
+ - "show me the threats our customers are reporting"
122
+ - "what is X's customer reporting?"
123
+ - "where are we missing detections for org Y?"
124
+ - "summarize recent reports submitted by customers"
125
+
126
+ \u2026use \`chainpatrol --json reports list --org <slug> --reported-by-customer\`
127
+ to fetch them, then highlight patterns (asset types, domains, common
128
+ keywords, recurring brands) so the user can:
129
+ 1. Spot detection coverage gaps to fix in the org's detection configs.
130
+ 2. Improve staff triage runbooks for recurring scams.
131
+ 3. Work directly with the customer to close the loop and prevent future
132
+ misses.
133
+
134
+ You can also compare with the non-customer set using
135
+ \`--no-reported-by-customer\` to gauge detection coverage on the same time
136
+ window.
137
+
86
138
  ## Checking Auth Status
87
139
 
88
140
  To check if the user is logged in, read the credentials file:
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-H7UKKLCV.js";
11
+ } from "./chunk-KMILLX3U.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
@@ -4,7 +4,7 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-H7UKKLCV.js";
7
+ } from "./chunk-KMILLX3U.js";
8
8
  import "./chunk-EEG7T6WT.js";
9
9
  import "./chunk-U73SABXK.js";
10
10
 
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-H7UKKLCV.js";
11
+ } from "./chunk-KMILLX3U.js";
12
12
  import "./chunk-EEG7T6WT.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
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.1.0",
5
+ "version": "0.2.0",
6
6
  "license": "UNLICENSED",
7
7
  "homepage": "https://chainpatrol.com/docs/cli",
8
8
  "keywords": [
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "type": "module",
14
14
  "bin": {
15
- "chainpatrol": "./dist/cli.js"
15
+ "chainpatrol": "dist/cli.js"
16
16
  },
17
17
  "files": [
18
18
  "./dist/**"
@@ -35,16 +35,16 @@
35
35
  "ink": "^7.0.0",
36
36
  "meow": "^14.1.0",
37
37
  "open": "^11.0.0",
38
- "react": "^19.2.4",
39
- "zod": "^3.25.76"
38
+ "react": "catalog:",
39
+ "zod": "catalog:"
40
40
  },
41
41
  "devDependencies": {
42
- "@types/react": "^19.2.7",
42
+ "@types/react": "catalog:",
43
43
  "ink-testing-library": "^4.0.0",
44
44
  "msw": "^2.0.0",
45
45
  "tsup": "^8.5.0",
46
- "tsx": "^4.19.4",
47
- "typescript": "6.0.2",
48
- "vitest": "^3.2.4"
46
+ "tsx": "catalog:",
47
+ "typescript": "catalog:",
48
+ "vitest": "catalog:"
49
49
  }
50
- }
50
+ }