@chainpatrol/cli 0.7.0 → 0.9.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 (36) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +14 -0
  3. package/dist/{breakdown-AX6QNTQH.js → breakdown-63FAOVL7.js} +2 -2
  4. package/dist/check-YZRIAUOK.js +85 -0
  5. package/dist/{chunk-AGXMZFUU.js → chunk-BJISZ3CY.js} +1 -1
  6. package/dist/{chunk-EEG7T6WT.js → chunk-EGWK6SRQ.js} +5 -0
  7. package/dist/{chunk-ZN3VMRWG.js → chunk-P4L4N5LM.js} +30 -2
  8. package/dist/{chunk-EBJMOX3Q.js → chunk-PZV55KAR.js} +6 -1
  9. package/dist/{chunk-LLWKCA3H.js → chunk-RIKR2WFT.js} +22 -7
  10. package/dist/{chunk-IUZB3DQW.js → chunk-Z76CUWSS.js} +11 -1
  11. package/dist/cli.js +66 -29
  12. package/dist/{completions-EGQIARFC.js → completions-D6SOLU2D.js} +1 -1
  13. package/dist/{configs-update-VROBC2HI.js → configs-update-2IOSKZH5.js} +2 -2
  14. package/dist/{create-XTCUNT2C.js → create-EWS3SFCH.js} +2 -2
  15. package/dist/{drift-VOKQJ36G.js → drift-Q3VG3XG3.js} +2 -2
  16. package/dist/{found-A5HRTJCJ.js → found-22B7RZT5.js} +2 -2
  17. package/dist/{healthcheck-AQUXVKAO.js → healthcheck-C3AIMUJT.js} +2 -2
  18. package/dist/{list-CVFXTKNX.js → list-AYHDFSQG.js} +2 -2
  19. package/dist/{list-PLZ67PNY.js → list-DNRWKM5O.js} +2 -2
  20. package/dist/{list-EYRN5JYC.js → list-HFWSMLGJ.js} +3 -3
  21. package/dist/{list-5ENZAOFL.js → list-IA4CSOIY.js} +2 -2
  22. package/dist/{list-CGRHTFAS.js → list-SDBLGBVW.js} +2 -2
  23. package/dist/{list-json-LEKCCWQU.js → list-json-CEPGVUGF.js} +2 -2
  24. package/dist/{login-C66GRR3Y.js → login-AMEXCOGT.js} +11 -2
  25. package/dist/{login-json-XGMXT5VJ.js → login-json-MPLXCSP4.js} +2 -2
  26. package/dist/{login-plain-CWKOUZKE.js → login-plain-NIJDS3D2.js} +2 -2
  27. package/dist/{logout-LA7VEKON.js → logout-T6Q4IVPU.js} +10 -1
  28. package/dist/{logout-json-4GIJZJ46.js → logout-json-ACXBI6PN.js} +11 -1
  29. package/dist/{run-MS5SA5YL.js → run-AQMJRFGL.js} +2 -2
  30. package/dist/{run-YHDUUP66.js → run-WJGHJPXN.js} +3 -3
  31. package/dist/{run-OT2X46GT.js → run-YTWNQD5X.js} +2 -2
  32. package/dist/{setup-skill-J7PZYVCE.js → setup-skill-DR4KGQMG.js} +2 -2
  33. package/dist/{snapshot-4QR4I67P.js → snapshot-C5MZWJTW.js} +2 -2
  34. package/dist/{summary-JOCABBCO.js → summary-NQDZQEOD.js} +2 -2
  35. package/dist/{validate-27RUCN7R.js → validate-5DVPSXJP.js} +2 -2
  36. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,32 @@
1
1
  # @chainpatrol/cli
2
2
 
3
+ ## 0.9.0
4
+
5
+ ### Minor Changes
6
+
7
+ - e2658e3: Add `chainpatrol asset check <content>` — look up a single URL, domain, or
8
+ crypto address against the ChainPatrol blocklist and external feeds and
9
+ return the aggregated status (`BLOCKED`, `ALLOWED`, or `UNKNOWN`) plus a
10
+ per-source breakdown. Supports `--json`, `--output markdown`, and
11
+ `--output csv` for agent/script use, and works with both device-code login
12
+ and `CHAINPATROL_API_KEY` env-var auth. The bundled `/chainpatrol` Claude
13
+ Code skill is updated to document the new command and to trigger on
14
+ phrases like "is this URL blocked" / "check this asset".
15
+
16
+ ## 0.8.0
17
+
18
+ ### Minor Changes
19
+
20
+ - 11d074f: Support non-interactive authentication via the `CHAINPATROL_API_KEY` env
21
+ var. When set, the CLI sends it as the `x-api-key` header on API calls and
22
+ skips the device-code login flow entirely. `chainpatrol login` reports
23
+ "already authenticated via env var" instead of polling, and
24
+ `chainpatrol logout` refuses to no-op (it tells you to unset the env var).
25
+ Stored Better Auth bearer credentials are still used as a fallback when
26
+ the env var is unset. Headless service accounts (e.g. agent42) should mint
27
+ a USER-type API key via `/staff/api-keys` and set
28
+ `CHAINPATROL_API_KEY=cp_live_…` in their deployment env.
29
+
3
30
  ## 0.7.0
4
31
 
5
32
  ### Minor Changes
package/README.md CHANGED
@@ -12,6 +12,7 @@ npm install -g chainpatrol
12
12
 
