@chainpatrol/cli 0.21.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 +7 -0
- package/dist/{chunk-4N2YKTJ5.js → chunk-LKITFBIG.js} +81 -6
- package/dist/cli.js +17 -7
- package/dist/{get-XN752KSL.js → get-GTACVCLE.js} +24 -2
- package/dist/{list-MHICJ2TK.js → list-JK4SRMPL.js} +56 -4
- package/dist/{setup-skill-UEOROLCF.js → setup-skill-V3HDWN7T.js} +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
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
|
+
|
|
3
10
|
## 0.21.0
|
|
4
11
|
|
|
5
12
|
### Minor Changes
|
|
@@ -205,6 +205,12 @@ description: |
|
|
|
205
205
|
"orgs that require admin approval", "orgs requiring approval",
|
|
206
206
|
"which orgs require approval for twitter", "approval scope",
|
|
207
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",
|
|
208
214
|
"is this URL blocked", "is this domain blocked", "is this address blocked",
|
|
209
215
|
"check this asset", "asset check", "lookup asset status",
|
|
210
216
|
"what is ARCHIVE_ORG", "what does PAGE mean", "list asset types",
|
|
@@ -735,10 +741,48 @@ So per-org JSON looks like:
|
|
|
735
741
|
"detection": { "active": true },
|
|
736
742
|
"darkWebMonitoring": { "active": false }
|
|
737
743
|
},
|
|
738
|
-
"obligatoryAdminApproval": { "active": true, "assetTypes": ["TWITTER"] }
|
|
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
|
+
]
|
|
739
754
|
}
|
|
740
755
|
\`\`\`
|
|
741
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
|
+
|
|
742
786
|
When a user asks about "automated reporting / reviewing / protection",
|
|
743
787
|
explain that the flag exists in the DB but has no operational effect and
|
|
744
788
|
isn't exposed by the public API \u2014 only \`takedowns.automated\` carries a
|
|
@@ -767,6 +811,15 @@ Filter flags (all optional, all comma-separated lists where noted):
|
|
|
767
811
|
\`--obligatory-approval-inactive\`. Use the canonical asset-type enum
|
|
768
812
|
names (\`TWITTER\`, \`URL\`, \`PAGE\`, \u2026); run \`chainpatrol asset types\`
|
|
769
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\`.
|
|
770
823
|
|
|
771
824
|
Service names: \`reporting\`, \`reviewing\`, \`protection\`, \`takedowns\`,
|
|
772
825
|
\`detection\`, \`darkWebMonitoring\`.
|
|
@@ -794,17 +847,39 @@ chainpatrol --json orgs list --obligatory-approval-active \\
|
|
|
794
847
|
| jq '.organizations[] | select(.obligatoryAdminApproval.assetTypes | length == 0) | .slug'
|
|
795
848
|
\`\`\`
|
|
796
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
|
+
|
|
797
870
|
### \`orgs get\` \u2014 Get a single organization by slug
|
|
798
871
|
|
|
799
872
|
Look up one organization the caller has access to. Returns the same per-org
|
|
800
873
|
shape as a single row from \`orgs list\` \u2014 \`active\` for every service,
|
|
801
874
|
plus \`automated\` on \`takedowns\` only (see the note above on why other
|
|
802
875
|
services don't expose an automation flag), plus \`obligatoryAdminApproval\`
|
|
803
|
-
(\`active\` + \`assetTypes\`)
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
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.
|
|
808
883
|
|
|
809
884
|
\`\`\`bash
|
|
810
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": {
|
|
@@ -919,6 +923,9 @@ ${getTopLevelHelp()}
|
|
|
919
923
|
obligatoryApprovalActive: { type: "boolean" },
|
|
920
924
|
obligatoryApprovalInactive: { type: "boolean" },
|
|
921
925
|
obligatoryApprovalAssetType: { type: "string" },
|
|
926
|
+
pendingApprovalActive: { type: "boolean" },
|
|
927
|
+
pendingApprovalInactive: { type: "boolean" },
|
|
928
|
+
pendingApprovalService: { type: "string" },
|
|
922
929
|
query: { type: "string" },
|
|
923
930
|
assetType: { type: "string" },
|
|
924
931
|
takedownStatus: { type: "string" },
|
|
@@ -1561,7 +1568,7 @@ async function main() {
|
|
|
1561
1568
|
}
|
|
1562
1569
|
case "orgs": {
|
|
1563
1570
|
if (subcommand === "list") {
|
|
1564
|
-
const { runOrgsList } = await import("./list-
|
|
1571
|
+
const { runOrgsList } = await import("./list-JK4SRMPL.js");
|
|
1565
1572
|
await runOrgsList({
|
|
1566
1573
|
query: cli.flags.query,
|
|
1567
1574
|
subscriptionStatus: cli.flags.subscriptionStatus,
|
|
@@ -1572,6 +1579,9 @@ async function main() {
|
|
|
1572
1579
|
obligatoryApprovalActive: cli.flags.obligatoryApprovalActive,
|
|
1573
1580
|
obligatoryApprovalInactive: cli.flags.obligatoryApprovalInactive,
|
|
1574
1581
|
obligatoryApprovalAssetType: cli.flags.obligatoryApprovalAssetType,
|
|
1582
|
+
pendingApprovalActive: cli.flags.pendingApprovalActive,
|
|
1583
|
+
pendingApprovalInactive: cli.flags.pendingApprovalInactive,
|
|
1584
|
+
pendingApprovalService: cli.flags.pendingApprovalService,
|
|
1575
1585
|
json: jsonMode,
|
|
1576
1586
|
outputFormat: cliContext.outputFormat
|
|
1577
1587
|
});
|
|
@@ -1580,7 +1590,7 @@ async function main() {
|
|
|
1580
1590
|
if (subcommand === "get") {
|
|
1581
1591
|
const positionalSlug = action;
|
|
1582
1592
|
const slug = positionalSlug ?? await tryResolveOrg();
|
|
1583
|
-
const { runOrgsGet } = await import("./get-
|
|
1593
|
+
const { runOrgsGet } = await import("./get-GTACVCLE.js");
|
|
1584
1594
|
await runOrgsGet({
|
|
1585
1595
|
slug,
|
|
1586
1596
|
json: jsonMode,
|
|
@@ -1711,12 +1721,12 @@ async function main() {
|
|
|
1711
1721
|
case "setup":
|
|
1712
1722
|
case "install":
|
|
1713
1723
|
case "i": {
|
|
1714
|
-
const { setupSkill } = await import("./setup-skill-
|
|
1724
|
+
const { setupSkill } = await import("./setup-skill-V3HDWN7T.js");
|
|
1715
1725
|
setupSkill({ json: jsonMode, cloud: cli.flags.cloud });
|
|
1716
1726
|
break;
|
|
1717
1727
|
}
|
|
1718
1728
|
case "uninstall": {
|
|
1719
|
-
const { uninstallSkill } = await import("./setup-skill-
|
|
1729
|
+
const { uninstallSkill } = await import("./setup-skill-V3HDWN7T.js");
|
|
1720
1730
|
uninstallSkill({ json: jsonMode });
|
|
1721
1731
|
break;
|
|
1722
1732
|
}
|
|
@@ -31,6 +31,13 @@ function summarizeObligatoryAdminApproval(org) {
|
|
|
31
31
|
const types = org.obligatoryAdminApproval.assetTypes;
|
|
32
32
|
return types.length === 0 ? "on (all asset types)" : `on (${types.join(", ")})`;
|
|
33
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
|
+
}
|
|
34
41
|
async function runOrgsGet(options) {
|
|
35
42
|
const slug = options.slug?.trim();
|
|
36
43
|
if (!slug) {
|
|
@@ -49,14 +56,17 @@ async function runOrgsGet(options) {
|
|
|
49
56
|
markdown: [
|
|
50
57
|
`# ${organization.name} (\`${organization.slug}\`)`,
|
|
51
58
|
"",
|
|
59
|
+
`- Contact URL: ${organization.contactUrl ?? "(not set)"}`,
|
|
52
60
|
`- Subscription status: ${organization.subscriptionStatus}`,
|
|
53
61
|
`- Services: ${summarizeServices(organization)}`,
|
|
54
|
-
`- Obligatory admin approval: ${summarizeObligatoryAdminApproval(organization)}
|
|
62
|
+
`- Obligatory admin approval: ${summarizeObligatoryAdminApproval(organization)}`,
|
|
63
|
+
`- Pending service approvals: ${summarizePendingApprovals(organization)}`
|
|
55
64
|
].join("\n"),
|
|
56
65
|
csv: toCsvRows([
|
|
57
66
|
{
|
|
58
67
|
slug: organization.slug,
|
|
59
68
|
name: organization.name,
|
|
69
|
+
contactUrl: organization.contactUrl ?? "",
|
|
60
70
|
subscriptionStatus: organization.subscriptionStatus,
|
|
61
71
|
reportingActive: organization.services.reporting.active,
|
|
62
72
|
reviewingActive: organization.services.reviewing.active,
|
|
@@ -69,16 +79,28 @@ async function runOrgsGet(options) {
|
|
|
69
79
|
obligatoryAdminApprovalActive: organization.obligatoryAdminApproval.active,
|
|
70
80
|
// Empty list + active=true means "applies to all asset types" — read
|
|
71
81
|
// the active column alongside this one to interpret.
|
|
72
|
-
obligatoryAdminApprovalAssetTypes: organization.obligatoryAdminApproval.assetTypes.join("|")
|
|
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("|")
|
|
73
91
|
}
|
|
74
92
|
]),
|
|
75
93
|
human: () => {
|
|
76
94
|
console.log(`${organization.name} (slug: ${organization.slug})`);
|
|
95
|
+
console.log(` contactUrl: ${organization.contactUrl ?? "(not set)"}`);
|
|
77
96
|
console.log(` subscriptionStatus: ${organization.subscriptionStatus}`);
|
|
78
97
|
console.log(` services: ${summarizeServices(organization)}`);
|
|
79
98
|
console.log(
|
|
80
99
|
` obligatoryAdminApproval: ${summarizeObligatoryAdminApproval(organization)}`
|
|
81
100
|
);
|
|
101
|
+
console.log(
|
|
102
|
+
` pendingServiceApprovals: ${summarizePendingApprovals(organization)}`
|
|
103
|
+
);
|
|
82
104
|
}
|
|
83
105
|
});
|
|
84
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);
|
|
@@ -155,6 +156,47 @@ function buildObligatoryAdminApprovalFilter(options) {
|
|
|
155
156
|
if (assetTypeStrings.length > 0) filter.assetTypes = assetTypeStrings;
|
|
156
157
|
return Object.keys(filter).length > 0 ? filter : void 0;
|
|
157
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
|
+
}
|
|
158
200
|
async function runOrgsList(options) {
|
|
159
201
|
const client = options.apiClient ?? createApiClient();
|
|
160
202
|
const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
|
|
@@ -162,7 +204,8 @@ async function runOrgsList(options) {
|
|
|
162
204
|
query: options.query ?? "",
|
|
163
205
|
subscriptionStatus: parseSubscriptionStatuses(options.subscriptionStatus),
|
|
164
206
|
services: buildServicesFilter(options),
|
|
165
|
-
obligatoryAdminApproval: buildObligatoryAdminApprovalFilter(options)
|
|
207
|
+
obligatoryAdminApproval: buildObligatoryAdminApprovalFilter(options),
|
|
208
|
+
pendingServiceApproval: buildPendingServiceApprovalFilter(options)
|
|
166
209
|
};
|
|
167
210
|
const result = await client.getUserOrgs(input);
|
|
168
211
|
const organizations = result.organizations;
|
|
@@ -173,13 +216,14 @@ async function runOrgsList(options) {
|
|
|
173
216
|
`# Organizations (${organizations.length})`,
|
|
174
217
|
"",
|
|
175
218
|
...organizations.map(
|
|
176
|
-
(org) => `- \`${org.slug}\` \u2014 ${org.name} \u2014 ${org.subscriptionStatus} \u2014 ${summarizeServices(org)} \u2014 admin-approval: ${summarizeObligatoryAdminApproval(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)}`
|
|
177
220
|
)
|
|
178
221
|
].join("\n"),
|
|
179
222
|
csv: toCsvRows(
|
|
180
223
|
organizations.map((org) => ({
|
|
181
224
|
slug: org.slug,
|
|
182
225
|
name: org.name,
|
|
226
|
+
contactUrl: org.contactUrl ?? "",
|
|
183
227
|
subscriptionStatus: org.subscriptionStatus,
|
|
184
228
|
reportingActive: org.services.reporting.active,
|
|
185
229
|
reviewingActive: org.services.reviewing.active,
|
|
@@ -193,7 +237,14 @@ async function runOrgsList(options) {
|
|
|
193
237
|
// Empty list + active means "applies to all asset types"; we keep
|
|
194
238
|
// the cell empty in CSV so spreadsheets render it as the natural
|
|
195
239
|
// "no narrowing" value — pair it with the active column.
|
|
196
|
-
obligatoryAdminApprovalAssetTypes: org.obligatoryAdminApproval.assetTypes.join("|")
|
|
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("|")
|
|
197
248
|
}))
|
|
198
249
|
),
|
|
199
250
|
human: () => {
|
|
@@ -205,8 +256,9 @@ async function runOrgsList(options) {
|
|
|
205
256
|
const slugWidth = Math.max(...organizations.map((org) => org.slug.length), 4);
|
|
206
257
|
const nameWidth = Math.max(...organizations.map((org) => org.name.length), 4);
|
|
207
258
|
for (const org of organizations) {
|
|
259
|
+
const contact = org.contactUrl ?? "(no contact)";
|
|
208
260
|
console.log(
|
|
209
|
-
` ${org.slug.padEnd(slugWidth)} ${org.name.padEnd(nameWidth)} ${org.subscriptionStatus.padEnd(11)} ${summarizeServices(org)} admin-approval=${summarizeObligatoryAdminApproval(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)}`
|
|
210
262
|
);
|
|
211
263
|
}
|
|
212
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": [
|