@chainpatrol/cli 0.20.0 → 0.22.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 +33 -0
- package/dist/{chunk-3LJTWYT6.js → chunk-LKITFBIG.js} +156 -13
- package/dist/cli.js +23 -7
- package/dist/{get-K7VHV5X2.js → get-GTACVCLE.js} +37 -2
- package/dist/{list-QXA6GLCV.js → list-JK4SRMPL.js} +89 -4
- package/dist/{setup-skill-A36WQLMU.js → setup-skill-V3HDWN7T.js} +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @chainpatrol/cli
|
|
2
2
|
|
|
3
|
+
## 0.22.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- e2b28a5: Expose `contactUrl` (main communication channel) on `orgs list` and `orgs get` output. Add `organization.missing-contact-url` healthcheck. Server-side work in @chainpatrol/external-trpc and @chainpatrol/validation supports the new field and healthcheck; both are private packages and don't need version bumps.
|
|
8
|
+
- b1e35f0: Surface pending Wallet Blocking / Takedown approvals on `orgs list` and `orgs get`. Each org now reports `pendingServiceApprovals` — open requests by lower-level members to enable an approval-gated service (Wallet Blocking / Takedowns) that a higher-up at the org has not yet approved. Adds `--pending-approval-active`, `--pending-approval-inactive`, and `--pending-approval-service <protection|takedowns>` filters to `orgs list` so you can track which organizations are awaiting sign-off, distinct from which have the service turned off. Server-side work in @chainpatrol/external-trpc and @chainpatrol/validation backs the new field and filters; both are private packages and don't need version bumps.
|
|
9
|
+
|
|
10
|
+
## 0.21.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- b3e77eb: `orgs list` and `orgs get` now surface each organization's "Obligatory
|
|
15
|
+
Organization Admin Approval" state (`Organization.requiresCustomerReview`)
|
|
16
|
+
alongside its per-asset-type scope. The new response field is
|
|
17
|
+
`obligatoryAdminApproval: { active, assetTypes }` — an empty `assetTypes`
|
|
18
|
+
list with `active: true` means approval applies to every asset type
|
|
19
|
+
(matches the in-app UI's behaviour when no narrowing has been configured).
|
|
20
|
+
|
|
21
|
+
`orgs list` also accepts three new filters that compose with the
|
|
22
|
+
existing ones via AND:
|
|
23
|
+
|
|
24
|
+
- `--obligatory-approval-active` / `--obligatory-approval-inactive` —
|
|
25
|
+
filter on the toggle itself.
|
|
26
|
+
- `--obligatory-approval-asset-type <csv>` — return only orgs whose
|
|
27
|
+
approval scope covers every listed asset type (an org's empty
|
|
28
|
+
per-asset-type list still counts as "all types"). Passing this flag
|
|
29
|
+
implies the feature is on, so it can't be combined with
|
|
30
|
+
`--obligatory-approval-inactive`.
|
|
31
|
+
|
|
32
|
+
Server-side work in `@chainpatrol/external-trpc` and `@chainpatrol/validation`
|
|
33
|
+
extends the `/user/orgs` and `/user/orgs/{slug}` schemas accordingly; both
|
|
34
|
+
are private packages and don't need version bumps.
|
|
35
|
+
|
|
3
36
|
## 0.20.0
|
|
4
37
|
|
|
5
38
|
### Minor Changes
|
|
@@ -199,6 +199,18 @@ description: |
|
|
|
199
199
|
"fetch org details", "org info",
|
|
200
200
|
"orgs with takedowns off", "automation off across orgs",
|
|
201
201
|
"which customers have X enabled", "service toggles by org",
|
|
202
|
+
"obligatory admin approval", "obligatory organization admin approval",
|
|
203
|
+
"obligatory approval", "admin approval enabled", "admin approval orgs",
|
|
204
|
+
"requires customer review", "requires admin approval",
|
|
205
|
+
"orgs that require admin approval", "orgs requiring approval",
|
|
206
|
+
"which orgs require approval for twitter", "approval scope",
|
|
207
|
+
"admin approval asset types", "approval per asset type",
|
|
208
|
+
"pending approval", "pending approvals", "pending service approval",
|
|
209
|
+
"orgs with pending approval", "pending wallet blocking approval",
|
|
210
|
+
"pending takedown approval", "awaiting approval", "waiting for approval",
|
|
211
|
+
"is protection active approval", "wallet blocking approval",
|
|
212
|
+
"who needs to approve wallet blocking", "service change request",
|
|
213
|
+
"track orgs with pending approval", "orgs awaiting sign-off",
|
|
202
214
|
"is this URL blocked", "is this domain blocked", "is this address blocked",
|
|
203
215
|
"check this asset", "asset check", "lookup asset status",
|
|
204
216
|
"what is ARCHIVE_ORG", "what does PAGE mean", "list asset types",
|
|
@@ -656,14 +668,17 @@ Guide. Key signals in the response:
|
|
|
656
668
|
|
|
657
669
|
Use \`--all\` to snapshot every org you have access to instead of a single slug.
|
|
658
670
|
|
|
659
|
-
### \`orgs list\` \u2014 List organizations with subscription status and
|
|
671
|
+
### \`orgs list\` \u2014 List organizations with subscription status, service toggles, and admin-approval scope
|
|
660
672
|
|
|
661
673
|
Returns every organization the caller can see, with each org's
|
|
662
|
-
subscription status (\`PROSPECT\`, \`TRIAL\`, \`ACTIVE\`, \`INTEGRATION\`)
|
|
663
|
-
which services are active
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
674
|
+
subscription status (\`PROSPECT\`, \`TRIAL\`, \`ACTIVE\`, \`INTEGRATION\`),
|
|
675
|
+
which services are active, and the per-org "Obligatory Organization
|
|
676
|
+
Admin Approval" toggle (with its asset-type scope). Use it to answer
|
|
677
|
+
questions like "which customers have takedowns enabled but automation
|
|
678
|
+
off?", "which prospects don't have detection turned on yet?", or "which
|
|
679
|
+
orgs require admin approval before adding Twitter assets to the
|
|
680
|
+
blocklist?" \u2014 filters compose with AND and are applied server-side, so
|
|
681
|
+
one call returns the final list.
|
|
667
682
|
|
|
668
683
|
\`\`\`bash
|
|
669
684
|
chainpatrol --json orgs list \\
|
|
@@ -686,6 +701,34 @@ misleading filters like "reporting.automated=true" that don't mean
|
|
|
686
701
|
anything. \`detection\` and \`darkWebMonitoring\` have always been
|
|
687
702
|
single-flag services.
|
|
688
703
|
|
|
704
|
+
#### Obligatory Organization Admin Approval
|
|
705
|
+
|
|
706
|
+
Each org also has a separate "Obligatory Organization Admin Approval"
|
|
707
|
+
toggle (\`Organization.requiresCustomerReview\` in the schema, surfaced
|
|
708
|
+
on the Services settings page in the app) that gates customer-side
|
|
709
|
+
review of proposals ChainPatrol staff have already approved before
|
|
710
|
+
they're added to the blocklist. It is NOT one of the operational
|
|
711
|
+
services above \u2014 it is an org-policy toggle with its own per-asset-type
|
|
712
|
+
scope.
|
|
713
|
+
|
|
714
|
+
Each org's response includes:
|
|
715
|
+
|
|
716
|
+
\`\`\`json
|
|
717
|
+
{
|
|
718
|
+
"obligatoryAdminApproval": {
|
|
719
|
+
"active": true,
|
|
720
|
+
"assetTypes": ["TWITTER", "URL"]
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
\`\`\`
|
|
724
|
+
|
|
725
|
+
- \`active=true\` + empty \`assetTypes\` means approval applies to
|
|
726
|
+
**all** asset types \u2014 the org has not narrowed the scope.
|
|
727
|
+
- \`active=true\` + a non-empty \`assetTypes\` list means approval is
|
|
728
|
+
required only for those asset types.
|
|
729
|
+
- \`active=false\` means no approval is required; \`assetTypes\` is
|
|
730
|
+
always empty in that case.
|
|
731
|
+
|
|
689
732
|
So per-org JSON looks like:
|
|
690
733
|
|
|
691
734
|
\`\`\`json
|
|
@@ -697,16 +740,55 @@ So per-org JSON looks like:
|
|
|
697
740
|
"takedowns": { "active": true, "automated": false },
|
|
698
741
|
"detection": { "active": true },
|
|
699
742
|
"darkWebMonitoring": { "active": false }
|
|
700
|
-
}
|
|
743
|
+
},
|
|
744
|
+
"obligatoryAdminApproval": { "active": true, "assetTypes": ["TWITTER"] },
|
|
745
|
+
"pendingServiceApprovals": [
|
|
746
|
+
{
|
|
747
|
+
"service": "protection",
|
|
748
|
+
"automated": false,
|
|
749
|
+
"serviceType": "isProtectionActive",
|
|
750
|
+
"serviceName": "Wallet Blocking",
|
|
751
|
+
"requestedAt": "2026-05-30T18:04:11.000Z"
|
|
752
|
+
}
|
|
753
|
+
]
|
|
701
754
|
}
|
|
702
755
|
\`\`\`
|
|
703
756
|
|
|
757
|
+
#### Pending service approvals (Wallet Blocking / Takedowns)
|
|
758
|
+
|
|
759
|
+
Turning on **Wallet Blocking** (\`protection\`, the "is protection active"
|
|
760
|
+
toggle) or **Takedowns** is approval-gated: when a lower-level org member
|
|
761
|
+
tries to enable one, ChainPatrol records a pending request that a higher-up
|
|
762
|
+
at the org \u2014 an org OWNER, or ChainPatrol staff acting as owner \u2014 must
|
|
763
|
+
approve before the service actually turns on. \`pendingServiceApprovals\`
|
|
764
|
+
lists those open, awaiting-sign-off requests per org.
|
|
765
|
+
|
|
766
|
+
This is the answer to "which organizations have a pending approval for
|
|
767
|
+
Wallet blocking or Takedown services?" \u2014 it is NOT the same as "which orgs
|
|
768
|
+
have Wallet Blocking turned off". An org can have \`protection.active=false\`
|
|
769
|
+
with **no** pending approval (nobody has asked) OR **with** a pending
|
|
770
|
+
approval (someone asked and it's waiting on an owner). Only
|
|
771
|
+
\`pendingServiceApprovals\` distinguishes the two.
|
|
772
|
+
|
|
773
|
+
Each entry:
|
|
774
|
+
|
|
775
|
+
- \`service\` \u2014 \`protection\` (Wallet Blocking) or \`takedowns\`, matching the
|
|
776
|
+
\`services\` keys.
|
|
777
|
+
- \`automated\` \u2014 \`true\` when the request is for the automated variant
|
|
778
|
+
(e.g. "Automated Wallet Blocking").
|
|
779
|
+
- \`serviceType\` \u2014 raw enum, e.g. \`isProtectionActive\`.
|
|
780
|
+
- \`serviceName\` \u2014 human label, e.g. \`Wallet Blocking\`.
|
|
781
|
+
- \`requestedAt\` \u2014 when the enable request was submitted (ISO 8601, UTC).
|
|
782
|
+
|
|
783
|
+
An **empty array means nothing is awaiting approval** \u2014 read \`services\` for
|
|
784
|
+
the current on/off state, never infer it from this field.
|
|
785
|
+
|
|
704
786
|
When a user asks about "automated reporting / reviewing / protection",
|
|
705
787
|
explain that the flag exists in the DB but has no operational effect and
|
|
706
788
|
isn't exposed by the public API \u2014 only \`takedowns.automated\` carries a
|
|
707
789
|
real meaning.
|
|
708
790
|
|
|
709
|
-
Filter flags (all optional, all comma-separated lists):
|
|
791
|
+
Filter flags (all optional, all comma-separated lists where noted):
|
|
710
792
|
|
|
711
793
|
- \`--query <text>\` partial name match (substring, case-insensitive)
|
|
712
794
|
- \`--subscription-status <list>\` one or more of \`PROSPECT\`, \`TRIAL\`,
|
|
@@ -719,6 +801,25 @@ Filter flags (all optional, all comma-separated lists):
|
|
|
719
801
|
meaningful automation toggle. Passing any other service name errors out.
|
|
720
802
|
- \`--service-manual <list>\` services whose automation must be OFF.
|
|
721
803
|
Same restriction \u2014 only \`takedowns\`.
|
|
804
|
+
- \`--obligatory-approval-active\` \u2014 only orgs with admin approval on
|
|
805
|
+
- \`--obligatory-approval-inactive\` \u2014 only orgs with admin approval off
|
|
806
|
+
- \`--obligatory-approval-asset-type <list>\` \u2014 only orgs whose admin
|
|
807
|
+
approval scope covers EVERY listed asset type. The match treats an
|
|
808
|
+
org's empty per-asset-type list as "applies to all asset types", so a
|
|
809
|
+
fully-broad org matches every value passed here. Passing this flag
|
|
810
|
+
implies the feature is on, so it can't be combined with
|
|
811
|
+
\`--obligatory-approval-inactive\`. Use the canonical asset-type enum
|
|
812
|
+
names (\`TWITTER\`, \`URL\`, \`PAGE\`, \u2026); run \`chainpatrol asset types\`
|
|
813
|
+
to see them all.
|
|
814
|
+
- \`--pending-approval-active\` \u2014 only orgs with at least one open
|
|
815
|
+
service-enable approval awaiting a higher-up's sign-off.
|
|
816
|
+
- \`--pending-approval-inactive\` \u2014 only orgs with no pending approvals.
|
|
817
|
+
- \`--pending-approval-service <list>\` \u2014 only orgs with a pending approval
|
|
818
|
+
for one of the listed services. Accepts \`protection\` (Wallet Blocking)
|
|
819
|
+
and/or \`takedowns\` \u2014 those are the only approval-gated services.
|
|
820
|
+
Matches both the manual and automated variant. Passing this flag implies
|
|
821
|
+
a pending approval exists, so it can't be combined with
|
|
822
|
+
\`--pending-approval-inactive\`.
|
|
722
823
|
|
|
723
824
|
Service names: \`reporting\`, \`reviewing\`, \`protection\`, \`takedowns\`,
|
|
724
825
|
\`detection\`, \`darkWebMonitoring\`.
|
|
@@ -727,16 +828,58 @@ Customers see only orgs they're a member of. Staff/superuser sessions see
|
|
|
727
828
|
every matching org. The response is the same in both cases; visibility is
|
|
728
829
|
enforced server-side.
|
|
729
830
|
|
|
831
|
+
#### Use case: finding which orgs require admin approval for an asset type
|
|
832
|
+
|
|
833
|
+
When a user asks "which orgs require admin approval before blocking
|
|
834
|
+
Twitter assets?", reach straight for the filter \u2014 no client-side
|
|
835
|
+
post-processing needed:
|
|
836
|
+
|
|
837
|
+
\`\`\`bash
|
|
838
|
+
chainpatrol --json orgs list --obligatory-approval-asset-type TWITTER
|
|
839
|
+
\`\`\`
|
|
840
|
+
|
|
841
|
+
To audit just the broad opt-ins ("which orgs require approval for
|
|
842
|
+
everything?"), filter on the toggle and inspect \`assetTypes\` in the
|
|
843
|
+
output \u2014 an empty array means "all":
|
|
844
|
+
|
|
845
|
+
\`\`\`bash
|
|
846
|
+
chainpatrol --json orgs list --obligatory-approval-active \\
|
|
847
|
+
| jq '.organizations[] | select(.obligatoryAdminApproval.assetTypes | length == 0) | .slug'
|
|
848
|
+
\`\`\`
|
|
849
|
+
|
|
850
|
+
#### Use case: tracking orgs with a pending Wallet Blocking / Takedown approval
|
|
851
|
+
|
|
852
|
+
When a user asks "which organizations have a pending approval for Wallet
|
|
853
|
+
blocking or Takedown services?", do NOT reach for \`--service-inactive
|
|
854
|
+
protection\` \u2014 that lists orgs with the service turned OFF, which is a
|
|
855
|
+
different question. Use the pending-approval filter, which surfaces
|
|
856
|
+
staff-initiated enable requests still waiting on a higher-up:
|
|
857
|
+
|
|
858
|
+
\`\`\`bash
|
|
859
|
+
# Every org with a pending Wallet Blocking OR Takedown approval:
|
|
860
|
+
chainpatrol --json orgs list --pending-approval-service protection,takedowns
|
|
861
|
+
|
|
862
|
+
# Just Wallet Blocking:
|
|
863
|
+
chainpatrol --json orgs list --pending-approval-service protection
|
|
864
|
+
|
|
865
|
+
# Any pending approval at all, then read each org's pendingServiceApprovals:
|
|
866
|
+
chainpatrol --json orgs list --pending-approval-active \\
|
|
867
|
+
| jq '.organizations[] | {slug, pending: [.pendingServiceApprovals[].serviceName]}'
|
|
868
|
+
\`\`\`
|
|
869
|
+
|
|
730
870
|
### \`orgs get\` \u2014 Get a single organization by slug
|
|
731
871
|
|
|
732
872
|
Look up one organization the caller has access to. Returns the same per-org
|
|
733
873
|
shape as a single row from \`orgs list\` \u2014 \`active\` for every service,
|
|
734
874
|
plus \`automated\` on \`takedowns\` only (see the note above on why other
|
|
735
|
-
services don't expose an automation flag)
|
|
736
|
-
\`
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
875
|
+
services don't expose an automation flag), plus \`obligatoryAdminApproval\`
|
|
876
|
+
(\`active\` + \`assetTypes\`) and \`pendingServiceApprovals\` (open Wallet
|
|
877
|
+
Blocking / Takedown enable requests awaiting a higher-up's sign-off).
|
|
878
|
+
Anything you could read from \`orgs list\` you can also read here without
|
|
879
|
+
paging or filtering. Use it when the user names a specific customer ("show
|
|
880
|
+
me acme's setup", "is takedowns automation on for morpho?", "does this org
|
|
881
|
+
require admin approval for Twitter?", "is morpho waiting on a Wallet
|
|
882
|
+
Blocking approval?") and you don't need the rest of the catalogue.
|
|
740
883
|
|
|
741
884
|
\`\`\`bash
|
|
742
885
|
chainpatrol orgs get <slug>
|
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-LKITFBIG.js";
|
|
17
17
|
import "./chunk-6VTWWGNV.js";
|
|
18
18
|
import {
|
|
19
19
|
DateTime
|
|
@@ -532,12 +532,16 @@ var HELP = {
|
|
|
532
532
|
"--service-active <l> Comma-separated services that must be active",
|
|
533
533
|
"--service-inactive <l> Comma-separated services that must be inactive",
|
|
534
534
|
"--service-automated <l> Services whose automation must be ON (only `takedowns` is accepted \u2014 the other services have no meaningful automation toggle)",
|
|
535
|
-
"--service-manual <l> Services whose automation must be OFF (only `takedowns` is accepted)"
|
|
535
|
+
"--service-manual <l> Services whose automation must be OFF (only `takedowns` is accepted)",
|
|
536
|
+
"--pending-approval-active Only orgs with an open service-enable approval awaiting a higher-up's sign-off",
|
|
537
|
+
"--pending-approval-inactive Only orgs with no pending service-enable approvals",
|
|
538
|
+
"--pending-approval-service <l> Only orgs with a pending approval for the listed service(s) \u2014 `protection` (Wallet Blocking) and/or `takedowns`"
|
|
536
539
|
],
|
|
537
540
|
examples: [
|
|
538
541
|
"chainpatrol orgs list --subscription-status ACTIVE",
|
|
539
542
|
"chainpatrol orgs list --service-active takedowns --service-manual takedowns",
|
|
540
|
-
"chainpatrol --json orgs list --subscription-status ACTIVE,TRIAL --service-active detection"
|
|
543
|
+
"chainpatrol --json orgs list --subscription-status ACTIVE,TRIAL --service-active detection",
|
|
544
|
+
"chainpatrol --json orgs list --pending-approval-service protection,takedowns"
|
|
541
545
|
]
|
|
542
546
|
},
|
|
543
547
|
"orgs get": {
|
|
@@ -916,6 +920,12 @@ ${getTopLevelHelp()}
|
|
|
916
920
|
serviceInactive: { type: "string" },
|
|
917
921
|
serviceAutomated: { type: "string" },
|
|
918
922
|
serviceManual: { type: "string" },
|
|
923
|
+
obligatoryApprovalActive: { type: "boolean" },
|
|
924
|
+
obligatoryApprovalInactive: { type: "boolean" },
|
|
925
|
+
obligatoryApprovalAssetType: { type: "string" },
|
|
926
|
+
pendingApprovalActive: { type: "boolean" },
|
|
927
|
+
pendingApprovalInactive: { type: "boolean" },
|
|
928
|
+
pendingApprovalService: { type: "string" },
|
|
919
929
|
query: { type: "string" },
|
|
920
930
|
assetType: { type: "string" },
|
|
921
931
|
takedownStatus: { type: "string" },
|
|
@@ -1558,7 +1568,7 @@ async function main() {
|
|
|
1558
1568
|
}
|
|
1559
1569
|
case "orgs": {
|
|
1560
1570
|
if (subcommand === "list") {
|
|
1561
|
-
const { runOrgsList } = await import("./list-
|
|
1571
|
+
const { runOrgsList } = await import("./list-JK4SRMPL.js");
|
|
1562
1572
|
await runOrgsList({
|
|
1563
1573
|
query: cli.flags.query,
|
|
1564
1574
|
subscriptionStatus: cli.flags.subscriptionStatus,
|
|
@@ -1566,6 +1576,12 @@ async function main() {
|
|
|
1566
1576
|
serviceInactive: cli.flags.serviceInactive,
|
|
1567
1577
|
serviceAutomated: cli.flags.serviceAutomated,
|
|
1568
1578
|
serviceManual: cli.flags.serviceManual,
|
|
1579
|
+
obligatoryApprovalActive: cli.flags.obligatoryApprovalActive,
|
|
1580
|
+
obligatoryApprovalInactive: cli.flags.obligatoryApprovalInactive,
|
|
1581
|
+
obligatoryApprovalAssetType: cli.flags.obligatoryApprovalAssetType,
|
|
1582
|
+
pendingApprovalActive: cli.flags.pendingApprovalActive,
|
|
1583
|
+
pendingApprovalInactive: cli.flags.pendingApprovalInactive,
|
|
1584
|
+
pendingApprovalService: cli.flags.pendingApprovalService,
|
|
1569
1585
|
json: jsonMode,
|
|
1570
1586
|
outputFormat: cliContext.outputFormat
|
|
1571
1587
|
});
|
|
@@ -1574,7 +1590,7 @@ async function main() {
|
|
|
1574
1590
|
if (subcommand === "get") {
|
|
1575
1591
|
const positionalSlug = action;
|
|
1576
1592
|
const slug = positionalSlug ?? await tryResolveOrg();
|
|
1577
|
-
const { runOrgsGet } = await import("./get-
|
|
1593
|
+
const { runOrgsGet } = await import("./get-GTACVCLE.js");
|
|
1578
1594
|
await runOrgsGet({
|
|
1579
1595
|
slug,
|
|
1580
1596
|
json: jsonMode,
|
|
@@ -1705,12 +1721,12 @@ async function main() {
|
|
|
1705
1721
|
case "setup":
|
|
1706
1722
|
case "install":
|
|
1707
1723
|
case "i": {
|
|
1708
|
-
const { setupSkill } = await import("./setup-skill-
|
|
1724
|
+
const { setupSkill } = await import("./setup-skill-V3HDWN7T.js");
|
|
1709
1725
|
setupSkill({ json: jsonMode, cloud: cli.flags.cloud });
|
|
1710
1726
|
break;
|
|
1711
1727
|
}
|
|
1712
1728
|
case "uninstall": {
|
|
1713
|
-
const { uninstallSkill } = await import("./setup-skill-
|
|
1729
|
+
const { uninstallSkill } = await import("./setup-skill-V3HDWN7T.js");
|
|
1714
1730
|
uninstallSkill({ json: jsonMode });
|
|
1715
1731
|
break;
|
|
1716
1732
|
}
|
|
@@ -26,6 +26,18 @@ function summarizeServices(org) {
|
|
|
26
26
|
if (org.services.darkWebMonitoring.active) parts.push("darkWebMonitoring");
|
|
27
27
|
return parts.length > 0 ? parts.join(" ") : "(no services enabled)";
|
|
28
28
|
}
|
|
29
|
+
function summarizeObligatoryAdminApproval(org) {
|
|
30
|
+
if (!org.obligatoryAdminApproval.active) return "off";
|
|
31
|
+
const types = org.obligatoryAdminApproval.assetTypes;
|
|
32
|
+
return types.length === 0 ? "on (all asset types)" : `on (${types.join(", ")})`;
|
|
33
|
+
}
|
|
34
|
+
function summarizePendingApprovals(org) {
|
|
35
|
+
if (org.pendingServiceApprovals.length === 0) return "none";
|
|
36
|
+
const labels = Array.from(
|
|
37
|
+
new Set(org.pendingServiceApprovals.map((approval) => approval.serviceName))
|
|
38
|
+
);
|
|
39
|
+
return labels.join(", ");
|
|
40
|
+
}
|
|
29
41
|
async function runOrgsGet(options) {
|
|
30
42
|
const slug = options.slug?.trim();
|
|
31
43
|
if (!slug) {
|
|
@@ -44,13 +56,17 @@ async function runOrgsGet(options) {
|
|
|
44
56
|
markdown: [
|
|
45
57
|
`# ${organization.name} (\`${organization.slug}\`)`,
|
|
46
58
|
"",
|
|
59
|
+
`- Contact URL: ${organization.contactUrl ?? "(not set)"}`,
|
|
47
60
|
`- Subscription status: ${organization.subscriptionStatus}`,
|
|
48
|
-
`- Services: ${summarizeServices(organization)}
|
|
61
|
+
`- Services: ${summarizeServices(organization)}`,
|
|
62
|
+
`- Obligatory admin approval: ${summarizeObligatoryAdminApproval(organization)}`,
|
|
63
|
+
`- Pending service approvals: ${summarizePendingApprovals(organization)}`
|
|
49
64
|
].join("\n"),
|
|
50
65
|
csv: toCsvRows([
|
|
51
66
|
{
|
|
52
67
|
slug: organization.slug,
|
|
53
68
|
name: organization.name,
|
|
69
|
+
contactUrl: organization.contactUrl ?? "",
|
|
54
70
|
subscriptionStatus: organization.subscriptionStatus,
|
|
55
71
|
reportingActive: organization.services.reporting.active,
|
|
56
72
|
reviewingActive: organization.services.reviewing.active,
|
|
@@ -59,13 +75,32 @@ async function runOrgsGet(options) {
|
|
|
59
75
|
// `takedowns` is the only service with a meaningful automation flag.
|
|
60
76
|
takedownsAutomated: organization.services.takedowns.automated,
|
|
61
77
|
detectionActive: organization.services.detection.active,
|
|
62
|
-
darkWebMonitoringActive: organization.services.darkWebMonitoring.active
|
|
78
|
+
darkWebMonitoringActive: organization.services.darkWebMonitoring.active,
|
|
79
|
+
obligatoryAdminApprovalActive: organization.obligatoryAdminApproval.active,
|
|
80
|
+
// Empty list + active=true means "applies to all asset types" — read
|
|
81
|
+
// the active column alongside this one to interpret.
|
|
82
|
+
obligatoryAdminApprovalAssetTypes: organization.obligatoryAdminApproval.assetTypes.join("|"),
|
|
83
|
+
// Pending staff requests to enable Wallet Blocking / Takedowns that a
|
|
84
|
+
// higher-up has not yet approved. Raw `serviceType` enum values,
|
|
85
|
+
// de-duplicated; an empty cell means nothing is awaiting approval.
|
|
86
|
+
pendingServiceApprovals: Array.from(
|
|
87
|
+
new Set(
|
|
88
|
+
organization.pendingServiceApprovals.map((approval) => approval.serviceType)
|
|
89
|
+
)
|
|
90
|
+
).join("|")
|
|
63
91
|
}
|
|
64
92
|
]),
|
|
65
93
|
human: () => {
|
|
66
94
|
console.log(`${organization.name} (slug: ${organization.slug})`);
|
|
95
|
+
console.log(` contactUrl: ${organization.contactUrl ?? "(not set)"}`);
|
|
67
96
|
console.log(` subscriptionStatus: ${organization.subscriptionStatus}`);
|
|
68
97
|
console.log(` services: ${summarizeServices(organization)}`);
|
|
98
|
+
console.log(
|
|
99
|
+
` obligatoryAdminApproval: ${summarizeObligatoryAdminApproval(organization)}`
|
|
100
|
+
);
|
|
101
|
+
console.log(
|
|
102
|
+
` pendingServiceApprovals: ${summarizePendingApprovals(organization)}`
|
|
103
|
+
);
|
|
69
104
|
}
|
|
70
105
|
});
|
|
71
106
|
}
|
|
@@ -29,6 +29,7 @@ var SINGLE_SERVICES = [
|
|
|
29
29
|
"darkWebMonitoring"
|
|
30
30
|
];
|
|
31
31
|
var ALL_SERVICES = [...DUAL_SERVICES, ...SINGLE_SERVICES];
|
|
32
|
+
var PENDING_APPROVAL_SERVICES = ["protection", "takedowns"];
|
|
32
33
|
function splitCsv(value) {
|
|
33
34
|
if (!value) return [];
|
|
34
35
|
return value.split(",").map((part) => part.trim()).filter(Boolean);
|
|
@@ -128,13 +129,83 @@ function summarizeServices(org) {
|
|
|
128
129
|
}
|
|
129
130
|
return parts.length > 0 ? parts.join(" ") : "(no services enabled)";
|
|
130
131
|
}
|
|
132
|
+
function summarizeObligatoryAdminApproval(org) {
|
|
133
|
+
if (!org.obligatoryAdminApproval.active) return "off";
|
|
134
|
+
const types = org.obligatoryAdminApproval.assetTypes;
|
|
135
|
+
return types.length === 0 ? "on (all asset types)" : `on (${types.join(", ")})`;
|
|
136
|
+
}
|
|
137
|
+
function buildObligatoryAdminApprovalFilter(options) {
|
|
138
|
+
if (options.obligatoryApprovalActive && options.obligatoryApprovalInactive) {
|
|
139
|
+
throw new CliExitError(
|
|
140
|
+
"Conflicting flags: --obligatory-approval-active and --obligatory-approval-inactive can't both be passed.",
|
|
141
|
+
ExitCode.USAGE
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
const assetTypeStrings = splitCsv(options.obligatoryApprovalAssetType).map(
|
|
145
|
+
(value) => value.toUpperCase()
|
|
146
|
+
);
|
|
147
|
+
if (assetTypeStrings.length > 0 && options.obligatoryApprovalInactive) {
|
|
148
|
+
throw new CliExitError(
|
|
149
|
+
"Conflicting flags: --obligatory-approval-asset-type implies the feature is on, so it can't be combined with --obligatory-approval-inactive.",
|
|
150
|
+
ExitCode.USAGE
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
const filter = {};
|
|
154
|
+
if (options.obligatoryApprovalActive) filter.active = true;
|
|
155
|
+
if (options.obligatoryApprovalInactive) filter.active = false;
|
|
156
|
+
if (assetTypeStrings.length > 0) filter.assetTypes = assetTypeStrings;
|
|
157
|
+
return Object.keys(filter).length > 0 ? filter : void 0;
|
|
158
|
+
}
|
|
159
|
+
function parsePendingApprovalServices(raw) {
|
|
160
|
+
const parts = splitCsv(raw);
|
|
161
|
+
const result = [];
|
|
162
|
+
for (const part of parts) {
|
|
163
|
+
if (!PENDING_APPROVAL_SERVICES.includes(part)) {
|
|
164
|
+
throw new CliExitError(
|
|
165
|
+
`Unknown service '${part}' in --pending-approval-service. Only ${PENDING_APPROVAL_SERVICES.join(", ")} can require approval.`,
|
|
166
|
+
ExitCode.USAGE
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
result.push(part);
|
|
170
|
+
}
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
function buildPendingServiceApprovalFilter(options) {
|
|
174
|
+
if (options.pendingApprovalActive && options.pendingApprovalInactive) {
|
|
175
|
+
throw new CliExitError(
|
|
176
|
+
"Conflicting flags: --pending-approval-active and --pending-approval-inactive can't both be passed.",
|
|
177
|
+
ExitCode.USAGE
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
const services = parsePendingApprovalServices(options.pendingApprovalService);
|
|
181
|
+
if (services.length > 0 && options.pendingApprovalInactive) {
|
|
182
|
+
throw new CliExitError(
|
|
183
|
+
"Conflicting flags: --pending-approval-service implies a pending approval exists, so it can't be combined with --pending-approval-inactive.",
|
|
184
|
+
ExitCode.USAGE
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
const filter = {};
|
|
188
|
+
if (options.pendingApprovalActive) filter.active = true;
|
|
189
|
+
if (options.pendingApprovalInactive) filter.active = false;
|
|
190
|
+
if (services.length > 0) filter.services = services;
|
|
191
|
+
return Object.keys(filter).length > 0 ? filter : void 0;
|
|
192
|
+
}
|
|
193
|
+
function summarizePendingApprovals(org) {
|
|
194
|
+
if (org.pendingServiceApprovals.length === 0) return "none";
|
|
195
|
+
const labels = Array.from(
|
|
196
|
+
new Set(org.pendingServiceApprovals.map((approval) => approval.serviceName))
|
|
197
|
+
);
|
|
198
|
+
return labels.join(", ");
|
|
199
|
+
}
|
|
131
200
|
async function runOrgsList(options) {
|
|
132
201
|
const client = options.apiClient ?? createApiClient();
|
|
133
202
|
const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
|
|
134
203
|
const input = {
|
|
135
204
|
query: options.query ?? "",
|
|
136
205
|
subscriptionStatus: parseSubscriptionStatuses(options.subscriptionStatus),
|
|
137
|
-
services: buildServicesFilter(options)
|
|
206
|
+
services: buildServicesFilter(options),
|
|
207
|
+
obligatoryAdminApproval: buildObligatoryAdminApprovalFilter(options),
|
|
208
|
+
pendingServiceApproval: buildPendingServiceApprovalFilter(options)
|
|
138
209
|
};
|
|
139
210
|
const result = await client.getUserOrgs(input);
|
|
140
211
|
const organizations = result.organizations;
|
|
@@ -145,13 +216,14 @@ async function runOrgsList(options) {
|
|
|
145
216
|
`# Organizations (${organizations.length})`,
|
|
146
217
|
"",
|
|
147
218
|
...organizations.map(
|
|
148
|
-
(org) => `- \`${org.slug}\` \u2014 ${org.name} \u2014 ${org.subscriptionStatus} \u2014 ${summarizeServices(org)}`
|
|
219
|
+
(org) => `- \`${org.slug}\` \u2014 ${org.name} \u2014 ${org.subscriptionStatus} \u2014 contact: ${org.contactUrl ?? "(not set)"} \u2014 ${summarizeServices(org)} \u2014 admin-approval: ${summarizeObligatoryAdminApproval(org)} \u2014 pending-approval: ${summarizePendingApprovals(org)}`
|
|
149
220
|
)
|
|
150
221
|
].join("\n"),
|
|
151
222
|
csv: toCsvRows(
|
|
152
223
|
organizations.map((org) => ({
|
|
153
224
|
slug: org.slug,
|
|
154
225
|
name: org.name,
|
|
226
|
+
contactUrl: org.contactUrl ?? "",
|
|
155
227
|
subscriptionStatus: org.subscriptionStatus,
|
|
156
228
|
reportingActive: org.services.reporting.active,
|
|
157
229
|
reviewingActive: org.services.reviewing.active,
|
|
@@ -160,7 +232,19 @@ async function runOrgsList(options) {
|
|
|
160
232
|
// `takedowns` is the only service with a meaningful automation flag.
|
|
161
233
|
takedownsAutomated: org.services.takedowns.automated,
|
|
162
234
|
detectionActive: org.services.detection.active,
|
|
163
|
-
darkWebMonitoringActive: org.services.darkWebMonitoring.active
|
|
235
|
+
darkWebMonitoringActive: org.services.darkWebMonitoring.active,
|
|
236
|
+
obligatoryAdminApprovalActive: org.obligatoryAdminApproval.active,
|
|
237
|
+
// Empty list + active means "applies to all asset types"; we keep
|
|
238
|
+
// the cell empty in CSV so spreadsheets render it as the natural
|
|
239
|
+
// "no narrowing" value — pair it with the active column.
|
|
240
|
+
obligatoryAdminApprovalAssetTypes: org.obligatoryAdminApproval.assetTypes.join("|"),
|
|
241
|
+
// Pending staff requests to enable Wallet Blocking / Takedowns that
|
|
242
|
+
// a higher-up has not yet approved. We emit the raw `serviceType`
|
|
243
|
+
// enum values (de-duplicated) so the column is machine-filterable;
|
|
244
|
+
// an empty cell means nothing is awaiting approval.
|
|
245
|
+
pendingServiceApprovals: Array.from(
|
|
246
|
+
new Set(org.pendingServiceApprovals.map((approval) => approval.serviceType))
|
|
247
|
+
).join("|")
|
|
164
248
|
}))
|
|
165
249
|
),
|
|
166
250
|
human: () => {
|
|
@@ -172,8 +256,9 @@ async function runOrgsList(options) {
|
|
|
172
256
|
const slugWidth = Math.max(...organizations.map((org) => org.slug.length), 4);
|
|
173
257
|
const nameWidth = Math.max(...organizations.map((org) => org.name.length), 4);
|
|
174
258
|
for (const org of organizations) {
|
|
259
|
+
const contact = org.contactUrl ?? "(no contact)";
|
|
175
260
|
console.log(
|
|
176
|
-
` ${org.slug.padEnd(slugWidth)} ${org.name.padEnd(nameWidth)} ${org.subscriptionStatus.padEnd(11)} ${summarizeServices(org)}`
|
|
261
|
+
` ${org.slug.padEnd(slugWidth)} ${org.name.padEnd(nameWidth)} ${org.subscriptionStatus.padEnd(11)} ${contact} ${summarizeServices(org)} admin-approval=${summarizeObligatoryAdminApproval(org)} pending-approval=${summarizePendingApprovals(org)}`
|
|
177
262
|
);
|
|
178
263
|
}
|
|
179
264
|
}
|
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.22.0",
|
|
6
6
|
"license": "UNLICENSED",
|
|
7
7
|
"homepage": "https://chainpatrol.com/docs/cli",
|
|
8
8
|
"keywords": [
|