13
13
  ```bash
14
14
  chainpatrol login
15
+ chainpatrol asset check https://phish.example
15
16
  chainpatrol configs list --org my-org
16
17
  chainpatrol --json configs list --org my-org
17
18
  chainpatrol detections healthcheck --org my-org --run
@@ -38,6 +39,19 @@ chainpatrol detections drift --org my-org --explain --output markdown
38
39
  chainpatrol queues snapshot --all --output csv
39
40
  ```
40
41
 
42
+ ### Asset commands
43
+
44
+ ```bash
45
+ # look up a single asset against the ChainPatrol blocklist and external feeds
46
+ chainpatrol asset check https://phish.example
47
+
48
+ # JSON output for automation (returns status, per-source breakdown, watchStatus)
49
+ chainpatrol --json asset check 0xabc123...
50
+
51
+ # markdown summary
52
+ chainpatrol asset check phish.example --output markdown
53
+ ```
54
+
41
55
  ### Detection commands
42
56
 
43
57
  ```bash
@@ -4,8 +4,8 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-LLWKCA3H.js";
8
- import "./chunk-EEG7T6WT.js";
7
+ } from "./chunk-RIKR2WFT.js";
8
+ import "./chunk-EGWK6SRQ.js";
9
9
  import "./chunk-TFCNKBRC.js";
10
10
  import "./chunk-U73SABXK.js";
11
11
 
@@ -0,0 +1,85 @@
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-RIKR2WFT.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
+ import "./chunk-TFCNKBRC.js";
14
+ import "./chunk-U73SABXK.js";
15
+
16
+ // src/commands/asset/check.ts
17
+ function statusLine(result) {
18
+ const watchTag = result.watchStatus ? ` watch=${result.watchStatus}` : "";
19
+ const reasonTag = result.reason ? ` reason=${result.reason}` : "";
20
+ return `${result.status} (source=${result.source}${reasonTag}${watchTag})`;
21
+ }
22
+ async function runAssetCheck(options) {
23
+ const content = options.content?.trim();
24
+ if (!content) {
25
+ throw new CliExitError(
26
+ "asset check requires an asset. Example: chainpatrol asset check https://example.com",
27
+ ExitCode.USAGE
28
+ );
29
+ }
30
+ const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
31
+ const client = options.apiClient ?? createApiClient();
32
+ const result = await client.assetCheck({ content });
33
+ printOutput({
34
+ outputFormat,
35
+ json: {
36
+ content,
37
+ ...result,
38
+ explanation: options.explain ? "Asset check aggregates ChainPatrol records and external sources to classify a domain, URL, or crypto address as BLOCKED, ALLOWED, or UNKNOWN." : void 0
39
+ },
40
+ markdown: [
41
+ `# Asset Check: ${content}`,
42
+ "",
43
+ `- Status: **${result.status}**`,
44
+ `- Source: ${result.source}`,
45
+ ...result.reason ? [`- Reason: ${result.reason}`] : [],
46
+ ...result.watchStatus ? [`- Watch status: ${result.watchStatus}`] : [],
47
+ ...result.message ? [`- Message: ${result.message}`] : [],
48
+ ...result.code ? [`- Code: ${result.code}`] : [],
49
+ "",
50
+ "## Per-source results",
51
+ "",
52
+ ...result.sources.map((entry) => `- ${entry.source}: ${entry.status}`)
53
+ ].join("\n"),
54
+ csv: toCsvRows(
55
+ result.sources.map((entry) => ({
56
+ content,
57
+ source: entry.source,
58
+ status: entry.status
59
+ }))
60
+ ),
61
+ human: () => {
62
+ console.log(`${content} -> ${statusLine(result)}`);
63
+ if (result.message) {
64
+ console.log(`Message: ${result.message}`);
65
+ }
66
+ if (result.code) {
67
+ console.log(`Code: ${result.code}`);
68
+ }
69
+ if (result.sources.length > 0) {
70
+ console.log("Sources:");
71
+ for (const entry of result.sources) {
72
+ console.log(` - ${entry.source}: ${entry.status}`);
73
+ }
74
+ }
75
+ if (options.explain) {
76
+ console.log(
77
+ "Status is the aggregated verdict across ChainPatrol and external feeds; see the per-source rows for the underlying signals."
78
+ );
79
+ }
80
+ }
81
+ });
82
+ }
83
+ export {
84
+ runAssetCheck
85
+ };
@@ -8,7 +8,7 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-LLWKCA3H.js";
11
+ } from "./chunk-RIKR2WFT.js";
12
12
  import {
13
13
  DateTime
14
14
  } from "./chunk-TFCNKBRC.js";
@@ -139,6 +139,7 @@ function clearCredentials() {
139
139
  }
140
140
  }
141
141
  function isLoggedIn() {
142
+ if (hasApiKeyEnv()) return true;
142
143
  try {
143
144
  getValidCredentials();
144
145
  return true;
@@ -146,6 +147,9 @@ function isLoggedIn() {
146
147
  return false;
147
148
  }
148
149
  }
150
+ function hasApiKeyEnv() {
151
+ return Boolean(process.env.CHAINPATROL_API_KEY?.trim());
152
+ }
149
153
  function getValidCredentials() {
150
154
  const creds = getCredentials();
151
155
  const expiresMs = new Date(creds.expiresAt).getTime();
@@ -280,6 +284,7 @@ export {
280
284
  storeCredentials,
281
285
  clearCredentials,
282
286
  isLoggedIn,
287
+ hasApiKeyEnv,
283
288
  getValidCredentials,
284
289
  fetchUserEmail,
285
290
  requestDeviceCode,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  installCompletions,
3
3
  uninstallCompletions
4
- } from "./chunk-IUZB3DQW.js";
4
+ } from "./chunk-Z76CUWSS.js";
5
5
 
6
6
  // src/commands/setup-skill.ts
7
7
  import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, readFileSync as readFileSync3, rmSync } from "fs";
