@chainpatrol/cli 0.2.2 → 0.3.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/dist/{breakdown-PBH5NX6P.js → breakdown-BECXDJIS.js} +2 -1
- package/dist/{chunk-6FIJ4NU4.js → chunk-B3CCMSG2.js} +4 -2
- package/dist/chunk-DSOM6TZX.js +150 -0
- package/dist/{setup-skill-SIWP3KFS.js → chunk-T4DYUWUD.js} +219 -7
- package/dist/{chunk-KMILLX3U.js → chunk-TFCNKBRC.js} +1 -146
- package/dist/cli.js +195 -20
- package/dist/{configs-update-OVEREFCP.js → configs-update-AHI7T6S4.js} +2 -1
- package/dist/{create-C7H3L7TU.js → create-RS37JAMM.js} +2 -1
- package/dist/{drift-UDNSOP2S.js → drift-2JRZK2MC.js} +2 -1
- package/dist/{found-SJVDAINM.js → found-YYYFF4FD.js} +4 -2
- package/dist/{healthcheck-URZRXICK.js → healthcheck-6RHZBEVE.js} +2 -1
- package/dist/{list-IZT5P7II.js → list-56BS25X4.js} +2 -1
- package/dist/{list-3JBC4637.js → list-M2UQCXIO.js} +3 -2
- package/dist/{list-V5OVJ5NG.js → list-MHEIY7LQ.js} +2 -1
- package/dist/{list-json-GTST5CRN.js → list-json-GANUT7SB.js} +2 -1
- package/dist/{run-MUMSMG4V.js → run-7NTCD7JI.js} +3 -2
- package/dist/{run-V66OGZ3W.js → run-JZNZSCDJ.js} +2 -1
- package/dist/setup-skill-ZUZ5MYLI.js +19 -0
- package/dist/{snapshot-TZHS7XQX.js → snapshot-KGMO44YB.js} +2 -1
- package/dist/{summary-SUWVXO7Y.js → summary-PA2CCZVO.js} +2 -1
- package/dist/{validate-VQKKFNBO.js → validate-NIBVL7X5.js} +2 -1
- package/package.json +1 -1
|
@@ -7,9 +7,11 @@ import {
|
|
|
7
7
|
toCsvRows
|
|
8
8
|
} from "./chunk-VFT3TD3E.js";
|
|
9
9
|
import {
|
|
10
|
-
DateTime,
|
|
11
10
|
createApiClient
|
|
12
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-DSOM6TZX.js";
|
|
12
|
+
import {
|
|
13
|
+
DateTime
|
|
14
|
+
} from "./chunk-TFCNKBRC.js";
|
|
13
15
|
|
|
14
16
|
// src/presets/index.ts
|
|
15
17
|
var PRESETS = [
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DateTime
|
|
3
|
+
} from "./chunk-TFCNKBRC.js";
|
|
4
|
+
import {
|
|
5
|
+
getValidCredentials
|
|
6
|
+
} from "./chunk-EEG7T6WT.js";
|
|
7
|
+
import {
|
|
8
|
+
getConfig
|
|
9
|
+
} from "./chunk-U73SABXK.js";
|
|
10
|
+
|
|
11
|
+
// src/lib/api-client.ts
|
|
12
|
+
function parseIsoDate(value) {
|
|
13
|
+
if (!value) return void 0;
|
|
14
|
+
const dt = DateTime.fromISO(value, { zone: "utc" });
|
|
15
|
+
if (!dt.isValid) {
|
|
16
|
+
throw new Error(`Invalid ISO date: '${value}'. Use YYYY-MM-DD or full ISO 8601.`);
|
|
17
|
+
}
|
|
18
|
+
return dt.toJSDate();
|
|
19
|
+
}
|
|
20
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
21
|
+
function createApiClient(options) {
|
|
22
|
+
const config = getConfig();
|
|
23
|
+
const apiUrl = options?.apiUrl ?? config.apiUrl;
|
|
24
|
+
const getToken = options?.getToken ?? (() => getValidCredentials().accessToken);
|
|
25
|
+
async function request(path, body) {
|
|
26
|
+
const token = getToken();
|
|
27
|
+
let res;
|
|
28
|
+
try {
|
|
29
|
+
res = await fetch(`${apiUrl}/api/v2${path}`, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers: {
|
|
32
|
+
"Content-Type": "application/json",
|
|
33
|
+
Authorization: `Bearer ${token}`
|
|
34
|
+
},
|
|
35
|
+
body: JSON.stringify(body),
|
|
36
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
37
|
+
});
|
|
38
|
+
} catch (err) {
|
|
39
|
+
if (err instanceof DOMException && err.name === "TimeoutError") {
|
|
40
|
+
throw new Error(
|
|
41
|
+
"Request timed out. Check your network connection and try again."
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
if (err instanceof TypeError && err.code === "ECONNREFUSED") {
|
|
45
|
+
throw new Error(`Cannot connect to ${apiUrl}. Is the server running?`);
|
|
46
|
+
}
|
|
47
|
+
throw new Error("Network error. Check your internet connection and try again.");
|
|
48
|
+
}
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
let errorMessageFromResponse = null;
|
|
51
|
+
try {
|
|
52
|
+
const errorBody = await res.json();
|
|
53
|
+
errorMessageFromResponse = errorBody.message ?? null;
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
if (res.status === 401) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
errorMessageFromResponse ?? "Authentication failed. Run `chainpatrol login` to re-authenticate."
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
if (res.status === 403) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
errorMessageFromResponse ?? "You don't have permission to access this resource. Check your --org slug and permissions."
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
if (res.status === 404) {
|
|
67
|
+
throw new Error(
|
|
68
|
+
errorMessageFromResponse ?? "Organization not found. Check your --org slug."
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
if (res.status === 422) {
|
|
72
|
+
throw new Error(errorMessageFromResponse ?? "Request validation failed.");
|
|
73
|
+
}
|
|
74
|
+
if (res.status >= 500) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
"ChainPatrol API is temporarily unavailable. Please try again later."
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
throw new Error(
|
|
80
|
+
errorMessageFromResponse ?? `Request failed (${res.status}). Run with --json for details.`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
return await res.json();
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
listDetectionConfigs(slug) {
|
|
87
|
+
return request("/detection/configs/list", { slug });
|
|
88
|
+
},
|
|
89
|
+
updateDetectionConfig(input) {
|
|
90
|
+
return request("/detection/configs/update", input);
|
|
91
|
+
},
|
|
92
|
+
runDetectionConfigs(input) {
|
|
93
|
+
return request("/detection/configs/run", input);
|
|
94
|
+
},
|
|
95
|
+
validateDetectionConfigs(input) {
|
|
96
|
+
return request("/detection/configs/validate", input);
|
|
97
|
+
},
|
|
98
|
+
getDetectionDrift(input) {
|
|
99
|
+
return request("/detection/drift", {
|
|
100
|
+
...input,
|
|
101
|
+
startDate: parseIsoDate(input.startDate),
|
|
102
|
+
endDate: parseIsoDate(input.endDate)
|
|
103
|
+
});
|
|
104
|
+
},
|
|
105
|
+
getOperationsQueuesSnapshot(input) {
|
|
106
|
+
return request(
|
|
107
|
+
"/operations/queues/snapshot",
|
|
108
|
+
input
|
|
109
|
+
);
|
|
110
|
+
},
|
|
111
|
+
getMetricsSummary(input) {
|
|
112
|
+
return request("/metrics/summary", {
|
|
113
|
+
...input,
|
|
114
|
+
startDate: parseIsoDate(input.startDate),
|
|
115
|
+
endDate: parseIsoDate(input.endDate)
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
getMetricsFound(input) {
|
|
119
|
+
return request("/metrics/found", {
|
|
120
|
+
...input,
|
|
121
|
+
startDate: parseIsoDate(input.startDate),
|
|
122
|
+
endDate: parseIsoDate(input.endDate)
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
getMetricsBreakdown(input) {
|
|
126
|
+
return request("/metrics/breakdown", {
|
|
127
|
+
...input,
|
|
128
|
+
startDate: parseIsoDate(input.startDate),
|
|
129
|
+
endDate: parseIsoDate(input.endDate)
|
|
130
|
+
});
|
|
131
|
+
},
|
|
132
|
+
createReport(input) {
|
|
133
|
+
return request("/report/create", input);
|
|
134
|
+
},
|
|
135
|
+
listOrganizationReports(input) {
|
|
136
|
+
return request("/public/getOrganizationReports", {
|
|
137
|
+
slug: input.slug,
|
|
138
|
+
limit: input.limit ?? 10,
|
|
139
|
+
cursor: input.cursor,
|
|
140
|
+
status: input.status,
|
|
141
|
+
searchQuery: input.searchQuery,
|
|
142
|
+
reportedByCustomer: input.reportedByCustomer
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export {
|
|
149
|
+
createApiClient
|
|
150
|
+
};
|
|
@@ -4,20 +4,85 @@ import {
|
|
|
4
4
|
} from "./chunk-IUZB3DQW.js";
|
|
5
5
|
|
|
6
6
|
// src/commands/setup-skill.ts
|
|
7
|
-
import { mkdirSync, writeFileSync, existsSync, readFileSync, rmSync } from "fs";
|
|
7
|
+
import { mkdirSync, writeFileSync, existsSync, readFileSync as readFileSync2, rmSync } from "fs";
|
|
8
8
|
import { join } from "path";
|
|
9
9
|
import { homedir } from "os";
|
|
10
|
+
|
|
11
|
+
// src/lib/version.ts
|
|
12
|
+
import { readFileSync } from "fs";
|
|
13
|
+
import { dirname, resolve } from "path";
|
|
14
|
+
import { fileURLToPath } from "url";
|
|
15
|
+
var PACKAGE_NAME = "@chainpatrol/cli";
|
|
16
|
+
var cached;
|
|
17
|
+
function getCliVersion() {
|
|
18
|
+
if (cached !== void 0) return cached;
|
|
19
|
+
cached = resolveCliVersion();
|
|
20
|
+
return cached;
|
|
21
|
+
}
|
|
22
|
+
function resolveCliVersion() {
|
|
23
|
+
try {
|
|
24
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
25
|
+
const candidates = [
|
|
26
|
+
resolve(here, "..", "package.json"),
|
|
27
|
+
resolve(here, "..", "..", "package.json")
|
|
28
|
+
];
|
|
29
|
+
for (const candidate of candidates) {
|
|
30
|
+
const version = tryReadVersion(candidate);
|
|
31
|
+
if (version) return version;
|
|
32
|
+
}
|
|
33
|
+
} catch {
|
|
34
|
+
}
|
|
35
|
+
return "0.0.0";
|
|
36
|
+
}
|
|
37
|
+
function tryReadVersion(path) {
|
|
38
|
+
try {
|
|
39
|
+
const raw = readFileSync(path, "utf-8");
|
|
40
|
+
const pkg = JSON.parse(raw);
|
|
41
|
+
if (pkg.name === PACKAGE_NAME && typeof pkg.version === "string") {
|
|
42
|
+
return pkg.version;
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
return void 0;
|
|
47
|
+
}
|
|
48
|
+
function compareVersions(a, b) {
|
|
49
|
+
const parsed = (value) => {
|
|
50
|
+
const hyphenIndex = value.indexOf("-");
|
|
51
|
+
const main = hyphenIndex >= 0 ? value.slice(0, hyphenIndex) : value;
|
|
52
|
+
const pre = hyphenIndex >= 0 ? value.slice(hyphenIndex + 1) : "";
|
|
53
|
+
const parts = main.split(".").map((n) => Number.parseInt(n, 10) || 0);
|
|
54
|
+
while (parts.length < 3) parts.push(0);
|
|
55
|
+
return { parts, pre };
|
|
56
|
+
};
|
|
57
|
+
const va = parsed(a);
|
|
58
|
+
const vb = parsed(b);
|
|
59
|
+
for (let i = 0; i < 3; i += 1) {
|
|
60
|
+
if (va.parts[i] !== vb.parts[i]) return va.parts[i] - vb.parts[i];
|
|
61
|
+
}
|
|
62
|
+
if (va.pre === vb.pre) return 0;
|
|
63
|
+
if (va.pre === "") return 1;
|
|
64
|
+
if (vb.pre === "") return -1;
|
|
65
|
+
return va.pre < vb.pre ? -1 : 1;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/commands/setup-skill.ts
|
|
10
69
|
var SKILL_DIR = join(homedir(), ".claude", "skills", "chainpatrol");
|
|
11
70
|
var SKILL_FILE = join(SKILL_DIR, "SKILL.md");
|
|
12
|
-
|
|
71
|
+
function buildSkillContent(version) {
|
|
72
|
+
return `---
|
|
13
73
|
name: chainpatrol
|
|
74
|
+
version: ${version}
|
|
14
75
|
description: |
|
|
15
76
|
ChainPatrol CLI assistant. Helps use the chainpatrol CLI tool: login via device
|
|
16
77
|
code flow, check auth status, list detection configs, list reports (including
|
|
17
|
-
customer-reported ones), and run
|
|
78
|
+
customer-reported ones), run CLI commands, and run an organization
|
|
79
|
+
healthcheck across the detection / reviewing / blocklisting / takedown
|
|
80
|
+
pipeline.
|
|
18
81
|
Use when: "chainpatrol cli", "login to chainpatrol", "check detection configs",
|
|
19
82
|
"am I logged in", "list configs", "use the cli", "list reports",
|
|
20
|
-
"customer reports", "reports reported by customer", "find detection gaps"
|
|
83
|
+
"customer reports", "reports reported by customer", "find detection gaps",
|
|
84
|
+
"org healthcheck", "organization health check", "audit my org",
|
|
85
|
+
"what's wrong with org", "review org setup".
|
|
21
86
|
allowed-tools:
|
|
22
87
|
- Bash
|
|
23
88
|
- Read
|
|
@@ -156,6 +221,22 @@ Config is stored at \`~/.chainpatrol/config.json\`:
|
|
|
156
221
|
|
|
157
222
|
Override config dir with \`CHAINPATROL_CONFIG_DIR\` env var.
|
|
158
223
|
|
|
224
|
+
## Version Checks
|
|
225
|
+
|
|
226
|
+
The CLI runs two lightweight version checks alongside each command and prints
|
|
227
|
+
a nudge to stderr if anything is out of date:
|
|
228
|
+
|
|
229
|
+
- **Skill freshness**: compares the installed skill (\`~/.claude/skills/chainpatrol/SKILL.md\`)
|
|
230
|
+
to the version bundled with the CLI. If it's missing or older, the user is
|
|
231
|
+
asked to run \`chainpatrol setup\`.
|
|
232
|
+
- **NPM freshness**: compares the running CLI version to the latest published
|
|
233
|
+
on the npm registry. The check is throttled to once per 24 hours and capped
|
|
234
|
+
at a 1.5s timeout, with the result cached at \`<configDir>/version-check.json\`.
|
|
235
|
+
|
|
236
|
+
Set \`CHAINPATROL_NO_UPDATE_CHECK=1\` to silence both checks. JSON mode
|
|
237
|
+
(\`--json\`) and quiet mode (\`-q\` / \`--quiet\`) also suppress the nudges so
|
|
238
|
+
machine-readable output is never polluted.
|
|
239
|
+
|
|
159
240
|
## Global Flags
|
|
160
241
|
|
|
161
242
|
| Flag | Description |
|
|
@@ -182,7 +263,129 @@ The \`configs list\` command shows detection sources grouped by:
|
|
|
182
263
|
- **Not Configured** \u2014 Sources available but not yet set up for the org
|
|
183
264
|
|
|
184
265
|
Each config entry includes: title, status, cron schedule, and configuration parameters.
|
|
266
|
+
|
|
267
|
+
## Organization HealthCheck Guide
|
|
268
|
+
|
|
269
|
+
This guide explains how to look for things that may be wrong for a given org
|
|
270
|
+
across the full pipeline: **detection \u2192 reviewing \u2192 blocklisting \u2192 takedowns**.
|
|
271
|
+
When the user asks for a "health check", "audit", "what's wrong with org X",
|
|
272
|
+
"review org X's setup", or similar, walk through each section below and surface
|
|
273
|
+
findings.
|
|
274
|
+
|
|
275
|
+
In each section there are things you can look for that may be wrong.
|
|
276
|
+
|
|
277
|
+
### Detection
|
|
278
|
+
|
|
279
|
+
#### Enable Config That May Be Turned Off for Current Threats
|
|
280
|
+
|
|
281
|
+
Blocked threats exist in an asset type but the matching detection source is
|
|
282
|
+
turned off. For example: lots of Twitter assets are on the blocklist, but
|
|
283
|
+
detection sources like "Twitter / X User Search" or "Twitter Post Search" are
|
|
284
|
+
disabled. Those should be turned on.
|
|
285
|
+
|
|
286
|
+
#### Spike in Detections
|
|
287
|
+
|
|
288
|
+
A spike in recent detections is worth investigating. It could be a bad config
|
|
289
|
+
change, or it could be a legitimate new attack push in this area \u2014 useful
|
|
290
|
+
intel to surface to the security team as a targeted spike.
|
|
291
|
+
|
|
292
|
+
#### Drop in Detections
|
|
293
|
+
|
|
294
|
+
A drop is also worth looking into. It may be a bad config change, or it may
|
|
295
|
+
mean the config is not really relevant and can be safely turned off (not all
|
|
296
|
+
default-on configs are relevant to every org).
|
|
297
|
+
|
|
298
|
+
### Reviewing
|
|
299
|
+
|
|
300
|
+
#### Pile Up / Backlog of Unreviewed Proposals
|
|
301
|
+
|
|
302
|
+
Too many proposals waiting in review. For most organizations this is over 100
|
|
303
|
+
reports, but really the threshold is relative to the average number of
|
|
304
|
+
confirmed threats per week. Example: if an org only adds 5 blocked threats per
|
|
305
|
+
week, then a 7-day backlog of even 10 proposals is a really big deal.
|
|
306
|
+
|
|
307
|
+
#### Really Old Proposals
|
|
308
|
+
|
|
309
|
+
Any proposal waiting for review longer than 14 days is a sign something has
|
|
310
|
+
gone wrong. Even complex investigations rarely take longer than this. Except
|
|
311
|
+
for rare cases, these should be rejected or approved to prevent a backlog
|
|
312
|
+
from building.
|
|
313
|
+
|
|
314
|
+
#### Spike in Auto Approved Reports
|
|
315
|
+
|
|
316
|
+
If suddenly a lot of proposals in an org are being approved by automation,
|
|
317
|
+
that can be a sign of a bad rule approving too much, or a break in auto
|
|
318
|
+
confidences. Sometimes detection is spamming things that uniquely combine
|
|
319
|
+
with a weakness of a rule \u2014 which is effectively a bad rule. In all these
|
|
320
|
+
cases, notify an engineer at ChainPatrol and check any detection configs you
|
|
321
|
+
adjusted recently, since those may be the cause of spam combined with a weak
|
|
322
|
+
rule.
|
|
323
|
+
|
|
324
|
+
### Blocklisting
|
|
325
|
+
|
|
326
|
+
#### Google Safe Browsing (Coming Soon)
|
|
327
|
+
|
|
328
|
+
(Needs new public API added before this works.)
|
|
329
|
+
|
|
330
|
+
High error rate in Google Safe Browsing submission tracker. Each submission
|
|
331
|
+
has a status. If too many are in \`CANCELLED\`, that means Google's engine
|
|
332
|
+
denied our submission. Contact ChainPatrol's eng team to investigate why, and
|
|
333
|
+
also take a look at the org's custom detection sources \u2014 it's possible there
|
|
334
|
+
are too many false positives landing on the blocklist, indicating issues with
|
|
335
|
+
detection and reviewing rules.
|
|
336
|
+
|
|
337
|
+
### Takedowns
|
|
338
|
+
|
|
339
|
+
#### Too Many Takedowns in ToDo
|
|
340
|
+
|
|
341
|
+
Can mean a gap in automated takedowns not being implemented for some new area
|
|
342
|
+
of threats. It can also mean the areas that require manual takedowns are
|
|
343
|
+
being missed by the takedown team.
|
|
344
|
+
|
|
345
|
+
#### Too Many Takedowns In Progress
|
|
346
|
+
|
|
347
|
+
Typically means something is wrong with the submission itself. The takedown
|
|
348
|
+
may need to be resubmitted, the vendor asked for more evidence, or we may
|
|
349
|
+
have submitted it in the wrong place.
|
|
350
|
+
|
|
351
|
+
#### Too Many Cancelled Takedowns
|
|
352
|
+
|
|
353
|
+
Takedowns should rarely be cancelled. A cancelled takedown means "we will not
|
|
354
|
+
do this takedown" for some reason. Cases like adding an item to the blocklist
|
|
355
|
+
when it's already taken down are treated as completed, not cancelled. So even
|
|
356
|
+
3 cancelled takedowns in a 7-day period is too many.
|
|
357
|
+
|
|
358
|
+
#### Automated Takedowns Turned Off for Over 30 Days
|
|
359
|
+
|
|
360
|
+
Automated takedowns should be on by default for nearly every organization.
|
|
361
|
+
Any issue that would make you want to turn off automated takedowns should be
|
|
362
|
+
resolved within 30 days.
|
|
185
363
|
`;
|
|
364
|
+
}
|
|
365
|
+
function getBundledSkillVersion() {
|
|
366
|
+
return getCliVersion();
|
|
367
|
+
}
|
|
368
|
+
function getBundledSkillContent() {
|
|
369
|
+
return buildSkillContent(getCliVersion());
|
|
370
|
+
}
|
|
371
|
+
function readInstalledSkillVersion() {
|
|
372
|
+
if (!existsSync(SKILL_FILE)) return void 0;
|
|
373
|
+
try {
|
|
374
|
+
const raw = readFileSync2(SKILL_FILE, "utf-8");
|
|
375
|
+
return parseSkillVersion(raw);
|
|
376
|
+
} catch {
|
|
377
|
+
return void 0;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function isSkillInstalled() {
|
|
381
|
+
return existsSync(SKILL_FILE);
|
|
382
|
+
}
|
|
383
|
+
function parseSkillVersion(content) {
|
|
384
|
+
const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
385
|
+
if (!fmMatch) return void 0;
|
|
386
|
+
const versionMatch = fmMatch[1].match(/^version:\s*(.+?)\s*$/m);
|
|
387
|
+
return versionMatch ? versionMatch[1].trim() : void 0;
|
|
388
|
+
}
|
|
186
389
|
var LOGO = `
|
|
187
390
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
188
391
|
\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557
|
|
@@ -195,10 +398,11 @@ var LOGO = `
|
|
|
195
398
|
\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
196
399
|
`;
|
|
197
400
|
function setupSkill(options) {
|
|
401
|
+
const skillContent = buildSkillContent(getCliVersion());
|
|
198
402
|
const alreadyExists = existsSync(SKILL_FILE);
|
|
199
403
|
if (alreadyExists) {
|
|
200
|
-
const existing =
|
|
201
|
-
if (existing ===
|
|
404
|
+
const existing = readFileSync2(SKILL_FILE, "utf-8");
|
|
405
|
+
if (existing === skillContent) {
|
|
202
406
|
if (options.json) {
|
|
203
407
|
console.log(JSON.stringify({ status: "up-to-date", path: SKILL_FILE }));
|
|
204
408
|
} else {
|
|
@@ -208,7 +412,7 @@ function setupSkill(options) {
|
|
|
208
412
|
}
|
|
209
413
|
}
|
|
210
414
|
mkdirSync(SKILL_DIR, { recursive: true });
|
|
211
|
-
writeFileSync(SKILL_FILE,
|
|
415
|
+
writeFileSync(SKILL_FILE, skillContent, { mode: 420 });
|
|
212
416
|
const completionResult = installCompletions();
|
|
213
417
|
if (options.json) {
|
|
214
418
|
console.log(
|
|
@@ -257,7 +461,15 @@ function uninstallSkill(options) {
|
|
|
257
461
|
}
|
|
258
462
|
}
|
|
259
463
|
}
|
|
464
|
+
|
|
260
465
|
export {
|
|
466
|
+
getCliVersion,
|
|
467
|
+
compareVersions,
|
|
468
|
+
getBundledSkillVersion,
|
|
469
|
+
getBundledSkillContent,
|
|
470
|
+
readInstalledSkillVersion,
|
|
471
|
+
isSkillInstalled,
|
|
472
|
+
parseSkillVersion,
|
|
261
473
|
setupSkill,
|
|
262
474
|
uninstallSkill
|
|
263
475
|
};
|
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getValidCredentials
|
|
3
|
-
} from "./chunk-EEG7T6WT.js";
|
|
4
|
-
import {
|
|
5
|
-
getConfig
|
|
6
|
-
} from "./chunk-U73SABXK.js";
|
|
7
|
-
|
|
8
1
|
// ../../node_modules/luxon/src/errors.js
|
|
9
2
|
var LuxonError = class extends Error {
|
|
10
3
|
};
|
|
@@ -6430,144 +6423,6 @@ function friendlyDateTime(dateTimeish) {
|
|
|
6430
6423
|
}
|
|
6431
6424
|
}
|
|
6432
6425
|
|
|
6433
|
-
// src/lib/api-client.ts
|
|
6434
|
-
function parseIsoDate(value) {
|
|
6435
|
-
if (!value) return void 0;
|
|
6436
|
-
const dt = DateTime.fromISO(value, { zone: "utc" });
|
|
6437
|
-
if (!dt.isValid) {
|
|
6438
|
-
throw new Error(`Invalid ISO date: '${value}'. Use YYYY-MM-DD or full ISO 8601.`);
|
|
6439
|
-
}
|
|
6440
|
-
return dt.toJSDate();
|
|
6441
|
-
}
|
|
6442
|
-
var REQUEST_TIMEOUT_MS = 3e4;
|
|
6443
|
-
function createApiClient(options) {
|
|
6444
|
-
const config = getConfig();
|
|
6445
|
-
const apiUrl = options?.apiUrl ?? config.apiUrl;
|
|
6446
|
-
const getToken = options?.getToken ?? (() => getValidCredentials().accessToken);
|
|
6447
|
-
async function request(path, body) {
|
|
6448
|
-
const token = getToken();
|
|
6449
|
-
let res;
|
|
6450
|
-
try {
|
|
6451
|
-
res = await fetch(`${apiUrl}/api/v2${path}`, {
|
|
6452
|
-
method: "POST",
|
|
6453
|
-
headers: {
|
|
6454
|
-
"Content-Type": "application/json",
|
|
6455
|
-
Authorization: `Bearer ${token}`
|
|
6456
|
-
},
|
|
6457
|
-
body: JSON.stringify(body),
|
|
6458
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
6459
|
-
});
|
|
6460
|
-
} catch (err) {
|
|
6461
|
-
if (err instanceof DOMException && err.name === "TimeoutError") {
|
|
6462
|
-
throw new Error(
|
|
6463
|
-
"Request timed out. Check your network connection and try again."
|
|
6464
|
-
);
|
|
6465
|
-
}
|
|
6466
|
-
if (err instanceof TypeError && err.code === "ECONNREFUSED") {
|
|
6467
|
-
throw new Error(`Cannot connect to ${apiUrl}. Is the server running?`);
|
|
6468
|
-
}
|
|
6469
|
-
throw new Error("Network error. Check your internet connection and try again.");
|
|
6470
|
-
}
|
|
6471
|
-
if (!res.ok) {
|
|
6472
|
-
let errorMessageFromResponse = null;
|
|
6473
|
-
try {
|
|
6474
|
-
const errorBody = await res.json();
|
|
6475
|
-
errorMessageFromResponse = errorBody.message ?? null;
|
|
6476
|
-
} catch {
|
|
6477
|
-
}
|
|
6478
|
-
if (res.status === 401) {
|
|
6479
|
-
throw new Error(
|
|
6480
|
-
errorMessageFromResponse ?? "Authentication failed. Run `chainpatrol login` to re-authenticate."
|
|
6481
|
-
);
|
|
6482
|
-
}
|
|
6483
|
-
if (res.status === 403) {
|
|
6484
|
-
throw new Error(
|
|
6485
|
-
errorMessageFromResponse ?? "You don't have permission to access this resource. Check your --org slug and permissions."
|
|
6486
|
-
);
|
|
6487
|
-
}
|
|
6488
|
-
if (res.status === 404) {
|
|
6489
|
-
throw new Error(
|
|
6490
|
-
errorMessageFromResponse ?? "Organization not found. Check your --org slug."
|
|
6491
|
-
);
|
|
6492
|
-
}
|
|
6493
|
-
if (res.status === 422) {
|
|
6494
|
-
throw new Error(errorMessageFromResponse ?? "Request validation failed.");
|
|
6495
|
-
}
|
|
6496
|
-
if (res.status >= 500) {
|
|
6497
|
-
throw new Error(
|
|
6498
|
-
"ChainPatrol API is temporarily unavailable. Please try again later."
|
|
6499
|
-
);
|
|
6500
|
-
}
|
|
6501
|
-
throw new Error(
|
|
6502
|
-
errorMessageFromResponse ?? `Request failed (${res.status}). Run with --json for details.`
|
|
6503
|
-
);
|
|
6504
|
-
}
|
|
6505
|
-
return await res.json();
|
|
6506
|
-
}
|
|
6507
|
-
return {
|
|
6508
|
-
listDetectionConfigs(slug) {
|
|
6509
|
-
return request("/detection/configs/list", { slug });
|
|
6510
|
-
},
|
|
6511
|
-
updateDetectionConfig(input) {
|
|
6512
|
-
return request("/detection/configs/update", input);
|
|
6513
|
-
},
|
|
6514
|
-
runDetectionConfigs(input) {
|
|
6515
|
-
return request("/detection/configs/run", input);
|
|
6516
|
-
},
|
|
6517
|
-
validateDetectionConfigs(input) {
|
|
6518
|
-
return request("/detection/configs/validate", input);
|
|
6519
|
-
},
|
|
6520
|
-
getDetectionDrift(input) {
|
|
6521
|
-
return request("/detection/drift", {
|
|
6522
|
-
...input,
|
|
6523
|
-
startDate: parseIsoDate(input.startDate),
|
|
6524
|
-
endDate: parseIsoDate(input.endDate)
|
|
6525
|
-
});
|
|
6526
|
-
},
|
|
6527
|
-
getOperationsQueuesSnapshot(input) {
|
|
6528
|
-
return request(
|
|
6529
|
-
"/operations/queues/snapshot",
|
|
6530
|
-
input
|
|
6531
|
-
);
|
|
6532
|
-
},
|
|
6533
|
-
getMetricsSummary(input) {
|
|
6534
|
-
return request("/metrics/summary", {
|
|
6535
|
-
...input,
|
|
6536
|
-
startDate: parseIsoDate(input.startDate),
|
|
6537
|
-
endDate: parseIsoDate(input.endDate)
|
|
6538
|
-
});
|
|
6539
|
-
},
|
|
6540
|
-
getMetricsFound(input) {
|
|
6541
|
-
return request("/metrics/found", {
|
|
6542
|
-
...input,
|
|
6543
|
-
startDate: parseIsoDate(input.startDate),
|
|
6544
|
-
endDate: parseIsoDate(input.endDate)
|
|
6545
|
-
});
|
|
6546
|
-
},
|
|
6547
|
-
getMetricsBreakdown(input) {
|
|
6548
|
-
return request("/metrics/breakdown", {
|
|
6549
|
-
...input,
|
|
6550
|
-
startDate: parseIsoDate(input.startDate),
|
|
6551
|
-
endDate: parseIsoDate(input.endDate)
|
|
6552
|
-
});
|
|
6553
|
-
},
|
|
6554
|
-
createReport(input) {
|
|
6555
|
-
return request("/report/create", input);
|
|
6556
|
-
},
|
|
6557
|
-
listOrganizationReports(input) {
|
|
6558
|
-
return request("/public/getOrganizationReports", {
|
|
6559
|
-
slug: input.slug,
|
|
6560
|
-
limit: input.limit ?? 10,
|
|
6561
|
-
cursor: input.cursor,
|
|
6562
|
-
status: input.status,
|
|
6563
|
-
searchQuery: input.searchQuery,
|
|
6564
|
-
reportedByCustomer: input.reportedByCustomer
|
|
6565
|
-
});
|
|
6566
|
-
}
|
|
6567
|
-
};
|
|
6568
|
-
}
|
|
6569
|
-
|
|
6570
6426
|
export {
|
|
6571
|
-
DateTime
|
|
6572
|
-
createApiClient
|
|
6427
|
+
DateTime
|
|
6573
6428
|
};
|
package/dist/cli.js
CHANGED
|
@@ -7,6 +7,20 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
resolveOutputFormat
|
|
9
9
|
} from "./chunk-VFT3TD3E.js";
|
|
10
|
+
import {
|
|
11
|
+
compareVersions,
|
|
12
|
+
getBundledSkillVersion,
|
|
13
|
+
getCliVersion,
|
|
14
|
+
isSkillInstalled,
|
|
15
|
+
readInstalledSkillVersion
|
|
16
|
+
} from "./chunk-T4DYUWUD.js";
|
|
17
|
+
import "./chunk-IUZB3DQW.js";
|
|
18
|
+
import {
|
|
19
|
+
DateTime
|
|
20
|
+
} from "./chunk-TFCNKBRC.js";
|
|
21
|
+
import {
|
|
22
|
+
getConfigDir
|
|
23
|
+
} from "./chunk-U73SABXK.js";
|
|
10
24
|
|
|
11
25
|
// src/cli.tsx
|
|
12
26
|
import meow from "meow";
|
|
@@ -345,6 +359,145 @@ function getTopLevelHelp() {
|
|
|
345
359
|
].join("\n");
|
|
346
360
|
}
|
|
347
361
|
|
|
362
|
+
// src/lib/version-check.ts
|
|
363
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
364
|
+
import { join } from "path";
|
|
365
|
+
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@chainpatrol/cli/latest";
|
|
366
|
+
var CHECK_FILE_NAME = "version-check.json";
|
|
367
|
+
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
368
|
+
var NPM_REQUEST_TIMEOUT_MS = 1500;
|
|
369
|
+
function checkSkillStatus() {
|
|
370
|
+
const bundled = getBundledSkillVersion();
|
|
371
|
+
if (!isSkillInstalled()) {
|
|
372
|
+
return { state: "missing", bundled };
|
|
373
|
+
}
|
|
374
|
+
const installed = readInstalledSkillVersion();
|
|
375
|
+
if (!installed) {
|
|
376
|
+
return { state: "outdated", installed: void 0, bundled };
|
|
377
|
+
}
|
|
378
|
+
if (compareVersions(installed, bundled) < 0) {
|
|
379
|
+
return { state: "outdated", installed, bundled };
|
|
380
|
+
}
|
|
381
|
+
return { state: "ok", installed, bundled };
|
|
382
|
+
}
|
|
383
|
+
function getCheckFilePath() {
|
|
384
|
+
return join(getConfigDir(), CHECK_FILE_NAME);
|
|
385
|
+
}
|
|
386
|
+
function readCheckCache() {
|
|
387
|
+
const path = getCheckFilePath();
|
|
388
|
+
if (!existsSync(path)) return {};
|
|
389
|
+
try {
|
|
390
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
391
|
+
} catch {
|
|
392
|
+
return {};
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
function writeCheckCache(cache) {
|
|
396
|
+
try {
|
|
397
|
+
mkdirSync(getConfigDir(), { recursive: true });
|
|
398
|
+
writeFileSync(getCheckFilePath(), JSON.stringify(cache, null, 2), { mode: 420 });
|
|
399
|
+
} catch {
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
async function fetchNpmLatestVersion(timeoutMs) {
|
|
403
|
+
const controller = new AbortController();
|
|
404
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
405
|
+
try {
|
|
406
|
+
const res = await fetch(NPM_REGISTRY_URL, {
|
|
407
|
+
signal: controller.signal,
|
|
408
|
+
headers: { accept: "application/json" }
|
|
409
|
+
});
|
|
410
|
+
if (!res.ok) return void 0;
|
|
411
|
+
const data = await res.json();
|
|
412
|
+
return typeof data.version === "string" ? data.version : void 0;
|
|
413
|
+
} catch {
|
|
414
|
+
return void 0;
|
|
415
|
+
} finally {
|
|
416
|
+
clearTimeout(timer);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
async function checkNpmStatus(options) {
|
|
420
|
+
const current = getCliVersion();
|
|
421
|
+
if (process.env.CHAINPATROL_NO_UPDATE_CHECK === "1") {
|
|
422
|
+
return { state: "unknown", current };
|
|
423
|
+
}
|
|
424
|
+
const now = options?.now ?? DateTime.now().toUTC().toMillis();
|
|
425
|
+
const force = options?.force ?? false;
|
|
426
|
+
const timeoutMs = options?.timeoutMs ?? NPM_REQUEST_TIMEOUT_MS;
|
|
427
|
+
const cache = readCheckCache();
|
|
428
|
+
const fresh = cache.lastNpmCheckAt !== void 0 && now - cache.lastNpmCheckAt < CHECK_INTERVAL_MS;
|
|
429
|
+
let latest;
|
|
430
|
+
if (!force && fresh && cache.lastSeenLatest) {
|
|
431
|
+
latest = cache.lastSeenLatest;
|
|
432
|
+
} else {
|
|
433
|
+
latest = await fetchNpmLatestVersion(timeoutMs);
|
|
434
|
+
if (latest) {
|
|
435
|
+
writeCheckCache({ lastNpmCheckAt: now, lastSeenLatest: latest });
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
if (!latest) return { state: "unknown", current };
|
|
439
|
+
if (compareVersions(current, latest) < 0) {
|
|
440
|
+
return { state: "outdated", current, latest };
|
|
441
|
+
}
|
|
442
|
+
return { state: "ok", current, latest };
|
|
443
|
+
}
|
|
444
|
+
function formatSkillNudge(status) {
|
|
445
|
+
if (status.state === "missing") {
|
|
446
|
+
return [
|
|
447
|
+
"Tip: the ChainPatrol skill for Claude Code is not installed.",
|
|
448
|
+
" Run `chainpatrol setup` to install it (one-time setup)."
|
|
449
|
+
].join("\n");
|
|
450
|
+
}
|
|
451
|
+
if (status.state === "outdated") {
|
|
452
|
+
const from = status.installed ?? "unknown";
|
|
453
|
+
return [
|
|
454
|
+
`Tip: your installed ChainPatrol skill (${from}) is out of date (latest: ${status.bundled}).`,
|
|
455
|
+
" Run `chainpatrol setup` to update."
|
|
456
|
+
].join("\n");
|
|
457
|
+
}
|
|
458
|
+
return void 0;
|
|
459
|
+
}
|
|
460
|
+
function formatNpmNudge(status) {
|
|
461
|
+
if (status.state === "outdated") {
|
|
462
|
+
return [
|
|
463
|
+
`Tip: a new version of @chainpatrol/cli is available: ${status.latest} (you have ${status.current}).`,
|
|
464
|
+
" Update: npm i -g @chainpatrol/cli"
|
|
465
|
+
].join("\n");
|
|
466
|
+
}
|
|
467
|
+
return void 0;
|
|
468
|
+
}
|
|
469
|
+
async function runVersionChecks(options) {
|
|
470
|
+
const skill = checkSkillStatus();
|
|
471
|
+
const npm = await checkNpmStatus(options);
|
|
472
|
+
const messages = [];
|
|
473
|
+
const skillMsg = formatSkillNudge(skill);
|
|
474
|
+
if (skillMsg) messages.push(skillMsg);
|
|
475
|
+
const npmMsg = formatNpmNudge(npm);
|
|
476
|
+
if (npmMsg) messages.push(npmMsg);
|
|
477
|
+
return { skill, npm, messages };
|
|
478
|
+
}
|
|
479
|
+
function shouldRunVersionChecks(opts) {
|
|
480
|
+
if (opts.jsonMode || opts.quiet || opts.helpOrVersionFlag) return false;
|
|
481
|
+
if (process.env.CHAINPATROL_NO_UPDATE_CHECK === "1") return false;
|
|
482
|
+
if (!opts.command) return false;
|
|
483
|
+
const skipCommands = /* @__PURE__ */ new Set([
|
|
484
|
+
"setup",
|
|
485
|
+
"install",
|
|
486
|
+
"i",
|
|
487
|
+
"uninstall",
|
|
488
|
+
"completions",
|
|
489
|
+
"help"
|
|
490
|
+
]);
|
|
491
|
+
return !skipCommands.has(opts.command);
|
|
492
|
+
}
|
|
493
|
+
function printVersionMessages(messages) {
|
|
494
|
+
if (messages.length === 0) return;
|
|
495
|
+
for (const msg of messages) {
|
|
496
|
+
console.error("");
|
|
497
|
+
console.error(msg);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
348
501
|
// src/cli.tsx
|
|
349
502
|
var COMMANDS = [
|
|
350
503
|
"login",
|
|
@@ -389,7 +542,7 @@ var cli = meow(`
|
|
|
389
542
|
${getTopLevelHelp()}
|
|
390
543
|
`, {
|
|
391
544
|
importMeta: import.meta,
|
|
392
|
-
version:
|
|
545
|
+
version: getCliVersion(),
|
|
393
546
|
// Boolean flags without an explicit `default` should resolve to `undefined`
|
|
394
547
|
// when not passed (rather than meow's default of `false`), so handlers can
|
|
395
548
|
// distinguish "flag omitted" from "flag explicitly set to false". Flags that
|
|
@@ -533,12 +686,12 @@ function parseAttachmentUrls() {
|
|
|
533
686
|
}
|
|
534
687
|
async function handleConfigsList(org) {
|
|
535
688
|
if (jsonMode) {
|
|
536
|
-
const { listConfigsJson } = await import("./list-json-
|
|
689
|
+
const { listConfigsJson } = await import("./list-json-GANUT7SB.js");
|
|
537
690
|
await listConfigsJson({ org });
|
|
538
691
|
return;
|
|
539
692
|
}
|
|
540
693
|
const { render } = await import("ink");
|
|
541
|
-
const { default: ConfigsList } = await import("./list-
|
|
694
|
+
const { default: ConfigsList } = await import("./list-56BS25X4.js");
|
|
542
695
|
const { default: React } = await import("react");
|
|
543
696
|
render(React.createElement(ConfigsList, { org }));
|
|
544
697
|
}
|
|
@@ -564,6 +717,14 @@ function maybeShowScopedHelp() {
|
|
|
564
717
|
}
|
|
565
718
|
return false;
|
|
566
719
|
}
|
|
720
|
+
var helpOrVersionFlag = cli.flags.help === true || cli.flags.version === true;
|
|
721
|
+
var versionChecksEnabled = shouldRunVersionChecks({
|
|
722
|
+
command,
|
|
723
|
+
jsonMode,
|
|
724
|
+
quiet,
|
|
725
|
+
helpOrVersionFlag
|
|
726
|
+
});
|
|
727
|
+
var versionChecksPromise = versionChecksEnabled ? runVersionChecks().catch(() => ({ messages: [] })) : null;
|
|
567
728
|
async function main() {
|
|
568
729
|
if (maybeShowScopedHelp()) {
|
|
569
730
|
return;
|
|
@@ -625,7 +786,7 @@ async function main() {
|
|
|
625
786
|
case "detections": {
|
|
626
787
|
const org = await resolveOrg();
|
|
627
788
|
if (subcommand === "healthcheck") {
|
|
628
|
-
const { runDetectionsHealthcheck } = await import("./healthcheck-
|
|
789
|
+
const { runDetectionsHealthcheck } = await import("./healthcheck-6RHZBEVE.js");
|
|
629
790
|
await runDetectionsHealthcheck({
|
|
630
791
|
org,
|
|
631
792
|
source: cli.flags.source,
|
|
@@ -640,7 +801,7 @@ async function main() {
|
|
|
640
801
|
break;
|
|
641
802
|
}
|
|
642
803
|
if (subcommand === "validate") {
|
|
643
|
-
const { runDetectionsValidate } = await import("./validate-
|
|
804
|
+
const { runDetectionsValidate } = await import("./validate-NIBVL7X5.js");
|
|
644
805
|
await runDetectionsValidate({
|
|
645
806
|
org,
|
|
646
807
|
source: cli.flags.source,
|
|
@@ -655,7 +816,7 @@ async function main() {
|
|
|
655
816
|
break;
|
|
656
817
|
}
|
|
657
818
|
if (subcommand === "drift") {
|
|
658
|
-
const { runDetectionsDrift } = await import("./drift-
|
|
819
|
+
const { runDetectionsDrift } = await import("./drift-2JRZK2MC.js");
|
|
659
820
|
await runDetectionsDrift({
|
|
660
821
|
org,
|
|
661
822
|
source: cli.flags.source,
|
|
@@ -669,7 +830,7 @@ async function main() {
|
|
|
669
830
|
break;
|
|
670
831
|
}
|
|
671
832
|
if (subcommand === "run") {
|
|
672
|
-
const { runDetectionsRun } = await import("./run-
|
|
833
|
+
const { runDetectionsRun } = await import("./run-JZNZSCDJ.js");
|
|
673
834
|
await runDetectionsRun({
|
|
674
835
|
org,
|
|
675
836
|
configId: cli.flags.configId,
|
|
@@ -688,7 +849,7 @@ async function main() {
|
|
|
688
849
|
break;
|
|
689
850
|
}
|
|
690
851
|
if (action === "run") {
|
|
691
|
-
const { runDetectionsRun } = await import("./run-
|
|
852
|
+
const { runDetectionsRun } = await import("./run-JZNZSCDJ.js");
|
|
692
853
|
await runDetectionsRun({
|
|
693
854
|
org,
|
|
694
855
|
configId: cli.flags.configId,
|
|
@@ -706,7 +867,7 @@ async function main() {
|
|
|
706
867
|
throw new Error("detections configs update requires --config-id");
|
|
707
868
|
}
|
|
708
869
|
const configPatch = getConfigPatchFromSetFlags();
|
|
709
|
-
const { runDetectionsConfigsUpdate } = await import("./configs-update-
|
|
870
|
+
const { runDetectionsConfigsUpdate } = await import("./configs-update-AHI7T6S4.js");
|
|
710
871
|
await runDetectionsConfigsUpdate({
|
|
711
872
|
org,
|
|
712
873
|
configId: cli.flags.configId,
|
|
@@ -733,7 +894,7 @@ async function main() {
|
|
|
733
894
|
case "metrics": {
|
|
734
895
|
const org = await resolveOrg();
|
|
735
896
|
if (subcommand === "summary") {
|
|
736
|
-
const { runMetricsSummary } = await import("./summary-
|
|
897
|
+
const { runMetricsSummary } = await import("./summary-PA2CCZVO.js");
|
|
737
898
|
await runMetricsSummary({
|
|
738
899
|
org,
|
|
739
900
|
from: cli.flags.from,
|
|
@@ -745,7 +906,7 @@ async function main() {
|
|
|
745
906
|
break;
|
|
746
907
|
}
|
|
747
908
|
if (subcommand === "found") {
|
|
748
|
-
const { runMetricsFound } = await import("./found-
|
|
909
|
+
const { runMetricsFound } = await import("./found-YYYFF4FD.js");
|
|
749
910
|
await runMetricsFound({
|
|
750
911
|
org,
|
|
751
912
|
from: cli.flags.from,
|
|
@@ -762,7 +923,7 @@ async function main() {
|
|
|
762
923
|
if (!by || !["day", "type", "brand"].includes(by)) {
|
|
763
924
|
throw new Error("metrics breakdown requires --by <day|type|brand>");
|
|
764
925
|
}
|
|
765
|
-
const { runMetricsBreakdown } = await import("./breakdown-
|
|
926
|
+
const { runMetricsBreakdown } = await import("./breakdown-BECXDJIS.js");
|
|
766
927
|
await runMetricsBreakdown({
|
|
767
928
|
org,
|
|
768
929
|
by,
|
|
@@ -782,7 +943,7 @@ async function main() {
|
|
|
782
943
|
case "reports": {
|
|
783
944
|
if (subcommand === "list") {
|
|
784
945
|
const org = await resolveOrg();
|
|
785
|
-
const { runReportsList } = await import("./list-
|
|
946
|
+
const { runReportsList } = await import("./list-MHEIY7LQ.js");
|
|
786
947
|
await runReportsList({
|
|
787
948
|
org,
|
|
788
949
|
limit: cli.flags.limit,
|
|
@@ -798,7 +959,7 @@ async function main() {
|
|
|
798
959
|
}
|
|
799
960
|
if (subcommand === "create") {
|
|
800
961
|
const org = await tryResolveOrg();
|
|
801
|
-
const { runReportsCreate } = await import("./create-
|
|
962
|
+
const { runReportsCreate } = await import("./create-RS37JAMM.js");
|
|
802
963
|
await runReportsCreate({
|
|
803
964
|
org,
|
|
804
965
|
title: cli.flags.title,
|
|
@@ -822,7 +983,7 @@ async function main() {
|
|
|
822
983
|
}
|
|
823
984
|
case "queues": {
|
|
824
985
|
if (subcommand === "snapshot") {
|
|
825
|
-
const { runQueuesSnapshot } = await import("./snapshot-
|
|
986
|
+
const { runQueuesSnapshot } = await import("./snapshot-KGMO44YB.js");
|
|
826
987
|
await runQueuesSnapshot({
|
|
827
988
|
org: cli.flags.org,
|
|
828
989
|
all: cli.flags.all,
|
|
@@ -840,7 +1001,7 @@ async function main() {
|
|
|
840
1001
|
}
|
|
841
1002
|
case "presets": {
|
|
842
1003
|
if (subcommand === "list") {
|
|
843
|
-
const { runPresetsList } = await import("./list-
|
|
1004
|
+
const { runPresetsList } = await import("./list-M2UQCXIO.js");
|
|
844
1005
|
await runPresetsList({ outputFormat: cliContext.outputFormat });
|
|
845
1006
|
break;
|
|
846
1007
|
}
|
|
@@ -851,7 +1012,7 @@ async function main() {
|
|
|
851
1012
|
);
|
|
852
1013
|
}
|
|
853
1014
|
const org = await resolveOrg();
|
|
854
|
-
const { runPresetsRun } = await import("./run-
|
|
1015
|
+
const { runPresetsRun } = await import("./run-7NTCD7JI.js");
|
|
855
1016
|
await runPresetsRun({
|
|
856
1017
|
presetId: action,
|
|
857
1018
|
org,
|
|
@@ -868,12 +1029,12 @@ async function main() {
|
|
|
868
1029
|
case "setup":
|
|
869
1030
|
case "install":
|
|
870
1031
|
case "i": {
|
|
871
|
-
const { setupSkill } = await import("./setup-skill-
|
|
1032
|
+
const { setupSkill } = await import("./setup-skill-ZUZ5MYLI.js");
|
|
872
1033
|
setupSkill({ json: jsonMode });
|
|
873
1034
|
break;
|
|
874
1035
|
}
|
|
875
1036
|
case "uninstall": {
|
|
876
|
-
const { uninstallSkill } = await import("./setup-skill-
|
|
1037
|
+
const { uninstallSkill } = await import("./setup-skill-ZUZ5MYLI.js");
|
|
877
1038
|
uninstallSkill({ json: jsonMode });
|
|
878
1039
|
break;
|
|
879
1040
|
}
|
|
@@ -893,12 +1054,26 @@ async function main() {
|
|
|
893
1054
|
}
|
|
894
1055
|
}
|
|
895
1056
|
}
|
|
896
|
-
|
|
1057
|
+
async function flushVersionChecks() {
|
|
1058
|
+
if (!versionChecksPromise) return;
|
|
1059
|
+
let timer;
|
|
1060
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
1061
|
+
timer = setTimeout(() => resolve({ messages: [] }), 500);
|
|
1062
|
+
});
|
|
1063
|
+
try {
|
|
1064
|
+
const result = await Promise.race([versionChecksPromise, timeoutPromise]);
|
|
1065
|
+
printVersionMessages(result.messages);
|
|
1066
|
+
} finally {
|
|
1067
|
+
if (timer) clearTimeout(timer);
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
main().then(flushVersionChecks).catch(async (err) => {
|
|
897
1071
|
const message = err instanceof Error ? err.message : String(err);
|
|
898
1072
|
const exitCode = mapErrorToExitCode(err);
|
|
899
1073
|
if (jsonMode) {
|
|
900
1074
|
jsonError(message, exitCode);
|
|
901
1075
|
}
|
|
902
1076
|
console.error(message);
|
|
1077
|
+
await flushVersionChecks();
|
|
903
1078
|
process.exit(exitCode);
|
|
904
1079
|
});
|
|
@@ -3,9 +3,11 @@ import {
|
|
|
3
3
|
toCsvRows
|
|
4
4
|
} from "./chunk-VFT3TD3E.js";
|
|
5
5
|
import {
|
|
6
|
-
DateTime,
|
|
7
6
|
createApiClient
|
|
8
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-DSOM6TZX.js";
|
|
8
|
+
import {
|
|
9
|
+
DateTime
|
|
10
|
+
} from "./chunk-TFCNKBRC.js";
|
|
9
11
|
import "./chunk-EEG7T6WT.js";
|
|
10
12
|
import "./chunk-U73SABXK.js";
|
|
11
13
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRESETS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-B3CCMSG2.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-DSOM6TZX.js";
|
|
10
|
+
import "./chunk-TFCNKBRC.js";
|
|
10
11
|
import "./chunk-EEG7T6WT.js";
|
|
11
12
|
import "./chunk-U73SABXK.js";
|
|
12
13
|
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getPresetDefinition,
|
|
3
3
|
runPreset
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-B3CCMSG2.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-DSOM6TZX.js";
|
|
11
|
+
import "./chunk-TFCNKBRC.js";
|
|
11
12
|
import "./chunk-EEG7T6WT.js";
|
|
12
13
|
import "./chunk-U73SABXK.js";
|
|
13
14
|
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getBundledSkillContent,
|
|
3
|
+
getBundledSkillVersion,
|
|
4
|
+
isSkillInstalled,
|
|
5
|
+
parseSkillVersion,
|
|
6
|
+
readInstalledSkillVersion,
|
|
7
|
+
setupSkill,
|
|
8
|
+
uninstallSkill
|
|
9
|
+
} from "./chunk-T4DYUWUD.js";
|
|
10
|
+
import "./chunk-IUZB3DQW.js";
|
|
11
|
+
export {
|
|
12
|
+
getBundledSkillContent,
|
|
13
|
+
getBundledSkillVersion,
|
|
14
|
+
isSkillInstalled,
|
|
15
|
+
parseSkillVersion,
|
|
16
|
+
readInstalledSkillVersion,
|
|
17
|
+
setupSkill,
|
|
18
|
+
uninstallSkill
|
|
19
|
+
};
|
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.3.0",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"homepage": "https://chainpatrol.com/docs/cli",
|
|
8
8
|
"keywords": [
|