@chainpatrol/cli 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,94 @@
1
1
  # @chainpatrol/cli
2
2
 
3
+ ## 1.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 08b9976: Broaden the public API surface across Reports, Takedowns, Detections,
8
+ Detection Configs, and Assets. Everything additive — no removed fields,
9
+ no renames — so existing callers are unaffected.
10
+
11
+ **Detections (`listDetections`)** — new fields on each `DetectionListItem`:
12
+ `reason` (free-text explanation of why the detection fired), `score` (raw
13
+ 0–1 model score), `configId` (which `ThreatDetectionConfig` produced the
14
+ hit), `reportedAt` (when it transitioned to `REPORTED`, nullable), and
15
+ `brands[]` (brand associations).
16
+
17
+ **Detection Configs (`listDetectionConfigs`)** — each `ConfigEntry` now
18
+ carries `description` (nullable), `createdAt`, and `updatedAt` so
19
+ integrators can spot silent or stale configs.
20
+
21
+ **Reports** — `listOrganizationReports` gains `slaDueAt` (SLA deadline),
22
+ `externalSubmissionLink` (original external source URL when submitted from
23
+ a third-party surface), and `duplicateOfId` (canonical report ID when this
24
+ one is a duplicate). Type gaps are also closed: `imageDisplay`,
25
+ `favoritedAt`, `attachments[]`, and `proposals.asset.scans[]` were already
26
+ returned by the handler but omitted from the SDK type — they're now
27
+ declared. `searchReports` (`report/search-external`) response now
28
+ surfaces the `title` and `createdAt` fields the handler already fetched.
29
+
30
+ **Takedowns (`listTakedowns`)** — each `TakedownItem` gains `externalId`
31
+ (vendor's own reference — CleanDNS report ID / registrar ticket, for
32
+ reconciliation), `hasFilingDelay`, `hasLivenessCheckDelay` (staff-set
33
+ flags used by median-time metrics), and `asset.blockedAt`.
34
+
35
+ **Takedowns (`getTakedown`)** — the response was previously bare
36
+ (`{ assetContent, takedownStatus, createdAt, updatedAt }`). It's now
37
+ fully built out with `asset` details (id / content / type /
38
+ `livenessStatus` / `blockedAt`) and a nested `takedown` record (id /
39
+ status / `externalId` / delay flags / assignee / brand / tasks[]). The
40
+ original top-level fields (`takedownStatus`, `createdAt`, `updatedAt`)
41
+ are preserved for existing callers.
42
+
43
+ **Assets — `livenessStatus` + `blockedAt` parity.** `listAssets`,
44
+ `listOrganizationAssets`, `assetSearch` (`.asset`), and `listThreats`
45
+ (`livenessStatus` only, `blockedAt` already there) now consistently
46
+ return both fields. `listAssets` also fixes a bug where each returned
47
+ row's `id` was stripped from the response mapper (the column was already
48
+ selected). Note: `assetCheck` is intentionally deferred — its response
49
+ goes through `@chainpatrol/asset`'s own `checkAsset()` contract; parity
50
+ there needs a scoped follow-up in the asset package.
51
+
52
+ Server-side work in `@chainpatrol/external-trpc`, `@chainpatrol/core`,
53
+ `@chainpatrol/validation`, and `@chainpatrol/database` backs the new
54
+ fields; those are private packages and don't need version bumps. Also
55
+ adds a `/// @deprecated` doc-comment to `Report.threatLevel` in the
56
+ Prisma schema — the field is not surfaced through the public API and no
57
+ in-app UI reads it; the column stays for now, removal is a separate
58
+ destructive migration.
59
+
60
+ ### Patch Changes
61
+
62
+ - Updated dependencies [08b9976]
63
+ - @chainpatrol/sdk@1.3.0
64
+
65
+ ## 1.2.0
66
+
67
+ ### Minor Changes
68
+
69
+ - 3faffa0: Surface the sibling visibility knobs, brand-facing metadata, and portal
70
+ customization on the `/user/orgs` and `/user/orgs/{slug}` responses, on
71
+ `UserOrg` in the SDK, and in `chainpatrol orgs get` / `chainpatrol orgs list`
72
+ output (human, markdown, CSV, and JSON):
73
+
74
+ - `reportVisibility` (`PUBLIC` / `PRIVATE`) — sibling of `portalVisibility`.
75
+ - `metricsVisibility` (`PUBLIC` / `PRIVATE` / `DISABLED`) — sibling of the
76
+ above.
77
+ - `brandMetadata` — `{ websiteUrl, twitterHandle, securityContactEmail,
78
+ industry }`, all nullable.
79
+ - `portalCustomization` — `{ useCustomDetails, name, slug, avatarUrl,
80
+ bannerEnabled, bannerContent }` for the customer-configurable portal
81
+ presentation.
82
+
83
+ Server-side work in `@chainpatrol/external-trpc` and `@chainpatrol/validation`
84
+ supports the new fields; both are private packages and don't need version
85
+ bumps.
86
+
87
+ ### Patch Changes
88
+
89
+ - Updated dependencies [3faffa0]
90
+ - @chainpatrol/sdk@1.2.0
91
+
3
92
  ## 1.1.0