@@ -196,7 +196,9 @@ description: |
196
196
  "org healthcheck", "organization health check", "audit my org",
197
197
  "what's wrong with org", "review org setup", "list orgs", "list organizations",
198
198
  "orgs with takedowns off", "automation off across orgs",
199
- "which customers have X enabled", "service toggles by org".
199
+ "which customers have X enabled", "service toggles by org",
200
+ "is this URL blocked", "is this domain blocked", "is this address blocked",
201
+ "check this asset", "asset check", "lookup asset status".
200
202
  allowed-tools:
201
203
  - Bash
202
204
  - Read
@@ -310,6 +312,32 @@ so the same background+tail pattern works without \`--json\`. Prefer
310
312
  chainpatrol logout
311
313
  \`\`\`
312
314
 
315
+ ### \`asset check\` \u2014 Check a single asset against the blocklist
316
+
317
+ Look up a URL, domain, or crypto address and return its aggregated status
318
+ (\`BLOCKED\`, \`ALLOWED\`, or \`UNKNOWN\`) plus a per-source breakdown
319
+ (ChainPatrol + external feeds like eth-phishing-detect, phishfort, seal,
320
+ polkadot-phishing). Works whether you're authenticated via device-code
321
+ login or via a \`CHAINPATROL_API_KEY\` env var.
322
+
323
+ \`\`\`bash
324
+ chainpatrol asset check https://phish.example
325
+ chainpatrol asset check 0xabc123...
326
+ \`\`\`
327
+
328
+ JSON mode (recommended for automations and agents \u2014 returns full
329
+ \`{ status, source, reason?, sources[], watchStatus? }\`):
330
+
331
+ \`\`\`bash
332
+ chainpatrol --json asset check https://phish.example
333
+ \`\`\`
334
+
335
+ Markdown is also supported for sharing in docs / chat:
336
+
337
+ \`\`\`bash
338
+ chainpatrol asset check phish.example --output markdown
339
+ \`\`\`
340
+
313
341
  ### \`configs list\` \u2014 List detection configurations
314
342
 
315
343
  Requires authentication and an organization slug.
@@ -1,17 +1,22 @@
1
1
  import {
2
2
  fetchUserEmail,
3
3
  getCredentials,
4
+ hasApiKeyEnv,
4
5
  isLoggedIn,
5
6
  pollForToken,
6
7
  requestDeviceCode,
7
8
  storeCredentials
8
- } from "./chunk-EEG7T6WT.js";
9
+ } from "./chunk-EGWK6SRQ.js";
9
10
  import {
10
11
  DateTime
11
12
  } from "./chunk-TFCNKBRC.js";
12
13
 
13
14
  // src/lib/login-flow.ts
14
15
  async function runLoginFlow(emit) {
16
+ if (hasApiKeyEnv()) {
17
+ emit({ type: "already_logged_in", email: null });
18
+ return true;
19
+ }
15
20
  if (isLoggedIn()) {
16
21
  const creds = getCredentials();
17
22
  emit({ type: "already_logged_in", email: creds.email ?? null });
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  getValidCredentials
3
- } from "./chunk-EEG7T6WT.js";
3
+ } from "./chunk-EGWK6SRQ.js";
4
4
  import {
5
5
  DateTime
6
6
  } from "./chunk-TFCNKBRC.js";
@@ -18,20 +18,32 @@ function parseIsoDate(value) {
18
18
  return dt.toJSDate();
19
19
  }
20
20
  var REQUEST_TIMEOUT_MS = 3e4;
21
+ function defaultGetCredential() {
22
+ const envKey = process.env.CHAINPATROL_API_KEY;
23
+ if (envKey && envKey.trim().length > 0) {
24
+ return { kind: "api-key", value: envKey.trim() };
25
+ }
26
+ return { kind: "bearer", value: getValidCredentials().accessToken };
27
+ }
21
28
  function createApiClient(options) {
22
29
  const config = getConfig();
23
30
  const apiUrl = options?.apiUrl ?? config.apiUrl;
24
- const getToken = options?.getToken ?? (() => getValidCredentials().accessToken);
31
+ const getCredential = options?.getCredential ?? (options?.getToken ? () => ({ kind: "bearer", value: options.getToken() }) : defaultGetCredential);
25
32
  async function request(path, body) {
26
- const token = getToken();
33
+ const credential = getCredential();
34
+ const headers = {
35
+ "Content-Type": "application/json"
36
+ };
37
+ if (credential.kind === "api-key") {
38
+ headers["x-api-key"] = credential.value;
39
+ } else {
40
+ headers["Authorization"] = `Bearer ${credential.value}`;
41
+ }
27
42
  let res;
28
43
  try {
29
44
  res = await fetch(`${apiUrl}/api/v2${path}`, {
30
45
  method: "POST",
31
- headers: {
32
- "Content-Type": "application/json",
33
- Authorization: `Bearer ${token}`
34
- },
46
+ headers,
35
47
  body: JSON.stringify(body),
36
48
  signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
37
49
  });
@@ -83,6 +95,9 @@ function createApiClient(options) {
83
95
  return await res.json();
84
96
  }
85
97
  return {
98
+ assetCheck(input) {
99
+ return request("/asset/check", input);
100
+ },
86
101
  listDetectionConfigs(slug) {
87
102
  return request("/detection/configs/list", { slug });
88
103
  },
@@ -16,6 +16,7 @@ _chainpatrol() {
16
16
  commands=(
17
17
  'login:Authenticate with ChainPatrol'
18
18
  'logout:Clear stored credentials'
19
+ 'asset:Check asset status against ChainPatrol'
19
20
  'configs:Manage detection configs'
20
21
  'detections:Validate and run detection configs'
21
22
  'metrics:Query organization metrics'
@@ -56,6 +57,11 @@ _chainpatrol() {
56
57
  _arguments -s $global_flags
57
58
  elif (( CURRENT == 3 )); then
58
59
  case "\${words[2]}" in
60
+ asset)
61
+ local -a asset_subcommands
62
+ asset_subcommands=('check:Check asset status against ChainPatrol')
63
+ _describe 'subcommand' asset_subcommands
64
+ ;;
59
65
  configs)
60
66
  local -a subcommands
61
67
  subcommands=('list:List detection configurations')
@@ -118,13 +124,17 @@ var BASH_COMPLETION = `_chainpatrol() {
118
124
  COMPREPLY=()
119
125
  cur="\${COMP_WORDS[COMP_CWORD]}"
120
126
  prev="\${COMP_WORDS[COMP_CWORD-1]}"
121
- commands="login logout configs detections metrics reports queues presets setup uninstall completions help"
127
+ commands="login logout asset configs detections metrics reports queues presets setup uninstall completions help"
122
128
 
123
129
  case "\${prev}" in
124
130
  chainpatrol)
