@chainpatrol/cli 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -0
- package/README.md +2 -0
- package/dist/{breakdown-63FAOVL7.js → breakdown-MM5GSZKV.js} +1 -1
- package/dist/{check-7QDINQPT.js → check-GDKUHVQI.js} +1 -1
- package/dist/{chunk-BJISZ3CY.js → chunk-37KYJWB3.js} +1 -1
- package/dist/{chunk-R2DZGMGT.js → chunk-EPKNZRX6.js} +1 -1
- package/dist/{chunk-RIKR2WFT.js → chunk-SX3L6NKR.js} +85 -4
- package/dist/{chunk-Z76CUWSS.js → chunk-YXRFUYA6.js} +13 -2
- package/dist/cli.js +131 -29
- package/dist/{completions-D6SOLU2D.js → completions-62OW3B3Y.js} +1 -1
- package/dist/{configs-update-2IOSKZH5.js → configs-update-7X7657PB.js} +1 -1
- package/dist/{create-EWS3SFCH.js → create-FL4QQTPN.js} +1 -1
- package/dist/{drift-Q3VG3XG3.js → drift-JKBHWWFU.js} +1 -1
- package/dist/{found-22B7RZT5.js → found-3WI4XQG4.js} +1 -1
- package/dist/{healthcheck-C3AIMUJT.js → healthcheck-EVQVPGWW.js} +1 -1
- package/dist/{list-IA4CSOIY.js → list-5DN6X3DP.js} +1 -1
- package/dist/{list-DNRWKM5O.js → list-BLNIE4GL.js} +1 -1
- package/dist/{list-AYHDFSQG.js → list-E5XNR2YG.js} +1 -1
- package/dist/list-JPG2ZSR4.js +137 -0
- package/dist/{list-HFWSMLGJ.js → list-ULHPOUJV.js} +2 -2
- package/dist/{list-SDBLGBVW.js → list-XDGU6SJQ.js} +1 -1
- package/dist/{list-json-CEPGVUGF.js → list-json-R2MIXADX.js} +1 -1
- package/dist/organization-MJRKWC4Z.js +75 -0
- package/dist/{run-AQMJRFGL.js → run-2YZZGZYN.js} +1 -1
- package/dist/{run-YTWNQD5X.js → run-5E5EC3MP.js} +1 -1
- package/dist/{run-WJGHJPXN.js → run-EUVRC72M.js} +2 -2
- package/dist/{setup-skill-BLJLRCXL.js → setup-skill-HAENFIFQ.js} +2 -2
- package/dist/{snapshot-C5MZWJTW.js → snapshot-3FUJICJJ.js} +1 -1
- package/dist/{summary-NQDZQEOD.js → summary-RVYQUKWV.js} +1 -1
- package/dist/{validate-5DVPSXJP.js → validate-UOVKEAJM.js} +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# @chainpatrol/cli
|
|
2
2
|
|
|
3
|
+
## 0.11.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 21e4455: Add `chainpatrol takedowns list` and `chainpatrol metrics organization`
|
|
8
|
+
commands so agents can pull per-takedown detail and org-scoped metrics
|
|
9
|
+
from the CLI instead of stopping at `queues snapshot`'s aggregate
|
|
10
|
+
counts.
|
|
11
|
+
|
|
12
|
+
`takedowns list` wraps `POST /takedowns/list` with filters for status,
|
|
13
|
+
asset type, liveness, date range, search, sort, and pagination, and
|
|
14
|
+
emits the standard human/json/markdown/csv shapes. `metrics
|
|
15
|
+
organization` wraps the documented `GET /organization/metrics`
|
|
16
|
+
endpoint, complementing the existing `metrics summary`/`found`/
|
|
17
|
+
`breakdown` subcommands.
|
|
18
|
+
|
|
19
|
+
Both endpoints now also accept a user-session bearer token (pass
|
|
20
|
+
`--org <slug>`) in addition to an API key, so customer-success operators
|
|
21
|
+
logged in via `chainpatrol login` can use them without provisioning a
|
|
22
|
+
key.
|
|
23
|
+
|
|
3
24
|
## 0.10.0
|
|
4
25
|
|
|
5
26
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -22,7 +22,9 @@ chainpatrol metrics found --org my-org --this-week
|
|
|
22
22
|
chainpatrol reports create --org my-org --title "Phishing report" --asset "https://phish.example:BLOCKED"
|
|
23
23
|
chainpatrol reports list --org my-org --limit 10
|
|
24
24
|
chainpatrol metrics breakdown --org my-org --by type --from 2026-01-01 --to 2026-01-08
|
|
25
|
+
chainpatrol metrics organization --org my-org --from 2026-01-01 --to 2026-02-01
|
|
25
26
|
chainpatrol queues snapshot --all --output markdown
|
|
27
|
+
chainpatrol takedowns list --org my-org --takedown-status TODO,IN_PROGRESS --per-page 50
|
|
26
28
|
chainpatrol presets run cs-weekly-health --org my-org --output json
|
|
27
29
|
```
|
|
28
30
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
installCompletions,
|
|
3
3
|
uninstallCompletions
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-YXRFUYA6.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";
|
|
@@ -17,6 +17,26 @@ function parseIsoDate(value) {
|
|
|
17
17
|
}
|
|
18
18
|
return dt.toJSDate();
|
|
19
19
|
}
|
|
20
|
+
function parseIsoDateString(value) {
|
|
21
|
+
return parseIsoDate(value)?.toISOString();
|
|
22
|
+
}
|
|
23
|
+
var TAKEDOWN_STATUSES = [
|
|
24
|
+
"TODO",
|
|
25
|
+
"IN_PROGRESS",
|
|
26
|
+
"COMPLETED",
|
|
27
|
+
"CANCELLED",
|
|
28
|
+
"PENDING_RETRACTION",
|
|
29
|
+
"RETRACTION_SENT",
|
|
30
|
+
"RETRACTED",
|
|
31
|
+
"PENDING_INPUT"
|
|
32
|
+
];
|
|
33
|
+
var LIVENESS_STATUSES = ["UNKNOWN", "ALIVE", "DEAD"];
|
|
34
|
+
var TAKEDOWN_SORT_KEYS = [
|
|
35
|
+
"updatedAt",
|
|
36
|
+
"createdAt",
|
|
37
|
+
"takedownStatus",
|
|
38
|
+
"takedownUpdatedAt"
|
|
39
|
+
];
|
|
20
40
|
var REQUEST_TIMEOUT_MS = 3e4;
|
|
21
41
|
function defaultGetCredential() {
|
|
22
42
|
const envKey = process.env.CHAINPATROL_API_KEY;
|
|
@@ -29,16 +49,49 @@ function createApiClient(options) {
|
|
|
29
49
|
const config = getConfig();
|
|
30
50
|
const apiUrl = options?.apiUrl ?? config.apiUrl;
|
|
31
51
|
const getCredential = options?.getCredential ?? (options?.getToken ? () => ({ kind: "bearer", value: options.getToken() }) : defaultGetCredential);
|
|
32
|
-
|
|
52
|
+
function buildAuthHeaders() {
|
|
33
53
|
const credential = getCredential();
|
|
34
|
-
const headers = {
|
|
35
|
-
"Content-Type": "application/json"
|
|
36
|
-
};
|
|
54
|
+
const headers = {};
|
|
37
55
|
if (credential.kind === "api-key") {
|
|
38
56
|
headers["x-api-key"] = credential.value;
|
|
39
57
|
} else {
|
|
40
58
|
headers["Authorization"] = `Bearer ${credential.value}`;
|
|
41
59
|
}
|
|
60
|
+
return headers;
|
|
61
|
+
}
|
|
62
|
+
async function requestGet(path, query) {
|
|
63
|
+
const headers = buildAuthHeaders();
|
|
64
|
+
const search = new URLSearchParams();
|
|
65
|
+
for (const [key, value] of Object.entries(query)) {
|
|
66
|
+
if (value !== void 0 && value !== "") {
|
|
67
|
+
search.set(key, value);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const queryString = search.toString();
|
|
71
|
+
const url = `${apiUrl}/api/v2${path}${queryString ? `?${queryString}` : ""}`;
|
|
72
|
+
let res;
|
|
73
|
+
try {
|
|
74
|
+
res = await fetch(url, {
|
|
75
|
+
method: "GET",
|
|
76
|
+
headers,
|
|
77
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
78
|
+
});
|
|
79
|
+
} catch (err) {
|
|
80
|
+
if (err instanceof DOMException && err.name === "TimeoutError") {
|
|
81
|
+
throw new Error(
|
|
82
|
+
"Request timed out. Check your network connection and try again."
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
if (err instanceof TypeError && err.code === "ECONNREFUSED") {
|
|
86
|
+
throw new Error(`Cannot connect to ${apiUrl}. Is the server running?`);
|
|
87
|
+
}
|
|
88
|
+
throw new Error("Network error. Check your internet connection and try again.");
|
|
89
|
+
}
|
|
90
|
+
return handleResponse(res);
|
|
91
|
+
}
|
|
92
|
+
async function request(path, body) {
|
|
93
|
+
const headers = buildAuthHeaders();
|
|
94
|
+
headers["Content-Type"] = "application/json";
|
|
42
95
|
let res;
|
|
43
96
|
try {
|
|
44
97
|
res = await fetch(`${apiUrl}/api/v2${path}`, {
|
|
@@ -58,6 +111,9 @@ function createApiClient(options) {
|
|
|
58
111
|
}
|
|
59
112
|
throw new Error("Network error. Check your internet connection and try again.");
|
|
60
113
|
}
|
|
114
|
+
return handleResponse(res);
|
|
115
|
+
}
|
|
116
|
+
async function handleResponse(res) {
|
|
61
117
|
if (!res.ok) {
|
|
62
118
|
let errorMessageFromResponse = null;
|
|
63
119
|
try {
|
|
@@ -157,6 +213,28 @@ function createApiClient(options) {
|
|
|
157
213
|
reportedByCustomer: input.reportedByCustomer
|
|
158
214
|
});
|
|
159
215
|
},
|
|
216
|
+
listTakedowns(input) {
|
|
217
|
+
return request("/takedowns/list", {
|
|
218
|
+
organizationSlug: input.organizationSlug,
|
|
219
|
+
query: input.query,
|
|
220
|
+
startDate: parseIsoDateString(input.startDate),
|
|
221
|
+
endDate: parseIsoDateString(input.endDate),
|
|
222
|
+
assetType: input.assetType,
|
|
223
|
+
takedownStatus: input.takedownStatus,
|
|
224
|
+
livenessStatus: input.livenessStatus,
|
|
225
|
+
sorting: input.sorting,
|
|
226
|
+
per_page: input.perPage,
|
|
227
|
+
next_page: input.nextPage
|
|
228
|
+
});
|
|
229
|
+
},
|
|
230
|
+
getOrganizationMetrics(input) {
|
|
231
|
+
return requestGet("/organization/metrics", {
|
|
232
|
+
organizationSlug: input.organizationSlug,
|
|
233
|
+
brandSlug: input.brandSlug,
|
|
234
|
+
startDate: parseIsoDateString(input.startDate),
|
|
235
|
+
endDate: parseIsoDateString(input.endDate)
|
|
236
|
+
});
|
|
237
|
+
},
|
|
160
238
|
listHealthchecks() {
|
|
161
239
|
return request("/healthchecks/list", {});
|
|
162
240
|
},
|
|
@@ -170,5 +248,8 @@ function createApiClient(options) {
|
|
|
170
248
|
}
|
|
171
249
|
|
|
172
250
|
export {
|
|
251
|
+
TAKEDOWN_STATUSES,
|
|
252
|
+
LIVENESS_STATUSES,
|
|
253
|
+
TAKEDOWN_SORT_KEYS,
|
|
173
254
|
createApiClient
|
|
174
255
|
};
|
|
@@ -22,6 +22,7 @@ _chainpatrol() {
|
|
|
22
22
|
'metrics:Query organization metrics'
|
|
23
23
|
'reports:Create and list reports from terminal'
|
|
24
24
|
'queues:Snapshot operations queues'
|
|
25
|
+
'takedowns:List individual takedown records'
|
|
25
26
|
'presets:Run saved workflows'
|
|
26
27
|
'setup:Install Claude Code skill and shell completions'
|
|
27
28
|
'uninstall:Remove Claude Code skill and shell completions'
|
|
@@ -84,6 +85,7 @@ _chainpatrol() {
|
|
|
84
85
|
'summary:Show metrics summary'
|
|
85
86
|
'found:Show found threats count'
|
|
86
87
|
'breakdown:Show metrics breakdown'
|
|
88
|
+
'organization:Get organization metrics (official endpoint)'
|
|
87
89
|
)
|
|
88
90
|
_describe 'subcommand' metrics_subcommands
|
|
89
91
|
;;
|
|
@@ -100,6 +102,11 @@ _chainpatrol() {
|
|
|
100
102
|
queues_subcommands=('snapshot:Show queue snapshot')
|
|
101
103
|
_describe 'subcommand' queues_subcommands
|
|
102
104
|
;;
|
|
105
|
+
takedowns)
|
|
106
|
+
local -a takedowns_subcommands
|
|
107
|
+
takedowns_subcommands=('list:List individual takedown records')
|
|
108
|
+
_describe 'subcommand' takedowns_subcommands
|
|
109
|
+
;;
|
|
103
110
|
presets)
|
|
104
111
|
local -a presets_subcommands
|
|
105
112
|
presets_subcommands=(
|
|
@@ -124,7 +131,7 @@ var BASH_COMPLETION = `_chainpatrol() {
|
|
|
124
131
|
COMPREPLY=()
|
|
125
132
|
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
126
133
|
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
127
|
-
commands="login logout asset configs detections metrics reports queues presets setup uninstall completions help"
|
|
134
|
+
commands="login logout asset configs detections metrics reports queues takedowns presets setup uninstall completions help"
|
|
128
135
|
|
|
129
136
|
case "\${prev}" in
|
|
130
137
|
chainpatrol)
|
|
@@ -144,7 +151,7 @@ var BASH_COMPLETION = `_chainpatrol() {
|
|
|
144
151
|
return 0
|
|
145
152
|
;;
|
|
146
153
|
metrics)
|
|
147
|
-
COMPREPLY=( $(compgen -W "summary found breakdown" -- "\${cur}") )
|
|
154
|
+
COMPREPLY=( $(compgen -W "summary found breakdown organization" -- "\${cur}") )
|
|
148
155
|
return 0
|
|
149
156
|
;;
|
|
150
157
|
reports)
|
|
@@ -155,6 +162,10 @@ var BASH_COMPLETION = `_chainpatrol() {
|
|
|
155
162
|
COMPREPLY=( $(compgen -W "snapshot" -- "\${cur}") )
|
|
156
163
|
return 0
|
|
157
164
|
;;
|
|
165
|
+
takedowns)
|
|
166
|
+
COMPREPLY=( $(compgen -W "list" -- "\${cur}") )
|
|
167
|
+
return 0
|
|
168
|
+
;;
|
|
158
169
|
presets)
|
|
159
170
|
COMPREPLY=( $(compgen -W "list run" -- "\${cur}") )
|
|
160
171
|
return 0
|
package/dist/cli.js
CHANGED
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
getCliVersion,
|
|
14
14
|
isSkillInstalled,
|
|
15
15
|
readInstalledSkillVersion
|
|
16
|
-
} from "./chunk-
|
|
17
|
-
import "./chunk-
|
|
16
|
+
} from "./chunk-EPKNZRX6.js";
|
|
17
|
+
import "./chunk-YXRFUYA6.js";
|
|
18
18
|
import {
|
|
19
19
|
DateTime
|
|
20
20
|
} from "./chunk-TFCNKBRC.js";
|
|
@@ -207,12 +207,28 @@ var HELP = {
|
|
|
207
207
|
},
|
|
208
208
|
metrics: {
|
|
209
209
|
description: "Query organization metrics and breakdowns.",
|
|
210
|
-
usage: "chainpatrol metrics <summary|found|breakdown>",
|
|
210
|
+
usage: "chainpatrol metrics <summary|found|breakdown|organization>",
|
|
211
211
|
options: ["--org <slug> Organization slug"],
|
|
212
212
|
examples: [
|
|
213
213
|
"chainpatrol metrics summary --org acme --this-week",
|
|
214
214
|
"chainpatrol metrics found --org acme --from 2025-01-01 --to 2025-02-01",
|
|
215
|
-
"chainpatrol metrics breakdown --org acme --by day --this-week"
|
|
215
|
+
"chainpatrol metrics breakdown --org acme --by day --this-week",
|
|
216
|
+
"chainpatrol metrics organization --org acme --from 2025-01-01 --to 2025-02-01"
|
|
217
|
+
]
|
|
218
|
+
},
|
|
219
|
+
"metrics organization": {
|
|
220
|
+
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).",
|
|
221
|
+
usage: "chainpatrol metrics organization [--org <slug>] [--from ISO --to ISO]",
|
|
222
|
+
options: [
|
|
223
|
+
"--org <slug> Organization slug (omit when using an API key)",
|
|
224
|
+
"--brand-slug <slug> Filter by brand slug (requires brands-metrics flag)",
|
|
225
|
+
"--from <iso> Start date (ISO 8601, e.g. 2026-01-01)",
|
|
226
|
+
"--to <iso> End date (ISO 8601)"
|
|
227
|
+
],
|
|
228
|
+
examples: [
|
|
229
|
+
"chainpatrol metrics organization --org acme",
|
|
230
|
+
"chainpatrol metrics organization --org acme --from 2026-01-01 --to 2026-02-01",
|
|
231
|
+
"CHAINPATROL_API_KEY=... chainpatrol metrics organization --json"
|
|
216
232
|
]
|
|
217
233
|
},
|
|
218
234
|
"metrics summary": {
|
|
@@ -295,6 +311,38 @@ var HELP = {
|
|
|
295
311
|
"chainpatrol queues snapshot --all"
|
|
296
312
|
]
|
|
297
313
|
},
|
|
314
|
+
takedowns: {
|
|
315
|
+
description: "List individual takedown records for an organization.",
|
|
316
|
+
usage: "chainpatrol takedowns list [--org <slug>] [filters]",
|
|
317
|
+
examples: [
|
|
318
|
+
"chainpatrol takedowns list --org acme",
|
|
319
|
+
"chainpatrol takedowns list --org acme --takedown-status TODO,IN_PROGRESS",
|
|
320
|
+
"chainpatrol takedowns list --org acme --from 2026-01-01 --to 2026-02-01 --per-page 50"
|
|
321
|
+
]
|
|
322
|
+
},
|
|
323
|
+
"takedowns list": {
|
|
324
|
+
description: "List takedowns for an organization. Defaults to takedowns updated in the last 30 days. Works with both API key auth (org derived from key) and session auth (pass --org).",
|
|
325
|
+
usage: "chainpatrol takedowns list [--org <slug>] [filters]",
|
|
326
|
+
options: [
|
|
327
|
+
"--org <slug> Organization slug (omit when using an API key)",
|
|
328
|
+
"--query <text> Search asset content (substring match)",
|
|
329
|
+
"--from <iso> Filter by updatedAt >= ISO date",
|
|
330
|
+
"--to <iso> Filter by updatedAt <= ISO date",
|
|
331
|
+
"--asset-type <list> Comma list (URL,TWITTER,TELEGRAM,...)",
|
|
332
|
+
"--takedown-status <list> Comma list (TODO,IN_PROGRESS,COMPLETED,CANCELLED,PENDING_INPUT,...)",
|
|
333
|
+
"--liveness-status <list> Comma list (ALIVE,DEAD,UNKNOWN)",
|
|
334
|
+
"--sort-by <key> Sort key: updatedAt|createdAt|takedownStatus|takedownUpdatedAt",
|
|
335
|
+
"--sort-direction <dir> asc|desc (default desc)",
|
|
336
|
+
"--per-page <n> Page size 1-100 (default 10; --limit also accepted)",
|
|
337
|
+
"--next-page <cursor> Cursor returned from a previous response"
|
|
338
|
+
],
|
|
339
|
+
examples: [
|
|
340
|
+
"chainpatrol takedowns list --org acme --takedown-status TODO,IN_PROGRESS",
|
|
341
|
+
"chainpatrol takedowns list --org acme --asset-type URL --liveness-status ALIVE",
|
|
342
|
+
"chainpatrol takedowns list --org acme --per-page 100 --output csv",
|
|
343
|
+
"CHAINPATROL_API_KEY=... chainpatrol takedowns list --json"
|
|
344
|
+
]
|
|
345
|
+
},
|
|
298
346
|
"queues snapshot": {
|
|
299
347
|
description: "Snapshot pending review proposals and open takedowns.",
|
|
300
348
|
usage: "chainpatrol queues snapshot [--org <slug>|--all] [--window-hours <n>]",
|
|
@@ -421,6 +469,7 @@ function getTopLevelHelp() {
|
|
|
421
469
|
" metrics Query organization metrics and breakdowns",
|
|
422
470
|
" reports Create and list reports from terminal",
|
|
423
471
|
" queues Snapshot operations review/takedown queues",
|
|
472
|
+
" takedowns List individual takedown records",
|
|
424
473
|
" orgs List organizations and filter by status/services",
|
|
425
474
|
" presets Run saved workflows for common jobs",
|
|
426
475
|
" setup Install Claude Code skill and shell completions",
|
|
@@ -592,6 +641,7 @@ var COMMANDS = [
|
|
|
592
641
|
"metrics",
|
|
593
642
|
"reports",
|
|
594
643
|
"queues",
|
|
644
|
+
"takedowns",
|
|
595
645
|
"orgs",
|
|
596
646
|
"presets",
|
|
597
647
|
"setup",
|
|
@@ -675,6 +725,14 @@ ${getTopLevelHelp()}
|
|
|
675
725
|
serviceAutomated: { type: "string" },
|
|
676
726
|
serviceManual: { type: "string" },
|
|
677
727
|
query: { type: "string" },
|
|
728
|
+
assetType: { type: "string" },
|
|
729
|
+
takedownStatus: { type: "string" },
|
|
730
|
+
livenessStatus: { type: "string" },
|
|
731
|
+
nextPage: { type: "string" },
|
|
732
|
+
sortBy: { type: "string" },
|
|
733
|
+
sortDirection: { type: "string" },
|
|
734
|
+
perPage: { type: "number" },
|
|
735
|
+
brandSlug: { type: "string" },
|
|
678
736
|
help: { type: "boolean", shortFlag: "h" },
|
|
679
737
|
version: { type: "boolean", shortFlag: "V" },
|
|
680
738
|
quiet: { type: "boolean", default: false, shortFlag: "q" },
|
|
@@ -773,18 +831,23 @@ function parseBrandIds() {
|
|
|
773
831
|
function parseAssetInputs() {
|
|
774
832
|
return getRepeatedFlag("asset");
|
|
775
833
|
}
|
|
834
|
+
function parseCsvList(value) {
|
|
835
|
+
if (!value) return void 0;
|
|
836
|
+
const items = value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
837
|
+
return items.length > 0 ? items : void 0;
|
|
838
|
+
}
|
|
776
839
|
function parseAttachmentUrls() {
|
|
777
840
|
const values = getRepeatedFlag("attachment-url").filter(Boolean);
|
|
778
841
|
return values.length > 0 ? values : void 0;
|
|
779
842
|
}
|
|
780
843
|
async function handleConfigsList(org) {
|
|
781
844
|
if (jsonMode) {
|
|
782
|
-
const { listConfigsJson } = await import("./list-json-
|
|
845
|
+
const { listConfigsJson } = await import("./list-json-R2MIXADX.js");
|
|
783
846
|
await listConfigsJson({ org });
|
|
784
847
|
return;
|
|
785
848
|
}
|
|
786
849
|
const { render } = await import("ink");
|
|
787
|
-
const { default: ConfigsList } = await import("./list-
|
|
850
|
+
const { default: ConfigsList } = await import("./list-5DN6X3DP.js");
|
|
788
851
|
const { default: React } = await import("react");
|
|
789
852
|
render(React.createElement(ConfigsList, { org }));
|
|
790
853
|
}
|
|
@@ -884,7 +947,7 @@ async function main() {
|
|
|
884
947
|
const positionals = cli.input.slice(2);
|
|
885
948
|
const flagAssets = parseAssetInputs();
|
|
886
949
|
const contents = [...positionals, ...flagAssets];
|
|
887
|
-
const { runAssetCheck } = await import("./check-
|
|
950
|
+
const { runAssetCheck } = await import("./check-GDKUHVQI.js");
|
|
888
951
|
await runAssetCheck({
|
|
889
952
|
contents,
|
|
890
953
|
json: jsonMode,
|
|
@@ -901,7 +964,7 @@ async function main() {
|
|
|
901
964
|
case "detections": {
|
|
902
965
|
const org = await resolveOrg();
|
|
903
966
|
if (subcommand === "healthcheck") {
|
|
904
|
-
const { runDetectionsHealthcheck } = await import("./healthcheck-
|
|
967
|
+
const { runDetectionsHealthcheck } = await import("./healthcheck-EVQVPGWW.js");
|
|
905
968
|
await runDetectionsHealthcheck({
|
|
906
969
|
org,
|
|
907
970
|
source: cli.flags.source,
|
|
@@ -916,7 +979,7 @@ async function main() {
|
|
|
916
979
|
break;
|
|
917
980
|
}
|
|
918
981
|
if (subcommand === "validate") {
|
|
919
|
-
const { runDetectionsValidate } = await import("./validate-
|
|
982
|
+
const { runDetectionsValidate } = await import("./validate-UOVKEAJM.js");
|
|
920
983
|
await runDetectionsValidate({
|
|
921
984
|
org,
|
|
922
985
|
source: cli.flags.source,
|
|
@@ -931,7 +994,7 @@ async function main() {
|
|
|
931
994
|
break;
|
|
932
995
|
}
|
|
933
996
|
if (subcommand === "drift") {
|
|
934
|
-
const { runDetectionsDrift } = await import("./drift-
|
|
997
|
+
const { runDetectionsDrift } = await import("./drift-JKBHWWFU.js");
|
|
935
998
|
await runDetectionsDrift({
|
|
936
999
|
org,
|
|
937
1000
|
source: cli.flags.source,
|
|
@@ -945,7 +1008,7 @@ async function main() {
|
|
|
945
1008
|
break;
|
|
946
1009
|
}
|
|
947
1010
|
if (subcommand === "run") {
|
|
948
|
-
const { runDetectionsRun } = await import("./run-
|
|
1011
|
+
const { runDetectionsRun } = await import("./run-5E5EC3MP.js");
|
|
949
1012
|
await runDetectionsRun({
|
|
950
1013
|
org,
|
|
951
1014
|
configId: cli.flags.configId,
|
|
@@ -964,7 +1027,7 @@ async function main() {
|
|
|
964
1027
|
break;
|
|
965
1028
|
}
|
|
966
1029
|
if (action === "run") {
|
|
967
|
-
const { runDetectionsRun } = await import("./run-
|
|
1030
|
+
const { runDetectionsRun } = await import("./run-5E5EC3MP.js");
|
|
968
1031
|
await runDetectionsRun({
|
|
969
1032
|
org,
|
|
970
1033
|
configId: cli.flags.configId,
|
|
@@ -982,7 +1045,7 @@ async function main() {
|
|
|
982
1045
|
throw new Error("detections configs update requires --config-id");
|
|
983
1046
|
}
|
|
984
1047
|
const configPatch = getConfigPatchFromSetFlags();
|
|
985
|
-
const { runDetectionsConfigsUpdate } = await import("./configs-update-
|
|
1048
|
+
const { runDetectionsConfigsUpdate } = await import("./configs-update-7X7657PB.js");
|
|
986
1049
|
await runDetectionsConfigsUpdate({
|
|
987
1050
|
org,
|
|
988
1051
|
configId: cli.flags.configId,
|
|
@@ -1007,9 +1070,22 @@ async function main() {
|
|
|
1007
1070
|
);
|
|
1008
1071
|
}
|
|
1009
1072
|
case "metrics": {
|
|
1073
|
+
if (subcommand === "organization" || subcommand === "org") {
|
|
1074
|
+
const org2 = await tryResolveOrg();
|
|
1075
|
+
const { runMetricsOrganization } = await import("./organization-MJRKWC4Z.js");
|
|
1076
|
+
await runMetricsOrganization({
|
|
1077
|
+
org: org2,
|
|
1078
|
+
brandSlug: cli.flags.brandSlug,
|
|
1079
|
+
from: cli.flags.from,
|
|
1080
|
+
to: cli.flags.to,
|
|
1081
|
+
json: jsonMode,
|
|
1082
|
+
outputFormat: cliContext.outputFormat
|
|
1083
|
+
});
|
|
1084
|
+
break;
|
|
1085
|
+
}
|
|
1010
1086
|
const org = await resolveOrg();
|
|
1011
1087
|
if (subcommand === "summary") {
|
|
1012
|
-
const { runMetricsSummary } = await import("./summary-
|
|
1088
|
+
const { runMetricsSummary } = await import("./summary-RVYQUKWV.js");
|
|
1013
1089
|
await runMetricsSummary({
|
|
1014
1090
|
org,
|
|
1015
1091
|
from: cli.flags.from,
|
|
@@ -1021,7 +1097,7 @@ async function main() {
|
|
|
1021
1097
|
break;
|
|
1022
1098
|
}
|
|
1023
1099
|
if (subcommand === "found") {
|
|
1024
|
-
const { runMetricsFound } = await import("./found-
|
|
1100
|
+
const { runMetricsFound } = await import("./found-3WI4XQG4.js");
|
|
1025
1101
|
await runMetricsFound({
|
|
1026
1102
|
org,
|
|
1027
1103
|
from: cli.flags.from,
|
|
@@ -1038,7 +1114,7 @@ async function main() {
|
|
|
1038
1114
|
if (!by || !["day", "type", "brand"].includes(by)) {
|
|
1039
1115
|
throw new Error("metrics breakdown requires --by <day|type|brand>");
|
|
1040
1116
|
}
|
|
1041
|
-
const { runMetricsBreakdown } = await import("./breakdown-
|
|
1117
|
+
const { runMetricsBreakdown } = await import("./breakdown-MM5GSZKV.js");
|
|
1042
1118
|
await runMetricsBreakdown({
|
|
1043
1119
|
org,
|
|
1044
1120
|
by,
|
|
@@ -1050,15 +1126,15 @@ async function main() {
|
|
|
1050
1126
|
});
|
|
1051
1127
|
break;
|
|
1052
1128
|
}
|
|
1053
|
-
const hint = subcommand ? suggest(subcommand, ["summary", "found", "breakdown"]) : null;
|
|
1129
|
+
const hint = subcommand ? suggest(subcommand, ["summary", "found", "breakdown", "organization"]) : null;
|
|
1054
1130
|
throw new Error(
|
|
1055
|
-
subcommand ? `Unknown subcommand: metrics ${subcommand}${hint ? `. Did you mean "metrics ${hint}"?` : ""}` : "Usage: chainpatrol metrics <summary|found|breakdown>"
|
|
1131
|
+
subcommand ? `Unknown subcommand: metrics ${subcommand}${hint ? `. Did you mean "metrics ${hint}"?` : ""}` : "Usage: chainpatrol metrics <summary|found|breakdown|organization>"
|
|
1056
1132
|
);
|
|
1057
1133
|
}
|
|
1058
1134
|
case "reports": {
|
|
1059
1135
|
if (subcommand === "list") {
|
|
1060
1136
|
const org = await resolveOrg();
|
|
1061
|
-
const { runReportsList } = await import("./list-
|
|
1137
|
+
const { runReportsList } = await import("./list-E5XNR2YG.js");
|
|
1062
1138
|
await runReportsList({
|
|
1063
1139
|
org,
|
|
1064
1140
|
limit: cli.flags.limit,
|
|
@@ -1074,7 +1150,7 @@ async function main() {
|
|
|
1074
1150
|
}
|
|
1075
1151
|
if (subcommand === "create") {
|
|
1076
1152
|
const org = await tryResolveOrg();
|
|
1077
|
-
const { runReportsCreate } = await import("./create-
|
|
1153
|
+
const { runReportsCreate } = await import("./create-FL4QQTPN.js");
|
|
1078
1154
|
await runReportsCreate({
|
|
1079
1155
|
org,
|
|
1080
1156
|
title: cli.flags.title,
|
|
@@ -1096,9 +1172,35 @@ async function main() {
|
|
|
1096
1172
|
subcommand ? `Unknown subcommand: reports ${subcommand}${hint ? `. Did you mean "reports ${hint}"?` : ""}` : "Usage: chainpatrol reports <create|list>"
|
|
1097
1173
|
);
|
|
1098
1174
|
}
|
|
1175
|
+
case "takedowns": {
|
|
1176
|
+
if (subcommand === "list") {
|
|
1177
|
+
const org = await tryResolveOrg();
|
|
1178
|
+
const { runTakedownsList } = await import("./list-JPG2ZSR4.js");
|
|
1179
|
+
await runTakedownsList({
|
|
1180
|
+
org,
|
|
1181
|
+
query: cli.flags.query,
|
|
1182
|
+
from: cli.flags.from,
|
|
1183
|
+
to: cli.flags.to,
|
|
1184
|
+
assetType: parseCsvList(cli.flags.assetType),
|
|
1185
|
+
takedownStatus: parseCsvList(cli.flags.takedownStatus),
|
|
1186
|
+
livenessStatus: parseCsvList(cli.flags.livenessStatus),
|
|
1187
|
+
sortBy: cli.flags.sortBy,
|
|
1188
|
+
sortDirection: cli.flags.sortDirection,
|
|
1189
|
+
perPage: cli.flags.perPage ?? cli.flags.limit,
|
|
1190
|
+
nextPage: cli.flags.nextPage,
|
|
1191
|
+
json: jsonMode,
|
|
1192
|
+
outputFormat: cliContext.outputFormat
|
|
1193
|
+
});
|
|
1194
|
+
break;
|
|
1195
|
+
}
|
|
1196
|
+
const hint = subcommand ? suggest(subcommand, ["list"]) : null;
|
|
1197
|
+
throw new Error(
|
|
1198
|
+
subcommand ? `Unknown subcommand: takedowns ${subcommand}${hint ? `. Did you mean "takedowns ${hint}"?` : ""}` : "Usage: chainpatrol takedowns list [--org <slug>] [filters]"
|
|
1199
|
+
);
|
|
1200
|
+
}
|
|
1099
1201
|
case "queues": {
|
|
1100
1202
|
if (subcommand === "snapshot") {
|
|
1101
|
-
const { runQueuesSnapshot } = await import("./snapshot-
|
|
1203
|
+
const { runQueuesSnapshot } = await import("./snapshot-3FUJICJJ.js");
|
|
1102
1204
|
await runQueuesSnapshot({
|
|
1103
1205
|
org: cli.flags.org,
|
|
1104
1206
|
all: cli.flags.all,
|
|
@@ -1116,7 +1218,7 @@ async function main() {
|
|
|
1116
1218
|
}
|
|
1117
1219
|
case "orgs": {
|
|
1118
1220
|
if (subcommand === "list") {
|
|
1119
|
-
const { runOrgsList } = await import("./list-
|
|
1221
|
+
const { runOrgsList } = await import("./list-XDGU6SJQ.js");
|
|
1120
1222
|
await runOrgsList({
|
|
1121
1223
|
query: cli.flags.query,
|
|
1122
1224
|
subscriptionStatus: cli.flags.subscriptionStatus,
|
|
@@ -1136,7 +1238,7 @@ async function main() {
|
|
|
1136
1238
|
}
|
|
1137
1239
|
case "healthchecks": {
|
|
1138
1240
|
if (subcommand === "list") {
|
|
1139
|
-
const { runHealthchecksList } = await import("./list-
|
|
1241
|
+
const { runHealthchecksList } = await import("./list-BLNIE4GL.js");
|
|
1140
1242
|
await runHealthchecksList({
|
|
1141
1243
|
json: jsonMode,
|
|
1142
1244
|
outputFormat: cliContext.outputFormat
|
|
@@ -1150,7 +1252,7 @@ async function main() {
|
|
|
1150
1252
|
thresholds.minResults = cli.flags.minResults;
|
|
1151
1253
|
if (cli.flags.lookbackHours !== void 0)
|
|
1152
1254
|
thresholds.lookbackHours = cli.flags.lookbackHours;
|
|
1153
|
-
const { runHealthchecksRun } = await import("./run-
|
|
1255
|
+
const { runHealthchecksRun } = await import("./run-2YZZGZYN.js");
|
|
1154
1256
|
await runHealthchecksRun({
|
|
1155
1257
|
org,
|
|
1156
1258
|
id: action,
|
|
@@ -1168,7 +1270,7 @@ async function main() {
|
|
|
1168
1270
|
}
|
|
1169
1271
|
case "presets": {
|
|
1170
1272
|
if (subcommand === "list") {
|
|
1171
|
-
const { runPresetsList } = await import("./list-
|
|
1273
|
+
const { runPresetsList } = await import("./list-ULHPOUJV.js");
|
|
1172
1274
|
await runPresetsList({ outputFormat: cliContext.outputFormat });
|
|
1173
1275
|
break;
|
|
1174
1276
|
}
|
|
@@ -1179,7 +1281,7 @@ async function main() {
|
|
|
1179
1281
|
);
|
|
1180
1282
|
}
|
|
1181
1283
|
const org = await resolveOrg();
|
|
1182
|
-
const { runPresetsRun } = await import("./run-
|
|
1284
|
+
const { runPresetsRun } = await import("./run-EUVRC72M.js");
|
|
1183
1285
|
await runPresetsRun({
|
|
1184
1286
|
presetId: action,
|
|
1185
1287
|
org,
|
|
@@ -1196,17 +1298,17 @@ async function main() {
|
|
|
1196
1298
|
case "setup":
|
|
1197
1299
|
case "install":
|
|
1198
1300
|
case "i": {
|
|
1199
|
-
const { setupSkill } = await import("./setup-skill-
|
|
1301
|
+
const { setupSkill } = await import("./setup-skill-HAENFIFQ.js");
|
|
1200
1302
|
setupSkill({ json: jsonMode, cloud: cli.flags.cloud });
|
|
1201
1303
|
break;
|
|
1202
1304
|
}
|
|
1203
1305
|
case "uninstall": {
|
|
1204
|
-
const { uninstallSkill } = await import("./setup-skill-
|
|
1306
|
+
const { uninstallSkill } = await import("./setup-skill-HAENFIFQ.js");
|
|
1205
1307
|
uninstallSkill({ json: jsonMode });
|
|
1206
1308
|
break;
|
|
1207
1309
|
}
|
|
1208
1310
|
case "completions": {
|
|
1209
|
-
const { printCompletions } = await import("./completions-
|
|
1311
|
+
const { printCompletions } = await import("./completions-62OW3B3Y.js");
|
|
1210
1312
|
printCompletions(subcommand);
|
|
1211
1313
|
break;
|
|
1212
1314
|
}
|
|
@@ -0,0 +1,137 @@
|
|
|
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
|
+
LIVENESS_STATUSES,
|
|
11
|
+
TAKEDOWN_SORT_KEYS,
|
|
12
|
+
TAKEDOWN_STATUSES,
|
|
13
|
+
createApiClient
|
|
14
|
+
} from "./chunk-SX3L6NKR.js";
|
|
15
|
+
import "./chunk-EGWK6SRQ.js";
|
|
16
|
+
import "./chunk-TFCNKBRC.js";
|
|
17
|
+
import "./chunk-U73SABXK.js";
|
|
18
|
+
|
|
19
|
+
// src/commands/takedowns/list.ts
|
|
20
|
+
var TAKEDOWN_STATUS_SET = new Set(TAKEDOWN_STATUSES);
|
|
21
|
+
var LIVENESS_STATUS_SET = new Set(LIVENESS_STATUSES);
|
|
22
|
+
var SORT_KEY_SET = new Set(TAKEDOWN_SORT_KEYS);
|
|
23
|
+
function toRow(takedown) {
|
|
24
|
+
return {
|
|
25
|
+
id: takedown.id,
|
|
26
|
+
status: takedown.status,
|
|
27
|
+
asset: takedown.asset.content,
|
|
28
|
+
assetType: takedown.asset.type,
|
|
29
|
+
livenessStatus: takedown.asset.livenessStatus ?? "",
|
|
30
|
+
brand: takedown.brand?.name ?? "",
|
|
31
|
+
assignee: takedown.assignee?.fullName ?? "",
|
|
32
|
+
createdAt: takedown.createdAt,
|
|
33
|
+
updatedAt: takedown.updatedAt
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function validateEnumValues(values, validSet, flagName) {
|
|
37
|
+
if (!values || values.length === 0) return void 0;
|
|
38
|
+
const invalid = values.filter((value) => !validSet.has(value));
|
|
39
|
+
if (invalid.length > 0) {
|
|
40
|
+
throw new CliExitError(
|
|
41
|
+
`Invalid value(s) for --${flagName}: ${invalid.join(", ")}. Valid: ${Array.from(
|
|
42
|
+
validSet
|
|
43
|
+
).join(", ")}`,
|
|
44
|
+
ExitCode.USAGE
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
return values;
|
|
48
|
+
}
|
|
49
|
+
async function runTakedownsList(options) {
|
|
50
|
+
const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
|
|
51
|
+
const perPage = options.perPage ?? 10;
|
|
52
|
+
if (perPage < 1 || perPage > 100) {
|
|
53
|
+
throw new CliExitError(
|
|
54
|
+
"takedowns list requires --per-page between 1 and 100.",
|
|
55
|
+
ExitCode.USAGE
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
const sortDirection = options.sortDirection ?? "desc";
|
|
59
|
+
if (sortDirection !== "asc" && sortDirection !== "desc") {
|
|
60
|
+
throw new CliExitError(
|
|
61
|
+
`Invalid --sort-direction '${sortDirection}'. Use 'asc' or 'desc'.`,
|
|
62
|
+
ExitCode.USAGE
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
if (options.sortBy && !SORT_KEY_SET.has(options.sortBy)) {
|
|
66
|
+
throw new CliExitError(
|
|
67
|
+
`Invalid --sort-by '${options.sortBy}'. Valid: ${TAKEDOWN_SORT_KEYS.join(", ")}`,
|
|
68
|
+
ExitCode.USAGE
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
const assetType = options.assetType?.length ? options.assetType : void 0;
|
|
72
|
+
const takedownStatus = validateEnumValues(
|
|
73
|
+
options.takedownStatus,
|
|
74
|
+
TAKEDOWN_STATUS_SET,
|
|
75
|
+
"takedown-status"
|
|
76
|
+
);
|
|
77
|
+
const livenessStatus = validateEnumValues(
|
|
78
|
+
options.livenessStatus,
|
|
79
|
+
LIVENESS_STATUS_SET,
|
|
80
|
+
"liveness-status"
|
|
81
|
+
);
|
|
82
|
+
const client = options.apiClient ?? createApiClient();
|
|
83
|
+
const input = {
|
|
84
|
+
organizationSlug: options.org,
|
|
85
|
+
query: options.query,
|
|
86
|
+
startDate: options.from,
|
|
87
|
+
endDate: options.to,
|
|
88
|
+
assetType,
|
|
89
|
+
takedownStatus,
|
|
90
|
+
livenessStatus,
|
|
91
|
+
perPage,
|
|
92
|
+
nextPage: options.nextPage,
|
|
93
|
+
sorting: options.sortBy ? [
|
|
94
|
+
{
|
|
95
|
+
key: options.sortBy,
|
|
96
|
+
direction: sortDirection
|
|
97
|
+
}
|
|
98
|
+
] : void 0
|
|
99
|
+
};
|
|
100
|
+
const result = await client.listTakedowns(input);
|
|
101
|
+
const rows = result.takedowns.map(toRow);
|
|
102
|
+
printOutput({
|
|
103
|
+
outputFormat,
|
|
104
|
+
json: result,
|
|
105
|
+
markdown: [
|
|
106
|
+
`# Takedowns${options.org ? ` (${options.org})` : ""}`,
|
|
107
|
+
"",
|
|
108
|
+
`- Returned: ${result.takedowns.length}`,
|
|
109
|
+
`- Next page cursor: ${result.next_page ?? "none"}`,
|
|
110
|
+
"",
|
|
111
|
+
...rows.map(
|
|
112
|
+
(row) => `- #${row.id} [${row.status}] ${row.asset} (${row.assetType}) brand=${row.brand || "-"} updated=${row.updatedAt}`
|
|
113
|
+
)
|
|
114
|
+
].join("\n"),
|
|
115
|
+
csv: toCsvRows(rows),
|
|
116
|
+
human: () => {
|
|
117
|
+
if (result.takedowns.length === 0) {
|
|
118
|
+
console.log("No takedowns found.");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
console.log(
|
|
122
|
+
`Takedowns${options.org ? ` for ${options.org}` : ""} (returned ${result.takedowns.length})`
|
|
123
|
+
);
|
|
124
|
+
for (const row of rows) {
|
|
125
|
+
console.log(
|
|
126
|
+
`#${row.id} [${row.status}] ${row.asset} (${row.assetType}) brand=${row.brand || "-"} assignee=${row.assignee || "-"} updated=${row.updatedAt}`
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
if (result.next_page) {
|
|
130
|
+
console.log(`Next page cursor: ${result.next_page}`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
export {
|
|
136
|
+
runTakedownsList
|
|
137
|
+
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRESETS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-37KYJWB3.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-
|
|
9
|
+
import "./chunk-SX3L6NKR.js";
|
|
10
10
|
import "./chunk-EGWK6SRQ.js";
|
|
11
11
|
import "./chunk-TFCNKBRC.js";
|
|
12
12
|
import "./chunk-U73SABXK.js";
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {
|
|
2
|
+
printOutput,
|
|
3
|
+
toCsvRows
|
|
4
|
+
} from "./chunk-VFT3TD3E.js";
|
|
5
|
+
import {
|
|
6
|
+
createApiClient
|
|
7
|
+
} from "./chunk-SX3L6NKR.js";
|
|
8
|
+
import "./chunk-EGWK6SRQ.js";
|
|
9
|
+
import "./chunk-TFCNKBRC.js";
|
|
10
|
+
import "./chunk-U73SABXK.js";
|
|
11
|
+
|
|
12
|
+
// src/commands/metrics/organization.ts
|
|
13
|
+
async function runMetricsOrganization(options) {
|
|
14
|
+
const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
|
|
15
|
+
const client = options.apiClient ?? createApiClient();
|
|
16
|
+
const input = {
|
|
17
|
+
organizationSlug: options.org,
|
|
18
|
+
brandSlug: options.brandSlug,
|
|
19
|
+
startDate: options.from,
|
|
20
|
+
endDate: options.to
|
|
21
|
+
};
|
|
22
|
+
const result = await client.getOrganizationMetrics(input);
|
|
23
|
+
const label = options.org ?? "your organization";
|
|
24
|
+
printOutput({
|
|
25
|
+
outputFormat,
|
|
26
|
+
json: result,
|
|
27
|
+
markdown: [
|
|
28
|
+
`# Organization Metrics (${label})`,
|
|
29
|
+
"",
|
|
30
|
+
`- Reports: ${result.metrics.reports}`,
|
|
31
|
+
`- New threats: ${result.metrics.newThreats}`,
|
|
32
|
+
`- Watchlisted threats: ${result.metrics.threatsWatchlisted}`,
|
|
33
|
+
`- Takedowns filed: ${result.metrics.takedownsFiled}`,
|
|
34
|
+
`- Takedowns completed: ${result.metrics.takedownsCompleted}`,
|
|
35
|
+
`- Domain threats: ${result.metrics.domainThreats}`,
|
|
36
|
+
`- Twitter threats: ${result.metrics.twitterThreats}`,
|
|
37
|
+
`- Telegram threats: ${result.metrics.telegramThreats}`,
|
|
38
|
+
`- Other threats: ${result.metrics.otherThreats}`
|
|
39
|
+
].join("\n"),
|
|
40
|
+
csv: toCsvRows([
|
|
41
|
+
{
|
|
42
|
+
reports: result.metrics.reports,
|
|
43
|
+
newThreats: result.metrics.newThreats,
|
|
44
|
+
threatsWatchlisted: result.metrics.threatsWatchlisted,
|
|
45
|
+
takedownsFiled: result.metrics.takedownsFiled,
|
|
46
|
+
takedownsCompleted: result.metrics.takedownsCompleted,
|
|
47
|
+
domainThreats: result.metrics.domainThreats,
|
|
48
|
+
twitterThreats: result.metrics.twitterThreats,
|
|
49
|
+
telegramThreats: result.metrics.telegramThreats,
|
|
50
|
+
otherThreats: result.metrics.otherThreats
|
|
51
|
+
}
|
|
52
|
+
]),
|
|
53
|
+
human: () => {
|
|
54
|
+
console.log(`Organization metrics for ${label}`);
|
|
55
|
+
console.log(`Reports: ${result.metrics.reports}`);
|
|
56
|
+
console.log(`New threats: ${result.metrics.newThreats}`);
|
|
57
|
+
console.log(`Watchlisted threats: ${result.metrics.threatsWatchlisted}`);
|
|
58
|
+
console.log(`Takedowns filed: ${result.metrics.takedownsFiled}`);
|
|
59
|
+
console.log(`Takedowns completed: ${result.metrics.takedownsCompleted}`);
|
|
60
|
+
console.log(
|
|
61
|
+
`Threat breakdown: domains=${result.metrics.domainThreats}, twitter=${result.metrics.twitterThreats}, telegram=${result.metrics.telegramThreats}, other=${result.metrics.otherThreats}`
|
|
62
|
+
);
|
|
63
|
+
if (result.blockedByDay.length > 0) {
|
|
64
|
+
console.log("");
|
|
65
|
+
console.log(`Blocked by day (${result.blockedByDay.length} entries):`);
|
|
66
|
+
for (const entry of result.blockedByDay) {
|
|
67
|
+
console.log(` ${entry.date}: ${entry.count}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
runMetricsOrganization
|
|
75
|
+
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getPresetDefinition,
|
|
3
3
|
runPreset
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-37KYJWB3.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-
|
|
10
|
+
import "./chunk-SX3L6NKR.js";
|
|
11
11
|
import "./chunk-EGWK6SRQ.js";
|
|
12
12
|
import "./chunk-TFCNKBRC.js";
|
|
13
13
|
import "./chunk-U73SABXK.js";
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
readInstalledSkillVersion,
|
|
7
7
|
setupSkill,
|
|
8
8
|
uninstallSkill
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
9
|
+
} from "./chunk-EPKNZRX6.js";
|
|
10
|
+
import "./chunk-YXRFUYA6.js";
|
|
11
11
|
export {
|
|
12
12
|
getBundledSkillContent,
|
|
13
13
|
getBundledSkillVersion,
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@chainpatrol/cli",
|
|
3
3
|
"description": "The official ChainPatrol CLI — terminal interface for threat detection",
|
|
4
4
|
"author": "Umar Ahmed <umar@chainpatrol.io>",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.11.0",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"homepage": "https://chainpatrol.com/docs/cli",
|
|
8
8
|
"keywords": [
|