@chainpatrol/cli 0.15.1 → 0.17.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 +67 -0
- package/dist/{breakdown-27PMFEWQ.js → breakdown-ETSF2BZ3.js} +1 -1
- package/dist/{check-M5U2CSWK.js → check-EMGFDXUO.js} +1 -1
- package/dist/{chunk-IPN6O3N4.js → chunk-75H4KTIQ.js} +96 -42
- package/dist/{chunk-UZDSNWGL.js → chunk-DPLUSURP.js} +9 -0
- package/dist/{chunk-FOTFWUOX.js → chunk-HGAQBGGL.js} +1 -1
- package/dist/cli.js +81 -49
- package/dist/{configs-update-YMPRYXKZ.js → configs-update-EKNLIRA6.js} +1 -1
- package/dist/{create-DQYNYD6G.js → create-M4PV5VVU.js} +1 -1
- package/dist/{drift-T4AF6Y3X.js → drift-RQKTGJWB.js} +1 -1
- package/dist/{found-XEGMO6YY.js → found-LVYBEOS6.js} +1 -1
- package/dist/get-BPTWCJ6H.js +75 -0
- package/dist/{healthcheck-FORGIJWP.js → healthcheck-J6BIZEG7.js} +1 -1
- package/dist/{list-VOWXWXHY.js → list-2ENAEZVP.js} +1 -1
- package/dist/{list-N4TTNR6N.js → list-44BQZHJD.js} +1 -1
- package/dist/{list-ZTMCEG56.js → list-AEM2KSO4.js} +1 -1
- package/dist/{list-OWJLAWMP.js → list-CNF2FGLN.js} +1 -1
- package/dist/{list-XHBQMP6P.js → list-E2JVJNHV.js} +1 -1
- package/dist/{list-BJMP3QEV.js → list-JCJHIJM4.js} +2 -2
- package/dist/{list-RMD4NKVB.js → list-LKNCICLT.js} +1 -1
- package/dist/{list-WPXNCBT3.js → list-O36VWTWR.js} +1 -1
- package/dist/{list-63HPS2RE.js → list-O62BZKQ5.js} +1 -1
- package/dist/{list-COJ5BLQV.js → list-OEJV4UNQ.js} +1 -1
- package/dist/{list-DVPEMSVH.js → list-S5Y6GW43.js} +1 -1
- package/dist/{list-json-X6QHIRE5.js → list-json-ZTS5GXNA.js} +1 -1
- package/dist/{organization-HD6LRAZP.js → organization-PFPBZXJZ.js} +35 -4
- package/dist/{run-V7QJBNLF.js → run-HS2EEOM6.js} +1 -1
- package/dist/{run-BHAH7DUC.js → run-LKPKTH3W.js} +2 -2
- package/dist/{run-G5J77GZD.js → run-R3RR6XQF.js} +1 -1
- package/dist/{search-ME6KFCEV.js → search-EBXEP7XC.js} +1 -1
- package/dist/{setup-skill-6SDQXFTR.js → setup-skill-RPVW2CCQ.js} +1 -1
- package/dist/{snapshot-B6TQCFQ3.js → snapshot-GNZMYTXT.js} +1 -1
- package/dist/{summary-NYHIQ5DP.js → summary-POVQC6B7.js} +1 -1
- package/dist/{validate-I3YHZDZR.js → validate-QE2F4PYY.js} +1 -1
- package/dist/{whoami-6KU4LOJS.js → whoami-24KUJUN4.js} +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,72 @@
|
|
|
1
1
|
# @chainpatrol/cli
|
|
2
2
|
|
|
3
|
+
## 0.17.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 27b18af: Adds `chainpatrol orgs get <slug>` for fetching a single accessible
|
|
8
|
+
organization, backed by a new public `GET /user/orgs/{slug}` endpoint.
|
|
9
|
+
Returns the same per-org shape as one row of `orgs list` (subscription
|
|
10
|
+
status + per-service active/automated flags) and reuses the same access
|
|
11
|
+
gating: org-scoped API keys must match the slug, user sessions / user-scoped
|
|
12
|
+
API keys need an active membership unless the caller is staff. The bundled
|
|
13
|
+
Claude Code skill (`/chainpatrol`) is updated to document the new command.
|
|
14
|
+
Server-side procedure + validation live in the private `@chainpatrol/external-trpc`
|
|
15
|
+
and `@chainpatrol/validation` packages and ship together with this CLI
|
|
16
|
+
release, but are not listed in the frontmatter because they are private.
|
|
17
|
+
|
|
18
|
+
## 0.16.0
|
|
19
|
+
|
|
20
|
+
### Minor Changes
|
|
21
|
+
|
|
22
|
+
- c1af664: Add `--all-my-orgs` to `chainpatrol metrics organization` and raise
|
|
23
|
+
the `--slugs` cap from 50 → 500.
|
|
24
|
+
|
|
25
|
+
`--all-my-orgs` is the shortest path to answer "how many takedowns
|
|
26
|
+
across all clients in the last 7 days?" — no `orgs list` round-trip,
|
|
27
|
+
no client-side slug stitching. The server resolves the org set using
|
|
28
|
+
the same logic as `/user/orgs` (so an agent already familiar with
|
|
29
|
+
that filter shape can reuse it), runs the per-org metric fan-out
|
|
30
|
+
with bounded concurrency, and rolls up totals + per-day /
|
|
31
|
+
per-org-per-day averages + the per-org breakdown in one HTTP call.
|
|
32
|
+
|
|
33
|
+
Pair with `--subscription-status` and/or `--service-active` to
|
|
34
|
+
narrow to the org set the question is actually about — e.g.
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
chainpatrol --json metrics organization \
|
|
38
|
+
--all-my-orgs \
|
|
39
|
+
--subscription-status ACTIVE \
|
|
40
|
+
--service-active takedowns \
|
|
41
|
+
--include takedownsCompleted \
|
|
42
|
+
--from 2026-05-12 --to 2026-05-19
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The `--slugs` form still works for cases where the caller wants a
|
|
46
|
+
specific named subset; both forms cap at 500 orgs per call (previously
|
|
47
|
+
50). The cap remains a backstop — `--all-my-orgs` returns a 413 with
|
|
48
|
+
a clear "narrow with filters" hint if the resolved set exceeds it.
|
|
49
|
+
|
|
50
|
+
Auth:
|
|
51
|
+
|
|
52
|
+
- Org-scoped API keys are rejected for `--all-my-orgs` (they're pinned
|
|
53
|
+
to a single org by design — the default form already covers that
|
|
54
|
+
org).
|
|
55
|
+
- User API keys (inherit owning user's memberships) and Bearer
|
|
56
|
+
sessions can use it; the resolved org set matches what `orgs list`
|
|
57
|
+
would return for the same caller.
|
|
58
|
+
- Staff users still get every active org (subject to the filters).
|
|
59
|
+
|
|
60
|
+
Response now includes `scope.resolvedFrom` (`"api-key" |
|
|
61
|
+
"organizationSlug" | "slugs" | "allMyOrgs"`) so agents can see how
|
|
62
|
+
the server interpreted their request and report it accurately to the
|
|
63
|
+
end user.
|
|
64
|
+
|
|
65
|
+
Skill content is rewritten to lead with `--all-my-orgs` for cross-org
|
|
66
|
+
questions, with `--slugs` repositioned as "use this for an explicit
|
|
67
|
+
subset." Existing `--slugs` examples and the `orgs list` + `--slugs`
|
|
68
|
+
recipe are updated to call out the new single-call alternative.
|
|
69
|
+
|
|
3
70
|
## 0.15.1
|
|
4
71
|
|
|
5
72
|
### Patch Changes
|
|
@@ -195,6 +195,8 @@ description: |
|
|
|
195
195
|
"customer reports", "reports reported by customer", "find detection gaps",
|
|
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
|
+
"get org", "get organization", "show org details", "org by slug",
|
|
199
|
+
"fetch org details", "org info",
|
|
198
200
|
"orgs with takedowns off", "automation off across orgs",
|
|
199
201
|
"which customers have X enabled", "service toggles by org",
|
|
200
202
|
"is this URL blocked", "is this domain blocked", "is this address blocked",
|
|
@@ -657,6 +659,35 @@ Customers see only orgs they're a member of. Staff/superuser sessions see
|
|
|
657
659
|
every matching org. The response is the same in both cases; visibility is
|
|
658
660
|
enforced server-side.
|
|
659
661
|
|
|
662
|
+
### \`orgs get\` \u2014 Get a single organization by slug
|
|
663
|
+
|
|
664
|
+
Look up one organization the caller has access to. Returns the same per-org
|
|
665
|
+
shape as a single row from \`orgs list\` (subscription status + per-service
|
|
666
|
+
active/automated flags), so anything you could read from \`orgs list\` you
|
|
667
|
+
can also read here without paging or filtering. Use it when the user names
|
|
668
|
+
a specific customer ("show me acme's setup", "is takedowns automation on
|
|
669
|
+
for morpho?") and you don't need the rest of the catalogue.
|
|
670
|
+
|
|
671
|
+
\`\`\`bash
|
|
672
|
+
chainpatrol orgs get <slug>
|
|
673
|
+
chainpatrol --json orgs get <slug>
|
|
674
|
+
\`\`\`
|
|
675
|
+
|
|
676
|
+
Permission rules match \`orgs list\`:
|
|
677
|
+
|
|
678
|
+
- Org-scoped API keys: must match the slug \u2014 querying another org returns
|
|
679
|
+
403.
|
|
680
|
+
- User sessions / user-scoped API keys: need an active OrganizationMembership
|
|
681
|
+
on the slug. Staff/superusers can read any org.
|
|
682
|
+
- Soft-deleted orgs are not returned (404). A 404 also fires for orgs the
|
|
683
|
+
caller would not be authorized to see \u2014 the endpoint deliberately does
|
|
684
|
+
not distinguish "doesn't exist" from "you can't see this" beyond the
|
|
685
|
+
generic 403/404 envelope.
|
|
686
|
+
|
|
687
|
+
Prefer \`orgs get\` over \`orgs list\` + client-side filter whenever the
|
|
688
|
+
caller already knows the slug; it's one round-trip and side-steps the
|
|
689
|
+
list filters entirely.
|
|
690
|
+
|
|
660
691
|
#### Use case: finding service configuration gaps across the customer base
|
|
661
692
|
|
|
662
693
|
When the user asks something like "which customers are paying us but don't
|
|
@@ -674,21 +705,25 @@ When the user asks for a number that **spans more than one customer/org/brand**
|
|
|
674
705
|
"across all brands", "company-wide", "total takedowns", "total threats",
|
|
675
706
|
"average takedowns per day across customers", "rollup across customers",
|
|
676
707
|
"how many takedowns in the last 7 days?" (no org named) \u2014 the answer is
|
|
677
|
-
**\`chainpatrol metrics organization --
|
|
678
|
-
|
|
679
|
-
single-org commands and have no \`--slugs
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
708
|
+
**\`chainpatrol metrics organization --all-my-orgs\`** (or \`--slugs\`
|
|
709
|
+
if you want a specific subset). \`summary\`, \`found\`, and \`breakdown\`
|
|
710
|
+
are single-org commands and have **no** \`--slugs\`/\`--all-my-orgs\`
|
|
711
|
+
form; only \`organization\` does. The multi-org form rolls totals +
|
|
712
|
+
per-day / per-org-per-day averages + a per-org breakdown server-side
|
|
713
|
+
in **one** HTTP call.
|
|
683
714
|
|
|
684
715
|
**Anti-patterns to avoid:**
|
|
685
716
|
|
|
686
|
-
- \u274C "There's no cross-org aggregation in the CLI." There is \u2014
|
|
687
|
-
organization --slugs
|
|
688
|
-
|
|
717
|
+
- \u274C "There's no cross-org aggregation in the CLI." There is \u2014
|
|
718
|
+
\`metrics organization --all-my-orgs\` (or \`--slugs\`). Don't bail
|
|
719
|
+
out citing the docs without trying these flags.
|
|
689
720
|
- \u274C Looping \`metrics summary --org X\` once per customer to sum
|
|
690
|
-
client-side.
|
|
691
|
-
the endpoint 503-prone in the first place.
|
|
721
|
+
client-side. The multi-org form is one round-trip; the loop is what
|
|
722
|
+
made the endpoint 503-prone in the first place.
|
|
723
|
+
- \u274C Calling \`orgs list\` to build a slug list when you just want
|
|
724
|
+
"everything." Use \`--all-my-orgs\` and skip the enumeration entirely \u2014
|
|
725
|
+
the server resolves the same set via shared logic. Save \`orgs list\`
|
|
726
|
+
+ \`--slugs\` for cases where the user asked for a specific subset.
|
|
692
727
|
- \u274C Routing the user to Metabase / the data warehouse for a question
|
|
693
728
|
the CLI can answer. Reach for Metabase only when the metric isn't
|
|
694
729
|
in the \`--include\` list (\`reports\`, \`newThreats\`,
|
|
@@ -696,35 +731,46 @@ section below.
|
|
|
696
731
|
\`domainThreats\`, \`twitterThreats\`, \`telegramThreats\`,
|
|
697
732
|
\`otherThreats\`, \`blockedByType\`, \`blockedByDay\`).
|
|
698
733
|
- \u274C "Iterating 100+ orgs one-by-one isn't practical here." Right \u2014
|
|
699
|
-
that's why you don't iterate.
|
|
700
|
-
|
|
701
|
-
|
|
734
|
+
that's why you don't iterate. \`--all-my-orgs\` covers up to 500
|
|
735
|
+
orgs in a single call; for larger fleets, narrow with
|
|
736
|
+
\`--subscription-status\` / \`--service-active\`, or split an
|
|
737
|
+
explicit \`--slugs\` list into batches and sum.
|
|
702
738
|
|
|
703
739
|
#### Common cross-org recipe (copy and run)
|
|
704
740
|
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
chainpatrol --json orgs list \\
|
|
708
|
-
--subscription-status ACTIVE \\
|
|
709
|
-
--service-active takedowns
|
|
741
|
+
The shortest answer to "how many takedowns across all customers in the
|
|
742
|
+
last 7 days?" is a single call \u2014 no \`orgs list\` step needed:
|
|
710
743
|
|
|
711
|
-
|
|
744
|
+
\`\`\`bash
|
|
712
745
|
chainpatrol --json metrics organization \\
|
|
713
|
-
--
|
|
746
|
+
--all-my-orgs \\
|
|
747
|
+
--subscription-status ACTIVE \\
|
|
748
|
+
--service-active takedowns \\
|
|
714
749
|
--include takedownsCompleted \\
|
|
715
750
|
--from <YYYY-MM-DD 7 days ago> --to <YYYY-MM-DD today>
|
|
716
751
|
|
|
717
752
|
# Read these fields from the JSON:
|
|
718
|
-
# .
|
|
719
|
-
# .
|
|
720
|
-
# .averages.
|
|
721
|
-
# .
|
|
753
|
+
# .scope.orgs \u2190 which orgs the server resolved
|
|
754
|
+
# .metrics.takedownsCompleted \u2190 grand total across all orgs
|
|
755
|
+
# .averages.perDay.takedownsCompleted \u2190 total / windowDays
|
|
756
|
+
# .averages.perOrgPerDay.takedownsCompleted \u2190 total / numOrgs / windowDays
|
|
757
|
+
# .perOrg[slug].metrics.takedownsCompleted \u2190 per-customer breakdown
|
|
722
758
|
\`\`\`
|
|
723
759
|
|
|
724
760
|
Replace \`takedownsCompleted\` with whatever metric the user asked about.
|
|
725
761
|
Replace the date range with whatever window they asked about (default
|
|
726
762
|
3 months if they didn't say).
|
|
727
763
|
|
|
764
|
+
If the user *asked* for a specific subset of customers ("how many for
|
|
765
|
+
acme, beta, gamma combined?"), use \`--slugs acme,beta,gamma\` instead
|
|
766
|
+
of \`--all-my-orgs\`. \`--slugs\` accepts up to 500 entries.
|
|
767
|
+
|
|
768
|
+
**Auth note for \`--all-my-orgs\`:** requires a user session or a
|
|
769
|
+
user-scoped API key (it inherits your memberships). Org-scoped API
|
|
770
|
+
keys are rejected \u2014 they're pinned to a single org by design. If
|
|
771
|
+
\`whoami\` shows an org-scoped key, use a Bearer session instead or
|
|
772
|
+
ask the user to provision a user API key.
|
|
773
|
+
|
|
728
774
|
#### Single-org examples
|
|
729
775
|
|
|
730
776
|
\`\`\`bash
|
|
@@ -826,36 +872,44 @@ The response shape switches to multi-org:
|
|
|
826
872
|
}
|
|
827
873
|
\`\`\`
|
|
828
874
|
|
|
829
|
-
To answer **"across all of our customers"**,
|
|
875
|
+
To answer **"across all of our customers"**, prefer \`--all-my-orgs\` \u2014
|
|
876
|
+
it lets the server resolve the org set in one round-trip, no \`orgs
|
|
877
|
+
list\` step needed:
|
|
830
878
|
|
|
831
879
|
\`\`\`bash
|
|
832
|
-
chainpatrol --json orgs list --subscription-status ACTIVE # \u2190 get slugs
|
|
833
880
|
chainpatrol --json metrics organization \\
|
|
834
|
-
--
|
|
881
|
+
--all-my-orgs \\
|
|
882
|
+
--subscription-status ACTIVE \\
|
|
883
|
+
--service-active takedowns \\
|
|
835
884
|
--include takedownsCompleted \\
|
|
836
885
|
--from 2026-05-12 --to 2026-05-19
|
|
837
886
|
\`\`\`
|
|
838
887
|
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
888
|
+
Use \`--slugs <comma-list>\` only when the user asked for a *specific*
|
|
889
|
+
subset of customers ("how many for acme, beta, gamma combined?"); the
|
|
890
|
+
two flags are mutually exclusive. Both forms cap at 500 orgs per call;
|
|
891
|
+
if a real fleet exceeds that, narrow with
|
|
892
|
+
\`--subscription-status\`/\`--service-active\` filters or split an
|
|
893
|
+
explicit \`--slugs\` list into batches and sum. **Do not loop
|
|
894
|
+
\`metrics organization\` once per org** \u2014 N HTTP round-trips is what
|
|
895
|
+
made the old code 503.
|
|
843
896
|
|
|
844
|
-
Always pair
|
|
845
|
-
what the user actually asked about. "How many takedowns did
|
|
846
|
-
complete?" \u2192 \`--include takedownsCompleted\`; "average reports
|
|
847
|
-
day per customer?" \u2192 \`--include reports\`. Skipping \`--include\`
|
|
848
|
-
~11 aggregates per org which is rarely necessary.
|
|
897
|
+
Always pair the multi-org form with \`--include\` to narrow the metric
|
|
898
|
+
set to what the user actually asked about. "How many takedowns did
|
|
899
|
+
we complete?" \u2192 \`--include takedownsCompleted\`; "average reports
|
|
900
|
+
per day per customer?" \u2192 \`--include reports\`. Skipping \`--include\`
|
|
901
|
+
runs ~11 aggregates per org which is rarely necessary.
|
|
849
902
|
|
|
850
|
-
#### Auth: which credential can use \`--slugs\`
|
|
903
|
+
#### Auth: which credential can use \`--slugs\` / \`--all-my-orgs\`
|
|
851
904
|
|
|
852
905
|
- **Org-scoped API key** (the most common production key): can only
|
|
853
|
-
query its own org. If \`--slugs\` contains
|
|
854
|
-
returns 403. Use the \`--org\`
|
|
906
|
+
query its own org. If \`--slugs\` contains foreign orgs the server
|
|
907
|
+
returns 403; \`--all-my-orgs\` is also rejected. Use the \`--org\`
|
|
908
|
+
form (or omit both) instead.
|
|
855
909
|
- **User API key**: inherits the user's org memberships, so it can
|
|
856
910
|
query any org the user is a member of (or any org for staff
|
|
857
|
-
users)
|
|
858
|
-
from automated agents.
|
|
911
|
+
users) via \`--slugs\` or \`--all-my-orgs\`. This is the credential
|
|
912
|
+
you want for cross-org rollups from automated agents.
|
|
859
913
|
- **Session auth** (Bearer token, e.g. \`chainpatrol login\`): same
|
|
860
914
|
membership rules as the user. Staff users can query any org.
|
|
861
915
|
|
|
@@ -279,6 +279,9 @@ function createApiClient(options) {
|
|
|
279
279
|
return requestGet("/organization/metrics", {
|
|
280
280
|
organizationSlug: input.organizationSlug,
|
|
281
281
|
slugs: input.slugs,
|
|
282
|
+
allMyOrgs: input.allMyOrgs ? "true" : void 0,
|
|
283
|
+
subscriptionStatus: input.subscriptionStatus,
|
|
284
|
+
services: input.services ? JSON.stringify(input.services) : void 0,
|
|
282
285
|
brandSlug: input.brandSlug,
|
|
283
286
|
startDate: parseIsoDateString(input.startDate),
|
|
284
287
|
endDate: parseIsoDateString(input.endDate),
|
|
@@ -294,6 +297,12 @@ function createApiClient(options) {
|
|
|
294
297
|
getUserOrgs(input) {
|
|
295
298
|
return request("/user/orgs", input);
|
|
296
299
|
},
|
|
300
|
+
getUserOrg(input) {
|
|
301
|
+
return requestGet(
|
|
302
|
+
`/user/orgs/${encodeURIComponent(input.slug)}`,
|
|
303
|
+
{}
|
|
304
|
+
);
|
|
305
|
+
},
|
|
297
306
|
listAssets(input) {
|
|
298
307
|
return request("/asset/list", {
|
|
299
308
|
type: input.type,
|
package/dist/cli.js
CHANGED
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
getCliVersion,
|
|
14
14
|
isSkillInstalled,
|
|
15
15
|
readInstalledSkillVersion
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-75H4KTIQ.js";
|
|
17
17
|
import "./chunk-NQT77IB7.js";
|
|
18
18
|
import {
|
|
19
19
|
DateTime
|
|
@@ -263,22 +263,25 @@ var HELP = {
|
|
|
263
263
|
]
|
|
264
264
|
},
|
|
265
265
|
"metrics organization": {
|
|
266
|
-
description: "Get organization metrics via the documented GET /organization/metrics endpoint. Works with org-scoped API keys (
|
|
267
|
-
usage: "chainpatrol metrics organization [--org <slug>|--slugs <list
|
|
266
|
+
description: "Get organization metrics via the documented GET /organization/metrics endpoint. Works with org-scoped API keys (single-org only), user API keys (inherits user permissions, multi-org allowed), and session auth. Defaults to the trailing 3 months if no date range is given. Use --include to skip aggregates; use --slugs OR --all-my-orgs for multi-org rollups (totals + per-day / per-org-per-day averages + per-org breakdown). Fields not included come back as null.",
|
|
267
|
+
usage: "chainpatrol metrics organization [--org <slug>|--slugs <list>|--all-my-orgs] [--from ISO --to ISO|--this-week] [--include <list>]",
|
|
268
268
|
options: [
|
|
269
|
-
"--org <slug>
|
|
270
|
-
"--slugs <list>
|
|
271
|
-
"--
|
|
272
|
-
"--
|
|
273
|
-
"--
|
|
274
|
-
"--
|
|
275
|
-
"--
|
|
269
|
+
"--org <slug> Single org. Mutually exclusive with --slugs and --all-my-orgs.",
|
|
270
|
+
"--slugs <list> Comma list of org slugs (cap 500). Response is the rollup across these orgs.",
|
|
271
|
+
"--all-my-orgs Shortcut for 'every org I can see'. Server resolves via the same logic as `orgs list` (org-scoped API keys are rejected). Cap 500 \u2014 narrow with --subscription-status / --service-active if you have more.",
|
|
272
|
+
"--subscription-status <list> With --all-my-orgs: only ACTIVE / TRIAL / PROSPECT / INTEGRATION (default: same set as `orgs list`).",
|
|
273
|
+
"--service-active <list> With --all-my-orgs: only orgs with the listed service(s) active. Allowed: reporting, reviewing, protection, takedowns, detection, darkWebMonitoring.",
|
|
274
|
+
"--brand-slug <slug> Filter by brand slug (requires brands-metrics flag; single-org only).",
|
|
275
|
+
"--from <iso> Start date (ISO 8601, e.g. 2026-01-01)",
|
|
276
|
+
"--to <iso> End date (ISO 8601)",
|
|
277
|
+
"--this-week Use the current ISO week (overrides default)",
|
|
278
|
+
"--include <list> Comma list of fields to compute: reports,newThreats,threatsWatchlisted,takedownsFiled,takedownsCompleted,domainThreats,twitterThreats,telegramThreats,otherThreats,blockedByType,blockedByDay (omit for all)"
|
|
276
279
|
],
|
|
277
280
|
examples: [
|
|
278
|
-
"chainpatrol metrics organization --org acme # defaults to last 3 months
|
|
281
|
+
"chainpatrol metrics organization --org acme # defaults to last 3 months",
|
|
279
282
|
"chainpatrol metrics organization --org acme --include takedownsFiled,takedownsCompleted",
|
|
280
|
-
"chainpatrol metrics organization --
|
|
281
|
-
"chainpatrol metrics organization --
|
|
283
|
+
"chainpatrol metrics organization --slugs acme,beta,gamma --include takedownsCompleted --from 2026-05-12 --to 2026-05-19",
|
|
284
|
+
"chainpatrol metrics organization --all-my-orgs --service-active takedowns --subscription-status ACTIVE --include takedownsCompleted --from 2026-05-12 --to 2026-05-19 # cross-customer total in one call",
|
|
282
285
|
"CHAINPATROL_API_KEY=... chainpatrol metrics organization --json --include reports"
|
|
283
286
|
]
|
|
284
287
|
},
|
|
@@ -471,10 +474,11 @@ var HELP = {
|
|
|
471
474
|
]
|
|
472
475
|
},
|
|
473
476
|
orgs: {
|
|
474
|
-
description: "List organizations, their assets, and asset groups. Top-level `orgs list` lists organizations accessible to you; the nested subcommands operate on the org tied to your API key.",
|
|
475
|
-
usage: "chainpatrol orgs <list|assets list|asset-groups list>",
|
|
477
|
+
description: "List organizations, their assets, and asset groups. Top-level `orgs list` lists organizations accessible to you; `orgs get <slug>` returns details for a single accessible org; the nested subcommands operate on the org tied to your API key.",
|
|
478
|
+
usage: "chainpatrol orgs <list|get <slug>|assets list|asset-groups list>",
|
|
476
479
|
examples: [
|
|
477
480
|
"chainpatrol orgs list",
|
|
481
|
+
"chainpatrol orgs get acme",
|
|
478
482
|
"chainpatrol orgs assets list --asset-type URL",
|
|
479
483
|
"chainpatrol orgs asset-groups list"
|
|
480
484
|
]
|
|
@@ -525,6 +529,18 @@ var HELP = {
|
|
|
525
529
|
"chainpatrol --json orgs list --subscription-status ACTIVE,TRIAL --service-active detection"
|
|
526
530
|
]
|
|
527
531
|
},
|
|
532
|
+
"orgs get": {
|
|
533
|
+
description: "Get a single organization by slug. Returns the same shape as one row of `orgs list` (subscription status + per-service active/automated flags). Caller must have access: org-scoped API keys must match the slug, user sessions/keys need an active membership unless the user is staff.",
|
|
534
|
+
usage: "chainpatrol orgs get <slug>",
|
|
535
|
+
options: [
|
|
536
|
+
"--org <slug> Use the saved default org instead of a positional argument."
|
|
537
|
+
],
|
|
538
|
+
examples: [
|
|
539
|
+
"chainpatrol orgs get acme",
|
|
540
|
+
"chainpatrol --json orgs get acme",
|
|
541
|
+
"chainpatrol orgs get acme --output markdown"
|
|
542
|
+
]
|
|
543
|
+
},
|
|
528
544
|
healthchecks: {
|
|
529
545
|
description: "Run organization healthchecks via the public API. Use `list` to see every available check (including planned ones not yet implemented on the backend) and `run` to execute one or all implemented checks.",
|
|
530
546
|
usage: "chainpatrol healthchecks <list|run <id>|run --all>",
|
|
@@ -864,6 +880,7 @@ ${getTopLevelHelp()}
|
|
|
864
880
|
by: { type: "string" },
|
|
865
881
|
include: { type: "string" },
|
|
866
882
|
slugs: { type: "string" },
|
|
883
|
+
allMyOrgs: { type: "boolean", default: false },
|
|
867
884
|
brand: { type: "string" },
|
|
868
885
|
enable: { type: "boolean", default: false },
|
|
869
886
|
disable: { type: "boolean", default: false },
|
|
@@ -1037,12 +1054,12 @@ function parseAttachmentUrls() {
|
|
|
1037
1054
|
}
|
|
1038
1055
|
async function handleConfigsList(org) {
|
|
1039
1056
|
if (jsonMode) {
|
|
1040
|
-
const { listConfigsJson } = await import("./list-json-
|
|
1057
|
+
const { listConfigsJson } = await import("./list-json-ZTS5GXNA.js");
|
|
1041
1058
|
await listConfigsJson({ org });
|
|
1042
1059
|
return;
|
|
1043
1060
|
}
|
|
1044
1061
|
const { render } = await import("ink");
|
|
1045
|
-
const { default: ConfigsList } = await import("./list-
|
|
1062
|
+
const { default: ConfigsList } = await import("./list-AEM2KSO4.js");
|
|
1046
1063
|
const { default: React } = await import("react");
|
|
1047
1064
|
render(React.createElement(ConfigsList, { org }));
|
|
1048
1065
|
}
|
|
@@ -1142,7 +1159,7 @@ async function main() {
|
|
|
1142
1159
|
const positionals = cli.input.slice(2);
|
|
1143
1160
|
const flagAssets = parseAssetInputs();
|
|
1144
1161
|
const contents = [...positionals, ...flagAssets];
|
|
1145
|
-
const { runAssetCheck } = await import("./check-
|
|
1162
|
+
const { runAssetCheck } = await import("./check-EMGFDXUO.js");
|
|
1146
1163
|
await runAssetCheck({
|
|
1147
1164
|
contents,
|
|
1148
1165
|
json: jsonMode,
|
|
@@ -1152,7 +1169,7 @@ async function main() {
|
|
|
1152
1169
|
break;
|
|
1153
1170
|
}
|
|
1154
1171
|
if (subcommand === "list") {
|
|
1155
|
-
const { runAssetList } = await import("./list-
|
|
1172
|
+
const { runAssetList } = await import("./list-2ENAEZVP.js");
|
|
1156
1173
|
await runAssetList({
|
|
1157
1174
|
type: cli.flags.type ?? cli.flags.assetType,
|
|
1158
1175
|
status: cli.flags.assetStatus ?? cli.flags.status,
|
|
@@ -1173,7 +1190,7 @@ async function main() {
|
|
|
1173
1190
|
case "detections": {
|
|
1174
1191
|
const org = await resolveOrg();
|
|
1175
1192
|
if (subcommand === "healthcheck") {
|
|
1176
|
-
const { runDetectionsHealthcheck } = await import("./healthcheck-
|
|
1193
|
+
const { runDetectionsHealthcheck } = await import("./healthcheck-J6BIZEG7.js");
|
|
1177
1194
|
await runDetectionsHealthcheck({
|
|
1178
1195
|
org,
|
|
1179
1196
|
source: cli.flags.source,
|
|
@@ -1188,7 +1205,7 @@ async function main() {
|
|
|
1188
1205
|
break;
|
|
1189
1206
|
}
|
|
1190
1207
|
if (subcommand === "validate") {
|
|
1191
|
-
const { runDetectionsValidate } = await import("./validate-
|
|
1208
|
+
const { runDetectionsValidate } = await import("./validate-QE2F4PYY.js");
|
|
1192
1209
|
await runDetectionsValidate({
|
|
1193
1210
|
org,
|
|
1194
1211
|
source: cli.flags.source,
|
|
@@ -1203,7 +1220,7 @@ async function main() {
|
|
|
1203
1220
|
break;
|
|
1204
1221
|
}
|
|
1205
1222
|
if (subcommand === "drift") {
|
|
1206
|
-
const { runDetectionsDrift } = await import("./drift-
|
|
1223
|
+
const { runDetectionsDrift } = await import("./drift-RQKTGJWB.js");
|
|
1207
1224
|
await runDetectionsDrift({
|
|
1208
1225
|
org,
|
|
1209
1226
|
source: cli.flags.source,
|
|
@@ -1217,7 +1234,7 @@ async function main() {
|
|
|
1217
1234
|
break;
|
|
1218
1235
|
}
|
|
1219
1236
|
if (subcommand === "run") {
|
|
1220
|
-
const { runDetectionsRun } = await import("./run-
|
|
1237
|
+
const { runDetectionsRun } = await import("./run-HS2EEOM6.js");
|
|
1221
1238
|
await runDetectionsRun({
|
|
1222
1239
|
org,
|
|
1223
1240
|
configId: cli.flags.configId,
|
|
@@ -1231,7 +1248,7 @@ async function main() {
|
|
|
1231
1248
|
break;
|
|
1232
1249
|
}
|
|
1233
1250
|
if (subcommand === "list") {
|
|
1234
|
-
const { runDetectionsList } = await import("./list-
|
|
1251
|
+
const { runDetectionsList } = await import("./list-LKNCICLT.js");
|
|
1235
1252
|
await runDetectionsList({
|
|
1236
1253
|
org,
|
|
1237
1254
|
cursor: cli.flags.cursor,
|
|
@@ -1260,7 +1277,7 @@ async function main() {
|
|
|
1260
1277
|
break;
|
|
1261
1278
|
}
|
|
1262
1279
|
if (action === "run") {
|
|
1263
|
-
const { runDetectionsRun } = await import("./run-
|
|
1280
|
+
const { runDetectionsRun } = await import("./run-HS2EEOM6.js");
|
|
1264
1281
|
await runDetectionsRun({
|
|
1265
1282
|
org,
|
|
1266
1283
|
configId: cli.flags.configId,
|
|
@@ -1278,7 +1295,7 @@ async function main() {
|
|
|
1278
1295
|
throw new Error("detections configs update requires --config-id");
|
|
1279
1296
|
}
|
|
1280
1297
|
const configPatch = getConfigPatchFromSetFlags();
|
|
1281
|
-
const { runDetectionsConfigsUpdate } = await import("./configs-update-
|
|
1298
|
+
const { runDetectionsConfigsUpdate } = await import("./configs-update-EKNLIRA6.js");
|
|
1282
1299
|
await runDetectionsConfigsUpdate({
|
|
1283
1300
|
org,
|
|
1284
1301
|
configId: cli.flags.configId,
|
|
@@ -1312,11 +1329,15 @@ async function main() {
|
|
|
1312
1329
|
case "metrics": {
|
|
1313
1330
|
if (subcommand === "organization" || subcommand === "org") {
|
|
1314
1331
|
const slugs = parseCsvList(cli.flags.slugs);
|
|
1315
|
-
const
|
|
1316
|
-
const
|
|
1332
|
+
const allMyOrgs = cli.flags.allMyOrgs;
|
|
1333
|
+
const org2 = slugs && slugs.length > 0 || allMyOrgs ? void 0 : await tryResolveOrg();
|
|
1334
|
+
const { runMetricsOrganization } = await import("./organization-PFPBZXJZ.js");
|
|
1317
1335
|
await runMetricsOrganization({
|
|
1318
1336
|
org: org2,
|
|
1319
1337
|
slugs,
|
|
1338
|
+
allMyOrgs: allMyOrgs || void 0,
|
|
1339
|
+
subscriptionStatus: parseCsvList(cli.flags.subscriptionStatus),
|
|
1340
|
+
serviceActive: parseCsvList(cli.flags.serviceActive),
|
|
1320
1341
|
brandSlug: cli.flags.brandSlug,
|
|
1321
1342
|
from: cli.flags.from,
|
|
1322
1343
|
to: cli.flags.to,
|
|
@@ -1329,7 +1350,7 @@ async function main() {
|
|
|
1329
1350
|
}
|
|
1330
1351
|
const org = await resolveOrg();
|
|
1331
1352
|
if (subcommand === "summary") {
|
|
1332
|
-
const { runMetricsSummary } = await import("./summary-
|
|
1353
|
+
const { runMetricsSummary } = await import("./summary-POVQC6B7.js");
|
|
1333
1354
|
await runMetricsSummary({
|
|
1334
1355
|
org,
|
|
1335
1356
|
from: cli.flags.from,
|
|
@@ -1342,7 +1363,7 @@ async function main() {
|
|
|
1342
1363
|
break;
|
|
1343
1364
|
}
|
|
1344
1365
|
if (subcommand === "found") {
|
|
1345
|
-
const { runMetricsFound } = await import("./found-
|
|
1366
|
+
const { runMetricsFound } = await import("./found-LVYBEOS6.js");
|
|
1346
1367
|
await runMetricsFound({
|
|
1347
1368
|
org,
|
|
1348
1369
|
from: cli.flags.from,
|
|
@@ -1359,7 +1380,7 @@ async function main() {
|
|
|
1359
1380
|
if (!by || !["day", "type", "brand"].includes(by)) {
|
|
1360
1381
|
throw new Error("metrics breakdown requires --by <day|type|brand>");
|
|
1361
1382
|
}
|
|
1362
|
-
const { runMetricsBreakdown } = await import("./breakdown-
|
|
1383
|
+
const { runMetricsBreakdown } = await import("./breakdown-ETSF2BZ3.js");
|
|
1363
1384
|
await runMetricsBreakdown({
|
|
1364
1385
|
org,
|
|
1365
1386
|
by,
|
|
@@ -1380,7 +1401,7 @@ async function main() {
|
|
|
1380
1401
|
case "reports": {
|
|
1381
1402
|
if (subcommand === "list") {
|
|
1382
1403
|
const org = await resolveOrg();
|
|
1383
|
-
const { runReportsList } = await import("./list-
|
|
1404
|
+
const { runReportsList } = await import("./list-44BQZHJD.js");
|
|
1384
1405
|
await runReportsList({
|
|
1385
1406
|
org,
|
|
1386
1407
|
limit: cli.flags.limit,
|
|
@@ -1408,7 +1429,7 @@ async function main() {
|
|
|
1408
1429
|
}
|
|
1409
1430
|
if (subcommand === "create") {
|
|
1410
1431
|
const org = await tryResolveOrg();
|
|
1411
|
-
const { runReportsCreate } = await import("./create-
|
|
1432
|
+
const { runReportsCreate } = await import("./create-M4PV5VVU.js");
|
|
1412
1433
|
await runReportsCreate({
|
|
1413
1434
|
org,
|
|
1414
1435
|
title: cli.flags.title,
|
|
@@ -1429,7 +1450,7 @@ async function main() {
|
|
|
1429
1450
|
const positionals = cli.input.slice(2);
|
|
1430
1451
|
const flagAssets = parseAssetInputs();
|
|
1431
1452
|
const assetContents = [...positionals, ...flagAssets];
|
|
1432
|
-
const { runReportsSearch } = await import("./search-
|
|
1453
|
+
const { runReportsSearch } = await import("./search-EBXEP7XC.js");
|
|
1433
1454
|
await runReportsSearch({
|
|
1434
1455
|
assetContents,
|
|
1435
1456
|
reportedByCustomer: cli.flags.reportedByCustomer,
|
|
@@ -1445,7 +1466,7 @@ async function main() {
|
|
|
1445
1466
|
}
|
|
1446
1467
|
case "threats": {
|
|
1447
1468
|
if (subcommand === "list") {
|
|
1448
|
-
const { runThreatsList } = await import("./list-
|
|
1469
|
+
const { runThreatsList } = await import("./list-E2JVJNHV.js");
|
|
1449
1470
|
await runThreatsList({
|
|
1450
1471
|
query: cli.flags.query,
|
|
1451
1472
|
from: cli.flags.from,
|
|
@@ -1468,7 +1489,7 @@ async function main() {
|
|
|
1468
1489
|
case "takedowns": {
|
|
1469
1490
|
if (subcommand === "list") {
|
|
1470
1491
|
const org = await tryResolveOrg();
|
|
1471
|
-
const { runTakedownsList } = await import("./list-
|
|
1492
|
+
const { runTakedownsList } = await import("./list-S5Y6GW43.js");
|
|
1472
1493
|
await runTakedownsList({
|
|
1473
1494
|
org,
|
|
1474
1495
|
query: cli.flags.query,
|
|
@@ -1499,7 +1520,7 @@ async function main() {
|
|
|
1499
1520
|
}
|
|
1500
1521
|
case "queues": {
|
|
1501
1522
|
if (subcommand === "snapshot") {
|
|
1502
|
-
const { runQueuesSnapshot } = await import("./snapshot-
|
|
1523
|
+
const { runQueuesSnapshot } = await import("./snapshot-GNZMYTXT.js");
|
|
1503
1524
|
await runQueuesSnapshot({
|
|
1504
1525
|
org: cli.flags.org,
|
|
1505
1526
|
all: cli.flags.all,
|
|
@@ -1517,7 +1538,7 @@ async function main() {
|
|
|
1517
1538
|
}
|
|
1518
1539
|
case "orgs": {
|
|
1519
1540
|
if (subcommand === "list") {
|
|
1520
|
-
const { runOrgsList } = await import("./list-
|
|
1541
|
+
const { runOrgsList } = await import("./list-OEJV4UNQ.js");
|
|
1521
1542
|
await runOrgsList({
|
|
1522
1543
|
query: cli.flags.query,
|
|
1523
1544
|
subscriptionStatus: cli.flags.subscriptionStatus,
|
|
@@ -1530,9 +1551,20 @@ async function main() {
|
|
|
1530
1551
|
});
|
|
1531
1552
|
break;
|
|
1532
1553
|
}
|
|
1554
|
+
if (subcommand === "get") {
|
|
1555
|
+
const positionalSlug = action;
|
|
1556
|
+
const slug = positionalSlug ?? await tryResolveOrg();
|
|
1557
|
+
const { runOrgsGet } = await import("./get-BPTWCJ6H.js");
|
|
1558
|
+
await runOrgsGet({
|
|
1559
|
+
slug,
|
|
1560
|
+
json: jsonMode,
|
|
1561
|
+
outputFormat: cliContext.outputFormat
|
|
1562
|
+
});
|
|
1563
|
+
break;
|
|
1564
|
+
}
|
|
1533
1565
|
if (subcommand === "assets") {
|
|
1534
1566
|
if (action === "list") {
|
|
1535
|
-
const { runOrgsAssetsList } = await import("./list-
|
|
1567
|
+
const { runOrgsAssetsList } = await import("./list-O62BZKQ5.js");
|
|
1536
1568
|
await runOrgsAssetsList({
|
|
1537
1569
|
type: cli.flags.type ?? cli.flags.assetType,
|
|
1538
1570
|
groupId: cli.flags.groupId,
|
|
@@ -1548,7 +1580,7 @@ async function main() {
|
|
|
1548
1580
|
}
|
|
1549
1581
|
if (subcommand === "asset-groups") {
|
|
1550
1582
|
if (action === "list") {
|
|
1551
|
-
const { runOrgsAssetGroupsList } = await import("./list-
|
|
1583
|
+
const { runOrgsAssetGroupsList } = await import("./list-O36VWTWR.js");
|
|
1552
1584
|
await runOrgsAssetGroupsList({
|
|
1553
1585
|
json: jsonMode,
|
|
1554
1586
|
outputFormat: cliContext.outputFormat
|
|
@@ -1557,13 +1589,13 @@ async function main() {
|
|
|
1557
1589
|
}
|
|
1558
1590
|
throw new Error("Usage: chainpatrol orgs asset-groups list");
|
|
1559
1591
|
}
|
|
1560
|
-
const hint = subcommand ? suggest(subcommand, ["list", "assets", "asset-groups"]) : null;
|
|
1592
|
+
const hint = subcommand ? suggest(subcommand, ["list", "get", "assets", "asset-groups"]) : null;
|
|
1561
1593
|
throw new Error(
|
|
1562
|
-
subcommand ? `Unknown subcommand: orgs ${subcommand}${hint ? `. Did you mean "orgs ${hint}"?` : ""}` : "Usage: chainpatrol orgs <list|assets list|asset-groups list> [filters]"
|
|
1594
|
+
subcommand ? `Unknown subcommand: orgs ${subcommand}${hint ? `. Did you mean "orgs ${hint}"?` : ""}` : "Usage: chainpatrol orgs <list|get <slug>|assets list|asset-groups list> [filters]"
|
|
1563
1595
|
);
|
|
1564
1596
|
}
|
|
1565
1597
|
case "whoami": {
|
|
1566
|
-
const { runWhoami } = await import("./whoami-
|
|
1598
|
+
const { runWhoami } = await import("./whoami-24KUJUN4.js");
|
|
1567
1599
|
await runWhoami({
|
|
1568
1600
|
json: jsonMode,
|
|
1569
1601
|
outputFormat: cliContext.outputFormat
|
|
@@ -1572,7 +1604,7 @@ async function main() {
|
|
|
1572
1604
|
}
|
|
1573
1605
|
case "healthchecks": {
|
|
1574
1606
|
if (subcommand === "list") {
|
|
1575
|
-
const { runHealthchecksList } = await import("./list-
|
|
1607
|
+
const { runHealthchecksList } = await import("./list-CNF2FGLN.js");
|
|
1576
1608
|
await runHealthchecksList({
|
|
1577
1609
|
json: jsonMode,
|
|
1578
1610
|
outputFormat: cliContext.outputFormat
|
|
@@ -1586,7 +1618,7 @@ async function main() {
|
|
|
1586
1618
|
thresholds.minResults = cli.flags.minResults;
|
|
1587
1619
|
if (cli.flags.lookbackHours !== void 0)
|
|
1588
1620
|
thresholds.lookbackHours = cli.flags.lookbackHours;
|
|
1589
|
-
const { runHealthchecksRun } = await import("./run-
|
|
1621
|
+
const { runHealthchecksRun } = await import("./run-R3RR6XQF.js");
|
|
1590
1622
|
await runHealthchecksRun({
|
|
1591
1623
|
org,
|
|
1592
1624
|
id: action,
|
|
@@ -1604,7 +1636,7 @@ async function main() {
|
|
|
1604
1636
|
}
|
|
1605
1637
|
case "presets": {
|
|
1606
1638
|
if (subcommand === "list") {
|
|
1607
|
-
const { runPresetsList } = await import("./list-
|
|
1639
|
+
const { runPresetsList } = await import("./list-JCJHIJM4.js");
|
|
1608
1640
|
await runPresetsList({ outputFormat: cliContext.outputFormat });
|
|
1609
1641
|
break;
|
|
1610
1642
|
}
|
|
@@ -1615,7 +1647,7 @@ async function main() {
|
|
|
1615
1647
|
);
|
|
1616
1648
|
}
|
|
1617
1649
|
const org = await resolveOrg();
|
|
1618
|
-
const { runPresetsRun } = await import("./run-
|
|
1650
|
+
const { runPresetsRun } = await import("./run-LKPKTH3W.js");
|
|
1619
1651
|
await runPresetsRun({
|
|
1620
1652
|
presetId: action,
|
|
1621
1653
|
org,
|
|
@@ -1632,12 +1664,12 @@ async function main() {
|
|
|
1632
1664
|
case "setup":
|
|
1633
1665
|
case "install":
|
|
1634
1666
|
case "i": {
|
|
1635
|
-
const { setupSkill } = await import("./setup-skill-
|
|
1667
|
+
const { setupSkill } = await import("./setup-skill-RPVW2CCQ.js");
|
|
1636
1668
|
setupSkill({ json: jsonMode, cloud: cli.flags.cloud });
|
|
1637
1669
|
break;
|
|
1638
1670
|
}
|
|
1639
1671
|
case "uninstall": {
|
|
1640
|
-
const { uninstallSkill } = await import("./setup-skill-
|
|
1672
|
+
const { uninstallSkill } = await import("./setup-skill-RPVW2CCQ.js");
|
|
1641
1673
|
uninstallSkill({ json: jsonMode });
|
|
1642
1674
|
break;
|
|
1643
1675
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
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-DPLUSURP.js";
|
|
12
|
+
import "./chunk-EGWK6SRQ.js";
|
|
13
|
+
import "./chunk-TFCNKBRC.js";
|
|
14
|
+
import "./chunk-U73SABXK.js";
|
|
15
|
+
|
|
16
|
+
// src/commands/orgs/get.ts
|
|
17
|
+
function summarizeServices(org) {
|
|
18
|
+
const parts = [];
|
|
19
|
+
for (const service of ["reporting", "reviewing", "protection", "takedowns"]) {
|
|
20
|
+
const entry = org.services[service];
|
|
21
|
+
if (!entry.active) continue;
|
|
22
|
+
parts.push(`${service}=${entry.automated ? "auto" : "manual"}`);
|
|
23
|
+
}
|
|
24
|
+
if (org.services.detection.active) parts.push("detection");
|
|
25
|
+
if (org.services.darkWebMonitoring.active) parts.push("darkWebMonitoring");
|
|
26
|
+
return parts.length > 0 ? parts.join(" ") : "(no services enabled)";
|
|
27
|
+
}
|
|
28
|
+
async function runOrgsGet(options) {
|
|
29
|
+
const slug = options.slug?.trim();
|
|
30
|
+
if (!slug) {
|
|
31
|
+
throw new CliExitError(
|
|
32
|
+
"orgs get requires an organization slug. Example: chainpatrol orgs get acme",
|
|
33
|
+
ExitCode.USAGE
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
const client = options.apiClient ?? createApiClient();
|
|
37
|
+
const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
|
|
38
|
+
const result = await client.getUserOrg({ slug });
|
|
39
|
+
const { organization } = result;
|
|
40
|
+
printOutput({
|
|
41
|
+
outputFormat,
|
|
42
|
+
json: result,
|
|
43
|
+
markdown: [
|
|
44
|
+
`# ${organization.name} (\`${organization.slug}\`)`,
|
|
45
|
+
"",
|
|
46
|
+
`- Subscription status: ${organization.subscriptionStatus}`,
|
|
47
|
+
`- Services: ${summarizeServices(organization)}`
|
|
48
|
+
].join("\n"),
|
|
49
|
+
csv: toCsvRows([
|
|
50
|
+
{
|
|
51
|
+
slug: organization.slug,
|
|
52
|
+
name: organization.name,
|
|
53
|
+
subscriptionStatus: organization.subscriptionStatus,
|
|
54
|
+
reportingActive: organization.services.reporting.active,
|
|
55
|
+
reportingAutomated: organization.services.reporting.automated,
|
|
56
|
+
reviewingActive: organization.services.reviewing.active,
|
|
57
|
+
reviewingAutomated: organization.services.reviewing.automated,
|
|
58
|
+
protectionActive: organization.services.protection.active,
|
|
59
|
+
protectionAutomated: organization.services.protection.automated,
|
|
60
|
+
takedownsActive: organization.services.takedowns.active,
|
|
61
|
+
takedownsAutomated: organization.services.takedowns.automated,
|
|
62
|
+
detectionActive: organization.services.detection.active,
|
|
63
|
+
darkWebMonitoringActive: organization.services.darkWebMonitoring.active
|
|
64
|
+
}
|
|
65
|
+
]),
|
|
66
|
+
human: () => {
|
|
67
|
+
console.log(`${organization.name} (slug: ${organization.slug})`);
|
|
68
|
+
console.log(` subscriptionStatus: ${organization.subscriptionStatus}`);
|
|
69
|
+
console.log(` services: ${summarizeServices(organization)}`);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
export {
|
|
74
|
+
runOrgsGet
|
|
75
|
+
};
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
PRESETS
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-HGAQBGGL.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-DPLUSURP.js";
|
|
10
10
|
import "./chunk-EGWK6SRQ.js";
|
|
11
11
|
import "./chunk-TFCNKBRC.js";
|
|
12
12
|
import "./chunk-U73SABXK.js";
|
|
@@ -8,12 +8,34 @@ import {
|
|
|
8
8
|
import {
|
|
9
9
|
ORGANIZATION_METRICS_INCLUDE_FIELDS,
|
|
10
10
|
createApiClient
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-DPLUSURP.js";
|
|
12
12
|
import "./chunk-EGWK6SRQ.js";
|
|
13
13
|
import "./chunk-TFCNKBRC.js";
|
|
14
14
|
import "./chunk-U73SABXK.js";
|
|
15
15
|
|
|
16
16
|
// src/commands/metrics/organization.ts
|
|
17
|
+
var SERVICE_KEYS = [
|
|
18
|
+
"reporting",
|
|
19
|
+
"reviewing",
|
|
20
|
+
"protection",
|
|
21
|
+
"takedowns",
|
|
22
|
+
"detection",
|
|
23
|
+
"darkWebMonitoring"
|
|
24
|
+
];
|
|
25
|
+
var SERVICE_KEY_SET = new Set(SERVICE_KEYS);
|
|
26
|
+
function buildServicesFilter(raw) {
|
|
27
|
+
if (!raw || raw.length === 0) return void 0;
|
|
28
|
+
const filter = {};
|
|
29
|
+
for (const value of raw) {
|
|
30
|
+
if (!SERVICE_KEY_SET.has(value)) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Unknown --service-active value: '${value}'. Allowed: ${SERVICE_KEYS.join(", ")}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
filter[value] = { active: true };
|
|
36
|
+
}
|
|
37
|
+
return filter;
|
|
38
|
+
}
|
|
17
39
|
var INCLUDE_VALUES = new Set(ORGANIZATION_METRICS_INCLUDE_FIELDS);
|
|
18
40
|
var METRIC_LABELS = [
|
|
19
41
|
["reports", "Reports"],
|
|
@@ -50,8 +72,13 @@ function populatedMetricRows(metrics, format = (v) => String(v)) {
|
|
|
50
72
|
]);
|
|
51
73
|
}
|
|
52
74
|
async function runMetricsOrganization(options) {
|
|
53
|
-
|
|
54
|
-
|
|
75
|
+
const explicitForms = [
|
|
76
|
+
Boolean(options.org),
|
|
77
|
+
Boolean(options.slugs && options.slugs.length > 0),
|
|
78
|
+
Boolean(options.allMyOrgs)
|
|
79
|
+
].filter(Boolean).length;
|
|
80
|
+
if (explicitForms > 1) {
|
|
81
|
+
throw new Error("Pass at most one of --org, --slugs, or --all-my-orgs.");
|
|
55
82
|
}
|
|
56
83
|
const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
|
|
57
84
|
const client = options.apiClient ?? createApiClient();
|
|
@@ -61,9 +88,13 @@ async function runMetricsOrganization(options) {
|
|
|
61
88
|
thisWeek: options.thisWeek
|
|
62
89
|
});
|
|
63
90
|
const include = parseIncludeList(options.include);
|
|
91
|
+
const services = buildServicesFilter(options.serviceActive);
|
|
64
92
|
const input = {
|
|
65
93
|
organizationSlug: options.org,
|
|
66
94
|
slugs: options.slugs && options.slugs.length > 0 ? options.slugs : void 0,
|
|
95
|
+
allMyOrgs: options.allMyOrgs || void 0,
|
|
96
|
+
subscriptionStatus: options.subscriptionStatus && options.subscriptionStatus.length > 0 ? options.subscriptionStatus : void 0,
|
|
97
|
+
services,
|
|
67
98
|
brandSlug: options.brandSlug,
|
|
68
99
|
startDate: range.startDate,
|
|
69
100
|
endDate: range.endDate,
|
|
@@ -71,7 +102,7 @@ async function runMetricsOrganization(options) {
|
|
|
71
102
|
};
|
|
72
103
|
const result = await client.getOrganizationMetrics(input);
|
|
73
104
|
const isMulti = result.scope.mode === "multi";
|
|
74
|
-
const scopeLabel = isMulti ? `${result.scope.orgs.length} orgs (${result.scope.orgs.join(", ")})` : result.scope.orgs[0] ?? options.org ?? "your organization";
|
|
105
|
+
const scopeLabel = isMulti ? `${result.scope.orgs.length} orgs${result.scope.resolvedFrom === "allMyOrgs" ? " (--all-my-orgs)" : ` (${result.scope.orgs.slice(0, 10).join(", ")}${result.scope.orgs.length > 10 ? ", \u2026" : ""})`}` : result.scope.orgs[0] ?? options.org ?? "your organization";
|
|
75
106
|
const includeLabel = include ? ` (include: ${include.join(", ")})` : "";
|
|
76
107
|
const totalsRows = populatedMetricRows(result.metrics);
|
|
77
108
|
const perDayRows = result.averages.perDay ? populatedMetricRows(result.averages.perDay, formatAverage) : [];
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getPresetDefinition,
|
|
3
3
|
runPreset
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-HGAQBGGL.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-DPLUSURP.js";
|
|
11
11
|
import "./chunk-EGWK6SRQ.js";
|
|
12
12
|
import "./chunk-TFCNKBRC.js";
|
|
13
13
|
import "./chunk-U73SABXK.js";
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@chainpatrol/cli",
|
|
3
3
|
"description": "The official ChainPatrol CLI — terminal interface for threat detection",
|
|
4
4
|
"author": "Umar Ahmed <umar@chainpatrol.io>",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.17.0",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"homepage": "https://chainpatrol.com/docs/cli",
|
|
8
8
|
"keywords": [
|