125
131
  COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
126
132
  return 0
127
133
  ;;
134
+ asset)
135
+ COMPREPLY=( $(compgen -W "check" -- "\${cur}") )
136
+ return 0
137
+ ;;
128
138
  configs)
129
139
  COMPREPLY=( $(compgen -W "list" -- "\${cur}") )
130
140
  return 0
package/dist/cli.js CHANGED
@@ -13,8 +13,8 @@ import {
13
13
  getCliVersion,
14
14
  isSkillInstalled,
15
15
  readInstalledSkillVersion
16
- } from "./chunk-ZN3VMRWG.js";
17
- import "./chunk-IUZB3DQW.js";
16
+ } from "./chunk-P4L4N5LM.js";
17
+ import "./chunk-Z76CUWSS.js";
18
18
  import {
19
19
  DateTime
20
20
  } from "./chunk-TFCNKBRC.js";
@@ -91,6 +91,23 @@ var HELP = {
91
91
  description: "Clear stored credentials from this machine.",
92
92
  usage: "chainpatrol logout"
93
93
  },
94
+ asset: {
95
+ description: "Check whether an asset is BLOCKED, ALLOWED, or UNKNOWN.",
96
+ usage: "chainpatrol asset check <content>",
97
+ examples: [
98
+ "chainpatrol asset check https://phish.example",
99
+ "chainpatrol --json asset check 0xabc123\u2026"
100
+ ]
101
+ },
102
+ "asset check": {
103
+ description: "Look up an asset (URL, domain, or crypto address) against the ChainPatrol blocklist and external feeds. Returns the aggregated status plus a per-source breakdown.",
104
+ usage: "chainpatrol asset check <content>",
105
+ examples: [
106
+ "chainpatrol asset check https://phish.example",
107
+ "chainpatrol asset check phish.example --output markdown",
108
+ "chainpatrol --json asset check 0xabc123\u2026"
109
+ ]
110
+ },
94
111
  configs: {
95
112
  description: "Manage detection configs for an organization.",
96
113
  usage: "chainpatrol configs <list>",
@@ -391,6 +408,7 @@ function getTopLevelHelp() {
391
408
  " Commands",
392
409
  " login Authenticate with ChainPatrol",
393
410
  " logout Clear stored credentials",
411
+ " asset Check asset status against ChainPatrol",
394
412
  " configs Manage detection configs",
395
413
  " detections Validate, run, update detection configs",
396
414
  " healthchecks List and run organization healthchecks",
@@ -561,6 +579,7 @@ function printVersionMessages(messages) {
561
579
  var COMMANDS = [
562
580
  "login",
563
581
  "logout",
582
+ "asset",
564
583
  "configs",
565
584
  "detections",
566
585
  "healthchecks",
@@ -754,12 +773,12 @@ function parseAttachmentUrls() {
754
773
  }
755
774
  async function handleConfigsList(org) {
756
775
  if (jsonMode) {
757
- const { listConfigsJson } = await import("./list-json-LEKCCWQU.js");
776
+ const { listConfigsJson } = await import("./list-json-CEPGVUGF.js");
758
777
  await listConfigsJson({ org });
759
778
  return;
760
779
  }
761
780
  const { render } = await import("ink");
762
- const { default: ConfigsList } = await import("./list-5ENZAOFL.js");
781
+ const { default: ConfigsList } = await import("./list-IA4CSOIY.js");
763
782
  const { default: React } = await import("react");
764
783
  render(React.createElement(ConfigsList, { org }));
765
784
  }
@@ -814,14 +833,14 @@ async function main() {
814
833
  );
815
834
  }
816
835
  if (jsonMode) {
817
- const { loginJson } = await import("./login-json-XGMXT5VJ.js");
836
+ const { loginJson } = await import("./login-json-MPLXCSP4.js");
818
837
  await loginJson();
819
838
  } else if (!process.stdout.isTTY) {
820
- const { loginPlain } = await import("./login-plain-CWKOUZKE.js");
839
+ const { loginPlain } = await import("./login-plain-NIJDS3D2.js");
821
840
  await loginPlain();
822
841
  } else {
823
842
  const { render } = await import("ink");
824
- const { default: Login } = await import("./login-C66GRR3Y.js");
843
+ const { default: Login } = await import("./login-AMEXCOGT.js");
825
844
  const { default: React } = await import("react");
826
845
  render(React.createElement(Login));
827
846
  }
@@ -829,11 +848,11 @@ async function main() {
829
848
  }
830
849
  case "logout": {
831
850
  if (jsonMode) {
832
- const { logoutJson } = await import("./logout-json-4GIJZJ46.js");
851
+ const { logoutJson } = await import("./logout-json-ACXBI6PN.js");
833
852
  await logoutJson();
834
853
  } else {
835
854
  const { render } = await import("ink");
836
- const { default: Logout } = await import("./logout-LA7VEKON.js");
855
+ const { default: Logout } = await import("./logout-T6Q4IVPU.js");
837
856
  const { default: React } = await import("react");
838
857
  render(React.createElement(Logout));
839
858
  }
@@ -854,10 +873,28 @@ async function main() {
854
873
  }
855
874
  throw new Error("Usage: chainpatrol configs list");
856
875
  }
876
+ case "asset": {
877
+ if (subcommand === "check") {
878
+ const positional = cli.input[2];
879
+ const content = positional ?? cli.flags.asset;
880
+ const { runAssetCheck } = await import("./check-YZRIAUOK.js");
881
+ await runAssetCheck({
882
+ content,
883
+ json: jsonMode,
884
+ outputFormat: cliContext.outputFormat,
885
+ explain: cliContext.explain
886
+ });
887
+ break;
888
+ }
889
+ const hint = subcommand ? suggest(subcommand, ["check"]) : null;
890
+ throw new Error(
891
+ subcommand ? `Unknown subcommand: asset ${subcommand}${hint ? `. Did you mean "asset ${hint}"?` : ""}` : "Usage: chainpatrol asset check <content>"
892
+ );
893
+ }
857
894
  case "detections": {
858
895
  const org = await resolveOrg();
859
896
  if (subcommand === "healthcheck") {
860
- const { runDetectionsHealthcheck } = await import("./healthcheck-AQUXVKAO.js");
897
+ const { runDetectionsHealthcheck } = await import("./healthcheck-C3AIMUJT.js");
861
898
  await runDetectionsHealthcheck({
862
899
  org,
863
900
  source: cli.flags.source,
@@ -872,7 +909,7 @@ async function main() {
872
909
  break;
873
910
  }
874
911
  if (subcommand === "validate") {
875
- const { runDetectionsValidate } = await import("./validate-27RUCN7R.js");
912
+ const { runDetectionsValidate } = await import("./validate-5DVPSXJP.js");
876
913
  await runDetectionsValidate({
877
914
  org,
878
915
  source: cli.flags.source,
@@ -887,7 +924,7 @@ async function main() {
887
924
  break;
888
925
  }
889
926
  if (subcommand === "drift") {
890
- const { runDetectionsDrift } = await import("./drift-VOKQJ36G.js");
927
+ const { runDetectionsDrift } = await import("./drift-Q3VG3XG3.js");
891
928
  await runDetectionsDrift({
892
929
  org,
893
930
  source: cli.flags.source,
@@ -901,7 +938,7 @@ async function main() {
901
938
  break;
902
939
  }
903
940
  if (subcommand === "run") {
904
- const { runDetectionsRun } = await import("./run-OT2X46GT.js");
941
+ const { runDetectionsRun } = await import("./run-YTWNQD5X.js");
905
942
  await runDetectionsRun({
906
943
  org,
907
944
  configId: cli.flags.configId,
@@ -920,7 +957,7 @@ async function main() {
920
957
  break;
921
958
  }
922
959
  if (action === "run") {
923
- const { runDetectionsRun } = await import("./run-OT2X46GT.js");
960
+ const { runDetectionsRun } = await import("./run-YTWNQD5X.js");
924
961
  await runDetectionsRun({
925
962
  org,
926
963
  configId: cli.flags.configId,
@@ -938,7 +975,7 @@ async function main() {
938
975
  throw new Error("detections configs update requires --config-id");
939
976
  }
940
977
  const configPatch = getConfigPatchFromSetFlags();
941
- const { runDetectionsConfigsUpdate } = await import("./configs-update-VROBC2HI.js");
978
+ const { runDetectionsConfigsUpdate } = await import("./configs-update-2IOSKZH5.js");
942
979
  await runDetectionsConfigsUpdate({
943
980
  org,
944
981
  configId: cli.flags.configId,
@@ -965,7 +1002,7 @@ async function main() {
965
1002
  case "metrics": {
966
1003
  const org = await resolveOrg();
967
1004
  if (subcommand === "summary") {
968
- const { runMetricsSummary } = await import("./summary-JOCABBCO.js");
1005
+ const { runMetricsSummary } = await import("./summary-NQDZQEOD.js");
969
1006
  await runMetricsSummary({
970
1007
  org,
971
1008
  from: cli.flags.from,
@@ -977,7 +1014,7 @@ async function main() {
977
1014
  break;
978
1015
  }
979
1016
  if (subcommand === "found") {
980
- const { runMetricsFound } = await import("./found-A5HRTJCJ.js");
1017
+ const { runMetricsFound } = await import("./found-22B7RZT5.js");
981
1018
  await runMetricsFound({
982
1019
  org,
983
1020
  from: cli.flags.from,
@@ -994,7 +1031,7 @@ async function main() {
994
1031
  if (!by || !["day", "type", "brand"].includes(by)) {
995
1032
  throw new Error("metrics breakdown requires --by <day|type|brand>");
996
1033
  }
997
- const { runMetricsBreakdown } = await import("./breakdown-AX6QNTQH.js");
1034
+ const { runMetricsBreakdown } = await import("./breakdown-63FAOVL7.js");
998
1035
  await runMetricsBreakdown({
999
1036
  org,
1000
1037
  by,
@@ -1014,7 +1051,7 @@ async function main() {
1014
1051
  case "reports": {
1015
1052
  if (subcommand === "list") {
1016
1053
  const org = await resolveOrg();
1017
- const { runReportsList } = await import("./list-CVFXTKNX.js");
1054
+ const { runReportsList } = await import("./list-AYHDFSQG.js");
1018
1055
  await runReportsList({
1019
1056
  org,
1020
1057
  limit: cli.flags.limit,
@@ -1030,7 +1067,7 @@ async function main() {
1030
1067
  }
1031
1068
  if (subcommand === "create") {
1032
1069
  const org = await tryResolveOrg();
1033
- const { runReportsCreate } = await import("./create-XTCUNT2C.js");
1070
+ const { runReportsCreate } = await import("./create-EWS3SFCH.js");
1034
1071
  await runReportsCreate({
1035
1072
  org,
1036
1073
  title: cli.flags.title,
@@ -1054,7 +1091,7 @@ async function main() {
1054
1091
  }
1055
1092
  case "queues": {
1056
1093
  if (subcommand === "snapshot") {
1057
- const { runQueuesSnapshot } = await import("./snapshot-4QR4I67P.js");
1094
+ const { runQueuesSnapshot } = await import("./snapshot-C5MZWJTW.js");
1058
1095
  await runQueuesSnapshot({
1059
1096
  org: cli.flags.org,
1060
1097
  all: cli.flags.all,
@@ -1072,7 +1109,7 @@ async function main() {
1072
1109
  }
1073
1110
  case "orgs": {
1074
1111
  if (subcommand === "list") {
1075
- const { runOrgsList } = await import("./list-CGRHTFAS.js");
1112
+ const { runOrgsList } = await import("./list-SDBLGBVW.js");
1076
1113
  await runOrgsList({
1077
1114
  query: cli.flags.query,
1078
1115
  subscriptionStatus: cli.flags.subscriptionStatus,
@@ -1092,7 +1129,7 @@ async function main() {
1092
1129
  }
1093
1130
  case "healthchecks": {
1094
1131
  if (subcommand === "list") {
1095
- const { runHealthchecksList } = await import("./list-PLZ67PNY.js");
1132
+ const { runHealthchecksList } = await import("./list-DNRWKM5O.js");
1096
1133
  await runHealthchecksList({
1097
1134
  json: jsonMode,
1098
1135
  outputFormat: cliContext.outputFormat
@@ -1106,7 +1143,7 @@ async function main() {
1106
1143
  thresholds.minResults = cli.flags.minResults;
1107
1144
  if (cli.flags.lookbackHours !== void 0)
1108
1145
  thresholds.lookbackHours = cli.flags.lookbackHours;
1109
- const { runHealthchecksRun } = await import("./run-MS5SA5YL.js");
1146
+ const { runHealthchecksRun } = await import("./run-AQMJRFGL.js");
1110
1147
  await runHealthchecksRun({
1111
1148
  org,
1112
1149
  id: action,
@@ -1124,7 +1161,7 @@ async function main() {
1124
1161
  }
1125
1162
  case "presets": {
1126
1163
  if (subcommand === "list") {
1127
- const { runPresetsList } = await import("./list-EYRN5JYC.js");
1164
+ const { runPresetsList } = await import("./list-HFWSMLGJ.js");
1128
1165
  await runPresetsList({ outputFormat: cliContext.outputFormat });
1129
1166
  break;
1130
1167
  }
@@ -1135,7 +1172,7 @@ async function main() {
1135
1172
  );
1136
1173
  }
1137
1174
  const org = await resolveOrg();
1138
- const { runPresetsRun } = await import("./run-YHDUUP66.js");
1175
+ const { runPresetsRun } = await import("./run-WJGHJPXN.js");
1139
1176
  await runPresetsRun({
1140
1177
  presetId: action,
1141
1178
  org,
@@ -1152,17 +1189,17 @@ async function main() {
1152
1189
  case "setup":
1153
1190
  case "install":
1154
1191
  case "i": {
1155
- const { setupSkill } = await import("./setup-skill-J7PZYVCE.js");
1192
+ const { setupSkill } = await import("./setup-skill-DR4KGQMG.js");
1156
1193
  setupSkill({ json: jsonMode, cloud: cli.flags.cloud });
1157
1194
  break;
1158
1195
  }
1159
1196
  case "uninstall": {
1160
- const { uninstallSkill } = await import("./setup-skill-J7PZYVCE.js");
1197
+ const { uninstallSkill } = await import("./setup-skill-DR4KGQMG.js");
1161
1198
  uninstallSkill({ json: jsonMode });
1162
1199
  break;
1163
1200
  }
1164
1201
  case "completions": {
1165
- const { printCompletions } = await import("./completions-EGQIARFC.js");
1202
+ const { printCompletions } = await import("./completions-D6SOLU2D.js");
1166
1203
  printCompletions(subcommand);
1167
1204
  break;
1168
1205
  }
@@ -3,7 +3,7 @@ import {
3
3
  installCompletions,
4
4
  printCompletions,
5
5
  uninstallCompletions
6
- } from "./chunk-IUZB3DQW.js";
6
+ } from "./chunk-Z76CUWSS.js";
7
7
  export {
8
8
  getCompletionScript,
9
9
  installCompletions,
@@ -7,8 +7,8 @@ import {
7
7
  } from "./chunk-VFT3TD3E.js";
8
8
  import {
9
9
  createApiClient
10
- } from "./chunk-LLWKCA3H.js";
11
- import "./chunk-EEG7T6WT.js";
10
+ } from "./chunk-RIKR2WFT.js";
11
+ import "./chunk-EGWK6SRQ.js";
12
12
  import "./chunk-TFCNKBRC.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-LLWKCA3H.js";
12
- import "./chunk-EEG7T6WT.js";
11
+ } from "./chunk-RIKR2WFT.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-LLWKCA3H.js";
12
- import "./chunk-EEG7T6WT.js";
11
+ } from "./chunk-RIKR2WFT.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
@@ -4,8 +4,8 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-LLWKCA3H.js";
8
- import "./chunk-EEG7T6WT.js";
7
+ } from "./chunk-RIKR2WFT.js";
8
+ import "./chunk-EGWK6SRQ.js";
9
9
  import {
10
10
  DateTime
11
11
  } from "./chunk-TFCNKBRC.js";
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-LLWKCA3H.js";
12
- import "./chunk-EEG7T6WT.js";
11
+ } from "./chunk-RIKR2WFT.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-LLWKCA3H.js";
12
- import "./chunk-EEG7T6WT.js";
11
+ } from "./chunk-RIKR2WFT.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
@@ -4,8 +4,8 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-LLWKCA3H.js";
8
- import "./chunk-EEG7T6WT.js";
7
+ } from "./chunk-RIKR2WFT.js";
8
+ import "./chunk-EGWK6SRQ.js";
9
9
  import "./chunk-TFCNKBRC.js";
10
10
  import "./chunk-U73SABXK.js";
11
11
 
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  PRESETS
3
- } from "./chunk-AGXMZFUU.js";
3
+ } from "./chunk-BJISZ3CY.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-LLWKCA3H.js";
10
- import "./chunk-EEG7T6WT.js";
9
+ import "./chunk-RIKR2WFT.js";
10
+ import "./chunk-EGWK6SRQ.js";
11
11
  import "./chunk-TFCNKBRC.js";
12
12
  import "./chunk-U73SABXK.js";
13
13
 
@@ -4,12 +4,12 @@ import {
4
4
  } from "./chunk-JCMWDZYY.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-LLWKCA3H.js";
7
+ } from "./chunk-RIKR2WFT.js";
8
8
  import {
9
9
  AuthCorruptedError,
10
10
  AuthExpiredError,
11
11
  AuthNotLoggedInError
12
- } from "./chunk-EEG7T6WT.js";
12
+ } from "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-LLWKCA3H.js";
12
- import "./chunk-EEG7T6WT.js";
11
+ } from "./chunk-RIKR2WFT.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createApiClient
3
- } from "./chunk-LLWKCA3H.js";
4
- import "./chunk-EEG7T6WT.js";
3
+ } from "./chunk-RIKR2WFT.js";
4
+ import "./chunk-EGWK6SRQ.js";
5
5
  import "./chunk-TFCNKBRC.js";
6
6
  import "./chunk-U73SABXK.js";
7
7
 
@@ -5,15 +5,16 @@ import {
5
5
  import {
6
6
  formatUserCode,
7
7
  isHeadlessEnv
8
- } from "./chunk-EBJMOX3Q.js";
8
+ } from "./chunk-PZV55KAR.js";
9
9
  import {
10
10
  fetchUserEmail,
11
11
  getCredentials,
12
+ hasApiKeyEnv,
12
13
  isLoggedIn,
13
14
  pollForToken,
14
15
  requestDeviceCode,
15
16
  storeCredentials
16
- } from "./chunk-EEG7T6WT.js";
17
+ } from "./chunk-EGWK6SRQ.js";
17
18
  import "./chunk-TFCNKBRC.js";
18
19
  import "./chunk-U73SABXK.js";
19
20
 
@@ -26,6 +27,14 @@ function Login() {
26
27
  const { exit } = useApp();
27
28
  const [state, setState] = useState({ phase: "checking" });
28
29
  useEffect(() => {
30
+ if (hasApiKeyEnv()) {
31
+ setState({
32
+ phase: "already-logged-in",
33
+ email: "(authenticated via CHAINPATROL_API_KEY)"
34
+ });
35
+ setTimeout(() => exit(), 100);
36
+ return;
37
+ }
29
38
  if (isLoggedIn()) {
30
39
  const creds = getCredentials();
31
40
  if (creds.email) {
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  isHeadlessEnv,
3
3
  runLoginFlow
4
- } from "./chunk-EBJMOX3Q.js";
5
- import "./chunk-EEG7T6WT.js";
4
+ } from "./chunk-PZV55KAR.js";
5
+ import "./chunk-EGWK6SRQ.js";
6
6
  import "./chunk-TFCNKBRC.js";
7
7
  import "./chunk-U73SABXK.js";
8
8
 
@@ -2,8 +2,8 @@ import {
2
2
  formatUserCode,
3
3
  isHeadlessEnv,
4
4
  runLoginFlow
5
- } from "./chunk-EBJMOX3Q.js";
6
- import "./chunk-EEG7T6WT.js";
5
+ } from "./chunk-PZV55KAR.js";
6
+ import "./chunk-EGWK6SRQ.js";
7
7
  import "./chunk-TFCNKBRC.js";
8
8
  import "./chunk-U73SABXK.js";
9
9
 
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  clearCredentials,
3
+ hasApiKeyEnv,
3
4
  isLoggedIn
4
- } from "./chunk-EEG7T6WT.js";
5
+ } from "./chunk-EGWK6SRQ.js";
5
6
  import "./chunk-U73SABXK.js";
6
7
 
7
8
  // src/commands/logout.tsx
@@ -9,6 +10,14 @@ import { Text, useApp } from "ink";
9
10
  import { jsx, jsxs } from "react/jsx-runtime";
10
11
  function Logout() {
11
12
  const { exit } = useApp();
13
+ if (hasApiKeyEnv()) {
14
+ setTimeout(() => exit(), 100);
15
+ return /* @__PURE__ */ jsxs(Text, { children: [
16
+ "Authenticated via ",
17
+ /* @__PURE__ */ jsx(Text, { bold: true, children: "CHAINPATROL_API_KEY" }),
18
+ "; unset the env var to log out."
19
+ ] });
20
+ }
12
21
  if (!isLoggedIn()) {
13
22
  setTimeout(() => exit(), 100);
14
23
  return /* @__PURE__ */ jsx(Text, { children: "Not currently logged in." });
@@ -1,11 +1,21 @@
1
1
  import {
2
2
  clearCredentials,
3
+ hasApiKeyEnv,
3
4
  isLoggedIn
4
- } from "./chunk-EEG7T6WT.js";
5
+ } from "./chunk-EGWK6SRQ.js";
5
6
  import "./chunk-U73SABXK.js";
6
7
 
7
8
  // src/commands/logout-json.ts
8
9
  async function logoutJson() {
10
+ if (hasApiKeyEnv()) {
11
+ console.log(
12
+ JSON.stringify({
13
+ status: "api_key_env_active",
14
+ message: "Authenticated via CHAINPATROL_API_KEY; unset the env var to log out."
15
+ })
16
+ );
17
+ return;
18
+ }
9
19
  if (!isLoggedIn()) {
10
20
  console.log(JSON.stringify({ status: "not_logged_in" }));
11
21
  return;
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-LLWKCA3H.js";
12
- import "./chunk-EEG7T6WT.js";
11
+ } from "./chunk-RIKR2WFT.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
@@ -1,14 +1,14 @@
1
1
  import {
2
2
  getPresetDefinition,
3
3
  runPreset
4
- } from "./chunk-AGXMZFUU.js";
4
+ } from "./chunk-BJISZ3CY.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-LLWKCA3H.js";
11
- import "./chunk-EEG7T6WT.js";
10
+ import "./chunk-RIKR2WFT.js";
11
+ import "./chunk-EGWK6SRQ.js";
12
12
  import "./chunk-TFCNKBRC.js";
13
13
  import "./chunk-U73SABXK.js";
14
14
 
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-LLWKCA3H.js";
12
- import "./chunk-EEG7T6WT.js";
11
+ } from "./chunk-RIKR2WFT.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
@@ -6,8 +6,8 @@ import {
6
6
  readInstalledSkillVersion,
7
7
  setupSkill,
8
8
  uninstallSkill
9
- } from "./chunk-ZN3VMRWG.js";
10
- import "./chunk-IUZB3DQW.js";
9
+ } from "./chunk-P4L4N5LM.js";
10
+ import "./chunk-Z76CUWSS.js";
11
11
  export {
12
12
  getBundledSkillContent,
13
13
  getBundledSkillVersion,
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-LLWKCA3H.js";
12
- import "./chunk-EEG7T6WT.js";
11
+ } from "./chunk-RIKR2WFT.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
@@ -4,8 +4,8 @@ import {
4
4
  } from "./chunk-VFT3TD3E.js";
5
5
  import {
6
6
  createApiClient
7
- } from "./chunk-LLWKCA3H.js";
8
- import "./chunk-EEG7T6WT.js";
7
+ } from "./chunk-RIKR2WFT.js";
8
+ import "./chunk-EGWK6SRQ.js";
9
9
  import "./chunk-TFCNKBRC.js";
10
10
  import "./chunk-U73SABXK.js";
11
11
 
@@ -8,8 +8,8 @@ import {
8
8
  } from "./chunk-VFT3TD3E.js";
9
9
  import {
10
10
  createApiClient
11
- } from "./chunk-LLWKCA3H.js";
12
- import "./chunk-EEG7T6WT.js";
11
+ } from "./chunk-RIKR2WFT.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
13
  import "./chunk-TFCNKBRC.js";
14
14
  import "./chunk-U73SABXK.js";
15
15
 
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.7.0",
5
+ "version": "0.9.0",
6
6
  "license": "UNLICENSED",
7
7
  "homepage": "https://chainpatrol.com/docs/cli",
8
8
  "keywords": [