4
93
 
5
94
  ### Minor Changes
package/dist/cli.js CHANGED
@@ -1606,7 +1606,7 @@ async function main() {
1606
1606
  }
1607
1607
  case "orgs": {
1608
1608
  if (subcommand === "list") {
1609
- const { runOrgsList } = await import("./list-N2TGW7MP.js");
1609
+ const { runOrgsList } = await import("./list-BAPTZNTM.js");
1610
1610
  await runOrgsList({
1611
1611
  query: cli.flags.query,
1612
1612
  subscriptionStatus: cli.flags.subscriptionStatus,
@@ -1628,7 +1628,7 @@ async function main() {
1628
1628
  if (subcommand === "get") {
1629
1629
  const positionalSlug = action;
1630
1630
  const slug = positionalSlug ?? await tryResolveOrg();
1631
- const { runOrgsGet } = await import("./get-BZCAHKID.js");
1631
+ const { runOrgsGet } = await import("./get-SM5KGJVA.js");
1632
1632
  await runOrgsGet({
1633
1633
  slug,
1634
1634
  json: jsonMode,
@@ -56,6 +56,27 @@ function summarizeLegalDocuments(org) {
56
56
  const poa = org.legalDocuments.powerOfAttorney.present ? "yes" : "no";
57
57
  return `LOA=${loa} POA=${poa}`;
58
58
  }
59
+ function summarizeVisibility(org) {
60
+ return `portal=${org.portalVisibility} reports=${org.reportVisibility} metrics=${org.metricsVisibility}`;
61
+ }
62
+ function summarizeBrandMetadata(org) {
63
+ const { websiteUrl, twitterHandle, securityContactEmail, industry } = org.brandMetadata;
64
+ const parts = [];
65
+ if (industry) parts.push(`industry=${industry}`);
66
+ if (websiteUrl) parts.push(`website=${websiteUrl}`);
67
+ if (twitterHandle) parts.push(`twitter=${twitterHandle}`);
68
+ if (securityContactEmail) parts.push(`security-contact=${securityContactEmail}`);
69
+ return parts.length > 0 ? parts.join(" ") : "(none set)";
70
+ }
71
+ function summarizePortalCustomization(org) {
72
+ const c = org.portalCustomization;
73
+ const parts = [];
74
+ parts.push(c.useCustomDetails ? "custom-details=on" : "custom-details=off");
75
+ if (c.name) parts.push(`name=${c.name}`);
76
+ if (c.slug) parts.push(`slug=${c.slug}`);
77
+ parts.push(c.bannerEnabled ? "banner=on" : "banner=off");
78
+ return parts.join(" ");
79
+ }
59
80
  function summarizeTrademarks(org) {
60
81
  const count = org.trademarkRegistrations.length;
61
82
  if (count === 0) return "none";
@@ -82,7 +103,11 @@ async function runOrgsGet(options) {
82
103
  "",
83
104
  `- Contact URL: ${organization.contactUrl ?? "(not set)"}`,
84
105
  `- Subscription status: ${organization.subscriptionStatus}`,
85
- `- Portal visibility: ${organization.portalVisibility}`,
106
+ `- Visibility: ${summarizeVisibility(organization)}`,
107
+ `- Brand metadata: ${summarizeBrandMetadata(organization)}`,
108
+ `- Portal customization: ${summarizePortalCustomization(organization)}`,
109
+ organization.portalCustomization.avatarUrl ? ` - Custom avatar: ${organization.portalCustomization.avatarUrl}` : "",
110
+ organization.portalCustomization.bannerEnabled && organization.portalCustomization.bannerContent ? ` - Banner content: ${organization.portalCustomization.bannerContent}` : "",
86
111
  `- Services: ${summarizeServices(organization)}`,
87
112
  `- Integrations: ${summarizeIntegrations(organization)}`,
88
113
  `- Obligatory admin approval: ${summarizeObligatoryAdminApproval(organization)}`,
@@ -102,6 +127,18 @@ async function runOrgsGet(options) {
102
127
  contactUrl: organization.contactUrl ?? "",
103
128
  subscriptionStatus: organization.subscriptionStatus,
104
129
  portalVisibility: organization.portalVisibility,
130
+ reportVisibility: organization.reportVisibility,
131
+ metricsVisibility: organization.metricsVisibility,
132
+ industry: organization.brandMetadata.industry ?? "",
133
+ websiteUrl: organization.brandMetadata.websiteUrl ?? "",
134
+ twitterHandle: organization.brandMetadata.twitterHandle ?? "",
135
+ securityContactEmail: organization.brandMetadata.securityContactEmail ?? "",
136
+ portalUseCustomDetails: organization.portalCustomization.useCustomDetails,
137
+ portalName: organization.portalCustomization.name ?? "",
138
+ portalSlug: organization.portalCustomization.slug ?? "",
139
+ portalAvatarUrl: organization.portalCustomization.avatarUrl ?? "",
140
+ portalBannerEnabled: organization.portalCustomization.bannerEnabled,
141
+ portalBannerContent: organization.portalCustomization.bannerContent ?? "",
105
142
  reportingActive: organization.services.reporting.active,
106
143
  reviewingActive: organization.services.reviewing.active,
107
144
  protectionActive: organization.services.protection.active,
@@ -135,7 +172,17 @@ async function runOrgsGet(options) {
135
172
  console.log(`${organization.name} (slug: ${organization.slug})`);
136
173
  console.log(` contactUrl: ${organization.contactUrl ?? "(not set)"}`);
137
174
  console.log(` subscriptionStatus: ${organization.subscriptionStatus}`);
138
- console.log(` portalVisibility: ${organization.portalVisibility}`);
175
+ console.log(` visibility: ${summarizeVisibility(organization)}`);
176
+ console.log(` brandMetadata: ${summarizeBrandMetadata(organization)}`);
177
+ console.log(` portalCustomization: ${summarizePortalCustomization(organization)}`);
178
+ if (organization.portalCustomization.avatarUrl) {
179
+ console.log(` custom avatar: ${organization.portalCustomization.avatarUrl}`);
180
+ }
181
+ if (organization.portalCustomization.bannerEnabled && organization.portalCustomization.bannerContent) {
182
+ console.log(
183
+ ` banner content: ${organization.portalCustomization.bannerContent}`
184
+ );
185
+ }
139
186
  console.log(` services: ${summarizeServices(organization)}`);
140
187
  console.log(` integrations: ${summarizeIntegrations(organization)}`);
141
188
  console.log(
@@ -215,6 +215,9 @@ function summarizeLegalDocuments(org) {
215
215
  const poa = org.legalDocuments.powerOfAttorney.present ? "yes" : "no";
216
216
  return `LOA=${loa} POA=${poa}`;
217
217
  }
218
+ function summarizeVisibility(org) {
219
+ return `portal=${org.portalVisibility} reports=${org.reportVisibility} metrics=${org.metricsVisibility}`;
220
+ }
218
221
  function summarizeTrademarks(org) {
219
222
  const count = org.trademarkRegistrations.length;
220
223
  return count === 0 ? "0" : `${count}`;
@@ -238,7 +241,7 @@ async function runOrgsList(options) {
238
241
  `# Organizations (${organizations.length})`,
239
242
  "",
240
243
  ...organizations.map(
241
- (org) => `- \`${org.slug}\` \u2014 ${org.name} \u2014 ${org.subscriptionStatus} \u2014 portal: ${org.portalVisibility} \u2014 contact: ${org.contactUrl ?? "(not set)"} \u2014 ${summarizeServices(org)} \u2014 integrations: ${summarizeIntegrations(org)} \u2014 admin-approval: ${summarizeObligatoryAdminApproval(org)} \u2014 pending-approval: ${summarizePendingApprovals(org)} \u2014 legal: ${summarizeLegalDocuments(org)} \u2014 trademarks: ${summarizeTrademarks(org)}`
244
+ (org) => `- \`${org.slug}\` \u2014 ${org.name} \u2014 ${org.subscriptionStatus} \u2014 visibility: ${summarizeVisibility(org)} \u2014 contact: ${org.contactUrl ?? "(not set)"} \u2014 ${summarizeServices(org)} \u2014 integrations: ${summarizeIntegrations(org)} \u2014 admin-approval: ${summarizeObligatoryAdminApproval(org)} \u2014 pending-approval: ${summarizePendingApprovals(org)} \u2014 legal: ${summarizeLegalDocuments(org)} \u2014 trademarks: ${summarizeTrademarks(org)}`
242
245
  )
243
246
  ].join("\n"),
244
247
  csv: toCsvRows(
@@ -248,6 +251,18 @@ async function runOrgsList(options) {
248
251
  contactUrl: org.contactUrl ?? "",
249
252
  subscriptionStatus: org.subscriptionStatus,
250
253
  portalVisibility: org.portalVisibility,
254
+ reportVisibility: org.reportVisibility,
255
+ metricsVisibility: org.metricsVisibility,
256
+ industry: org.brandMetadata.industry ?? "",
257
+ websiteUrl: org.brandMetadata.websiteUrl ?? "",
258
+ twitterHandle: org.brandMetadata.twitterHandle ?? "",
259
+ securityContactEmail: org.brandMetadata.securityContactEmail ?? "",
260
+ portalUseCustomDetails: org.portalCustomization.useCustomDetails,
261
+ portalName: org.portalCustomization.name ?? "",
262
+ portalSlug: org.portalCustomization.slug ?? "",
263
+ portalAvatarUrl: org.portalCustomization.avatarUrl ?? "",
264
+ portalBannerEnabled: org.portalCustomization.bannerEnabled,
265
+ portalBannerContent: org.portalCustomization.bannerContent ?? "",
251
266
  reportingActive: org.services.reporting.active,
252
267
  reviewingActive: org.services.reviewing.active,
253
268
  protectionActive: org.services.protection.active,
@@ -286,7 +301,7 @@ async function runOrgsList(options) {
286
301
  for (const org of organizations) {
287
302
  const contact = org.contactUrl ?? "(no contact)";
288
303
  console.log(
289
- ` ${org.slug.padEnd(slugWidth)} ${org.name.padEnd(nameWidth)} ${org.subscriptionStatus.padEnd(11)} portal=${org.portalVisibility.padEnd(8)} ${contact} ${summarizeServices(org)} integrations=${summarizeIntegrations(org)} admin-approval=${summarizeObligatoryAdminApproval(org)} pending-approval=${summarizePendingApprovals(org)} legal=${summarizeLegalDocuments(org)} trademarks=${summarizeTrademarks(org)}`
304
+ ` ${org.slug.padEnd(slugWidth)} ${org.name.padEnd(nameWidth)} ${org.subscriptionStatus.padEnd(11)} visibility=${summarizeVisibility(org)} ${contact} ${summarizeServices(org)} integrations=${summarizeIntegrations(org)} admin-approval=${summarizeObligatoryAdminApproval(org)} pending-approval=${summarizePendingApprovals(org)} legal=${summarizeLegalDocuments(org)} trademarks=${summarizeTrademarks(org)}`
290
305
  );
291
306
  }
292
307
  }
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": "1.1.0",
5
+ "version": "1.3.0",
6
6
  "license": "UNLICENSED",
7
7
  "homepage": "https://chainpatrol.com/docs/cli",
8
8
  "keywords": [
@@ -33,7 +33,7 @@
33
33
  "lint": "npx oxlint ."
34
34
  },
35
35
  "dependencies": {
36
- "@chainpatrol/sdk": "1.1.0",
36
+ "@chainpatrol/sdk": "1.3.0",
37
37
  "ink": "^7.0.1",
38
38
  "meow": "^14.1.0",
39
39
  "open": "^11.0.0",