@chainpatrol/cli 0.25.0 → 1.0.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,71 @@
1
1
  # @chainpatrol/cli
2
2
 
3
+ ## 1.0.0
4
+
5
+ ### Major Changes
6
+
7
+ - 2c03d10: Rename the `TRIAL` organization subscription status to `POC` everywhere, matching the term we use in our CRM. The product status, API response payloads, and CLI flags now say `POC` in place of `TRIAL`.
8
+
9
+ **Breaking changes for SDK and CLI consumers:**
10
+
11
+ - `/user/orgs` and `/user/orgs/{slug}` now return `"subscriptionStatus": "POC"` instead of `"TRIAL"`.
12
+ - `chainpatrol orgs list --subscription-status POC` replaces `--subscription-status TRIAL` (the alias `TRIAL` is no longer accepted).
13
+ - `UserOrgsSubscriptionStatus` SDK type is now `"PROSPECT" | "POC" | "ACTIVE" | "INTEGRATION"`.
14
+
15
+ The underlying database column value is unchanged (`"trial"` via Prisma `@map`), so no data migration is required.
16
+
17
+ ### Patch Changes
18
+
19
+ - Updated dependencies [2c03d10]
20
+ - @chainpatrol/sdk@1.0.0
21
+
22
+ ## 0.28.0
23
+
24
+ ### Minor Changes
25
+
26
+ - 5a1208e: Expose organization trademark registrations and legal documents (Letter of Authorization, Power of Attorney) on the API, SDK, and CLI so callers can answer questions like "which customers have their trademarks added?" from one place.
27
+
28
+ - `/user/orgs` and `/user/orgs/{slug}` now include `legalDocuments` (`{ letterOfAuthorization, powerOfAttorney }`, each `{ present, fileName, fileUrl }`) and `trademarkRegistrations` (full list with issuing office, registration number, page URL, and a `certificateFileUrl` link to the certificate PDF when uploaded).
29
+ - `/organization/brands` adds the same `legalDocuments` and `trademarkRegistrations` per brand, plus a `useOrganizationDocs` flag that signals brand-level fallback to org-level docs.
30
+ - `chainpatrol orgs list`, `chainpatrol orgs get`, and `chainpatrol brands list` print the new fields in human / markdown / CSV / JSON output, including the direct view links for the documents and certificates.
31
+
32
+ Backing work in `@chainpatrol/external-trpc` and `@chainpatrol/validation` adds the new Zod schemas and Kysely helpers; both are private packages and don't need version bumps.
33
+
34
+ ### Patch Changes
35
+
36
+ - Updated dependencies [5a1208e]
37
+ - @chainpatrol/sdk@0.12.0
38
+
39
+ ## 0.27.0
40
+
41
+ ### Minor Changes
42
+
43
+ - 1346c67: Expose brand configuration fields in the `/organization/brands` API endpoint.
44
+ The response now includes `brandColors`, `includedTerms`, `excludedTerms`,
45
+ `tickers`, `avatarUrl`, `websiteUrl`, and `twitterHandle` for each brand.
46
+ The CLI `brands list` command displays these fields in all output formats.
47
+
48
+ ### Patch Changes
49
+
50
+ - Updated dependencies [1346c67]
51
+ - @chainpatrol/sdk@0.11.0
52
+
53
+ ## 0.26.0
54
+
55
+ ### Minor Changes
56
+
57
+ - 4a3d9fb: Add integration connection status to orgs API and CLI. The `/user/orgs` and
58
+ `/user/orgs/{slug}` endpoints now return an `integrations` object for each
59
+ organization showing which third-party integrations are connected: Slack,
60
+ Discord, Telegram (with group count), Vercel, Intercom, and the Moderation API.
61
+ The CLI `orgs get` and `orgs list` commands display integration status in all
62
+ output formats.
63
+
64
+ ### Patch Changes
65
+
66
+ - Updated dependencies [4a3d9fb]
67
+ - @chainpatrol/sdk@0.10.0
68
+
3
69
  ## 0.25.0
4
70
 
5
71
  ### Minor Changes
package/dist/cli.js CHANGED
@@ -296,7 +296,7 @@ var HELP = {
296
296
  "--org <slug> Single org. Mutually exclusive with --slugs and --all-my-orgs.",
297
297
  "--slugs <list> Comma list of org slugs (cap 500). Response is the rollup across these orgs.",
298
298
  "--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.",
299
- "--subscription-status <list> With --all-my-orgs: only ACTIVE / TRIAL / PROSPECT / INTEGRATION (default: same set as `orgs list`).",
299
+ "--subscription-status <list> With --all-my-orgs: only ACTIVE / POC / PROSPECT / INTEGRATION (default: same set as `orgs list`).",
300
300
  "--service-active <list> With --all-my-orgs: only orgs with the listed service(s) active. Allowed: reporting, reviewing, protection, takedowns, detection, darkWebMonitoring.",
301
301
  "--brand-slug <slug> Filter by brand slug (requires brands-metrics flag; single-org only).",
302
302
  "--from <iso> Start date (ISO 8601, e.g. 2026-01-01)",
@@ -544,7 +544,7 @@ var HELP = {
544
544
  usage: "chainpatrol orgs list [filters]",
545
545
  options: [
546
546
  "--query <text> Partial name match (substring, case-insensitive)",
547
- "--subscription-status <l> Comma-separated list (PROSPECT,TRIAL,ACTIVE,INTEGRATION)",
547
+ "--subscription-status <l> Comma-separated list (PROSPECT,POC,ACTIVE,INTEGRATION)",
548
548
  "--service-active <l> Comma-separated services that must be active",
549
549
  "--service-inactive <l> Comma-separated services that must be inactive",
550
550
  "--service-automated <l> Services whose automation must be ON (only `takedowns` is accepted \u2014 the other services have no meaningful automation toggle)",
@@ -556,7 +556,7 @@ var HELP = {
556
556
  examples: [
557
557
  "chainpatrol orgs list --subscription-status ACTIVE",
558
558
  "chainpatrol orgs list --service-active takedowns --service-manual takedowns",
559
- "chainpatrol --json orgs list --subscription-status ACTIVE,TRIAL --service-active detection",
559
+ "chainpatrol --json orgs list --subscription-status ACTIVE,POC --service-active detection",
560
560
  "chainpatrol --json orgs list --pending-approval-service protection,takedowns"
561
561
  ]
562
562
  },
@@ -1243,7 +1243,7 @@ async function main() {
1243
1243
  break;
1244
1244
  }
1245
1245
  if (subcommand === "types") {
1246
- const { runAssetTypes } = await import("./types-M6QMFNUS.js");
1246
+ const { runAssetTypes } = await import("./types-KUPHV2V3.js");
1247
1247
  runAssetTypes({
1248
1248
  json: jsonMode,
1249
1249
  outputFormat: cliContext.outputFormat
@@ -1606,7 +1606,7 @@ async function main() {
1606
1606
  }
1607
1607
  case "orgs": {
1608
1608
  if (subcommand === "list") {
1609
- const { runOrgsList } = await import("./list-T2YIGGM6.js");
1609
+ const { runOrgsList } = await import("./list-DNRSTJ63.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-4YHQ7ZE2.js");
1631
+ const { runOrgsGet } = await import("./get-AQG3TLAK.js");
1632
1632
  await runOrgsGet({
1633
1633
  slug,
1634
1634
  json: jsonMode,
@@ -1676,7 +1676,7 @@ async function main() {
1676
1676
  "brands list --type must be ORGANIZATION or INDIVIDUAL (case-insensitive)."
1677
1677
  );
1678
1678
  }
1679
- const { runBrandsList } = await import("./list-33ILIIXB.js");
1679
+ const { runBrandsList } = await import("./list-4YBWU2JZ.js");
1680
1680
  await runBrandsList({
1681
1681
  type: rawType,
1682
1682
  json: jsonMode,
@@ -0,0 +1,168 @@
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-OFUIYMPD.js";
12
+ import "./chunk-EGWK6SRQ.js";
13
+ import "./chunk-U73SABXK.js";
14
+
15
+ // src/commands/orgs/get.ts
16
+ function summarizeServices(org) {
17
+ const parts = [];
18
+ if (org.services.takedowns.active) {
19
+ parts.push(`takedowns=${org.services.takedowns.automated ? "auto" : "manual"}`);
20
+ }
21
+ if (org.services.reporting.active) parts.push("reporting");
22
+ if (org.services.reviewing.active) parts.push("reviewing");
23
+ if (org.services.protection.active) parts.push("protection");
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
+ function summarizeObligatoryAdminApproval(org) {
29
+ if (!org.obligatoryAdminApproval.active) return "off";
30
+ const types = org.obligatoryAdminApproval.assetTypes;
31
+ return types.length === 0 ? "on (all asset types)" : `on (${types.join(", ")})`;
32
+ }
33
+ function summarizePendingApprovals(org) {
34
+ if (org.pendingServiceApprovals.length === 0) return "none";
35
+ const labels = Array.from(
36
+ new Set(org.pendingServiceApprovals.map((approval) => approval.serviceName))
37
+ );
38
+ return labels.join(", ");
39
+ }
40
+ function summarizeIntegrations(org) {
41
+ const parts = [];
42
+ if (org.integrations.slack) parts.push("slack");
43
+ if (org.integrations.discord) parts.push("discord");
44
+ if (org.integrations.telegram.connected) {
45
+ parts.push(
46
+ org.integrations.telegram.groupCount === 1 ? "telegram" : `telegram(${org.integrations.telegram.groupCount} groups)`
47
+ );
48
+ }
49
+ if (org.integrations.vercel) parts.push("vercel");
50
+ if (org.integrations.intercom) parts.push("intercom");
51
+ if (org.integrations.moderation) parts.push("moderation-api");
52
+ return parts.length > 0 ? parts.join(" ") : "(none)";
53
+ }
54
+ function summarizeLegalDocuments(org) {
55
+ const loa = org.legalDocuments.letterOfAuthorization.present ? "yes" : "no";
56
+ const poa = org.legalDocuments.powerOfAttorney.present ? "yes" : "no";
57
+ return `LOA=${loa} POA=${poa}`;
58
+ }
59
+ function summarizeTrademarks(org) {
60
+ const count = org.trademarkRegistrations.length;
61
+ if (count === 0) return "none";
62
+ const labels = org.trademarkRegistrations.slice(0, 3).map((tm) => `${tm.brandName} (${tm.trademarkOffice} #${tm.registrationNumber})`);
63
+ return count > labels.length ? `${count} on file \u2014 ${labels.join(", ")}, +${count - labels.length} more` : `${count} on file \u2014 ${labels.join(", ")}`;
64
+ }
65
+ async function runOrgsGet(options) {
66
+ const slug = options.slug?.trim();
67
+ if (!slug) {
68
+ throw new CliExitError(
69
+ "orgs get requires an organization slug. Example: chainpatrol orgs get acme",
70
+ ExitCode.USAGE
71
+ );
72
+ }
73
+ const client = options.apiClient ?? createApiClient();
74
+ const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
75
+ const result = await client.getUserOrg({ slug });
76
+ const { organization } = result;
77
+ printOutput({
78
+ outputFormat,
79
+ json: result,
80
+ markdown: [
81
+ `# ${organization.name} (\`${organization.slug}\`)`,
82
+ "",
83
+ `- Contact URL: ${organization.contactUrl ?? "(not set)"}`,
84
+ `- Subscription status: ${organization.subscriptionStatus}`,
85
+ `- Services: ${summarizeServices(organization)}`,
86
+ `- Integrations: ${summarizeIntegrations(organization)}`,
87
+ `- Obligatory admin approval: ${summarizeObligatoryAdminApproval(organization)}`,
88
+ `- Pending service approvals: ${summarizePendingApprovals(organization)}`,
89
+ `- Legal documents: ${summarizeLegalDocuments(organization)}`,
90
+ organization.legalDocuments.letterOfAuthorization.fileUrl ? ` - LOA: ${organization.legalDocuments.letterOfAuthorization.fileUrl}` : "",
91
+ organization.legalDocuments.powerOfAttorney.fileUrl ? ` - POA: ${organization.legalDocuments.powerOfAttorney.fileUrl}` : "",
92
+ `- Trademarks: ${summarizeTrademarks(organization)}`,
93
+ ...organization.trademarkRegistrations.map(
94
+ (tm) => ` - #${tm.id} ${tm.brandName} \u2014 ${tm.trademarkOffice} #${tm.registrationNumber}` + (tm.registrationPageUrl ? ` \u2014 page: ${tm.registrationPageUrl}` : "") + (tm.certificateFileUrl ? ` \u2014 certificate: ${tm.certificateFileUrl}` : "")
95
+ )
96
+ ].filter((line) => line !== "").join("\n"),
97
+ csv: toCsvRows([
98
+ {
99
+ slug: organization.slug,
100
+ name: organization.name,
101
+ contactUrl: organization.contactUrl ?? "",
102
+ subscriptionStatus: organization.subscriptionStatus,
103
+ reportingActive: organization.services.reporting.active,
104
+ reviewingActive: organization.services.reviewing.active,
105
+ protectionActive: organization.services.protection.active,
106
+ takedownsActive: organization.services.takedowns.active,
107
+ takedownsAutomated: organization.services.takedowns.automated,
108
+ detectionActive: organization.services.detection.active,
109
+ darkWebMonitoringActive: organization.services.darkWebMonitoring.active,
110
+ slackConnected: organization.integrations.slack,
111
+ discordConnected: organization.integrations.discord,
112
+ telegramConnected: organization.integrations.telegram.connected,
113
+ telegramGroupCount: organization.integrations.telegram.groupCount,
114
+ vercelConnected: organization.integrations.vercel,
115
+ intercomConnected: organization.integrations.intercom,
116
+ moderationApiConnected: organization.integrations.moderation,
117
+ obligatoryAdminApprovalActive: organization.obligatoryAdminApproval.active,
118
+ obligatoryAdminApprovalAssetTypes: organization.obligatoryAdminApproval.assetTypes.join("|"),
119
+ pendingServiceApprovals: Array.from(
120
+ new Set(
121
+ organization.pendingServiceApprovals.map((approval) => approval.serviceType)
122
+ )
123
+ ).join("|"),
124
+ letterOfAuthorizationPresent: organization.legalDocuments.letterOfAuthorization.present,
125
+ letterOfAuthorizationFileUrl: organization.legalDocuments.letterOfAuthorization.fileUrl ?? "",
126
+ powerOfAttorneyPresent: organization.legalDocuments.powerOfAttorney.present,
127
+ powerOfAttorneyFileUrl: organization.legalDocuments.powerOfAttorney.fileUrl ?? "",
128
+ trademarkRegistrationCount: organization.trademarkRegistrations.length,
129
+ trademarkBrandNames: organization.trademarkRegistrations.map((tm) => tm.brandName).join("|")
130
+ }
131
+ ]),
132
+ human: () => {
133
+ console.log(`${organization.name} (slug: ${organization.slug})`);
134
+ console.log(` contactUrl: ${organization.contactUrl ?? "(not set)"}`);
135
+ console.log(` subscriptionStatus: ${organization.subscriptionStatus}`);
136
+ console.log(` services: ${summarizeServices(organization)}`);
137
+ console.log(` integrations: ${summarizeIntegrations(organization)}`);
138
+ console.log(
139
+ ` obligatoryAdminApproval: ${summarizeObligatoryAdminApproval(organization)}`
140
+ );
141
+ console.log(
142
+ ` pendingServiceApprovals: ${summarizePendingApprovals(organization)}`
143
+ );
144
+ console.log(` legalDocuments: ${summarizeLegalDocuments(organization)}`);
145
+ if (organization.legalDocuments.letterOfAuthorization.fileUrl) {
146
+ console.log(
147
+ ` LOA: ${organization.legalDocuments.letterOfAuthorization.fileUrl}`
148
+ );
149
+ }
150
+ if (organization.legalDocuments.powerOfAttorney.fileUrl) {
151
+ console.log(` POA: ${organization.legalDocuments.powerOfAttorney.fileUrl}`);
152
+ }
153
+ console.log(` trademarks: ${summarizeTrademarks(organization)}`);
154
+ for (const tm of organization.trademarkRegistrations) {
155
+ const extras = [];
156
+ if (tm.registrationPageUrl) extras.push(`page=${tm.registrationPageUrl}`);
157
+ if (tm.certificateFileUrl) extras.push(`certificate=${tm.certificateFileUrl}`);
158
+ const extraStr = extras.length > 0 ? ` (${extras.join(", ")})` : "";
159
+ console.log(
160
+ ` #${tm.id} ${tm.brandName} \u2014 ${tm.trademarkOffice} #${tm.registrationNumber}${extraStr}`
161
+ );
162
+ }
163
+ }
164
+ });
165
+ }
166
+ export {
167
+ runOrgsGet
168
+ };
@@ -0,0 +1,137 @@
1
+ import {
2
+ printOutput,
3
+ toCsvRows
4
+ } from "./chunk-VFT3TD3E.js";
5
+ import {
6
+ createApiClient
7
+ } from "./chunk-OFUIYMPD.js";
8
+ import "./chunk-EGWK6SRQ.js";
9
+ import "./chunk-U73SABXK.js";
10
+
11
+ // src/commands/brands/list.ts
12
+ function summarizeLegalDocs(brand) {
13
+ const loa = brand.legalDocuments.letterOfAuthorization.present ? "yes" : "no";
14
+ const poa = brand.legalDocuments.powerOfAttorney.present ? "yes" : "no";
15
+ const inheritance = brand.useOrganizationDocs ? " (inherits org docs)" : "";
16
+ return `LOA=${loa} POA=${poa}${inheritance}`;
17
+ }
18
+ function toRow(brand) {
19
+ return {
20
+ id: brand.id,
21
+ slug: brand.slug,
22
+ name: brand.name,
23
+ type: brand.type,
24
+ description: brand.description ?? "",
25
+ avatarUrl: brand.avatarUrl ?? "",
26
+ websiteUrl: brand.websiteUrl ?? "",
27
+ twitterHandle: brand.twitterHandle ?? "",
28
+ brandColors: brand.brandColors.join(", "),
29
+ includedTerms: brand.includedTerms.join(", "),
30
+ excludedTerms: brand.excludedTerms.join(", "),
31
+ tickers: brand.tickers.join(", "),
32
+ brandGroupId: brand.brandGroupId,
33
+ createdAt: brand.createdAt,
34
+ useOrganizationDocs: brand.useOrganizationDocs,
35
+ letterOfAuthorizationPresent: brand.legalDocuments.letterOfAuthorization.present,
36
+ letterOfAuthorizationFileUrl: brand.legalDocuments.letterOfAuthorization.fileUrl ?? "",
37
+ powerOfAttorneyPresent: brand.legalDocuments.powerOfAttorney.present,
38
+ powerOfAttorneyFileUrl: brand.legalDocuments.powerOfAttorney.fileUrl ?? "",
39
+ trademarkRegistrationCount: brand.trademarkRegistrations.length,
40
+ trademarkBrandNames: brand.trademarkRegistrations.map((tm) => tm.brandName).join("|")
41
+ };
42
+ }
43
+ async function runBrandsList(options) {
44
+ const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
45
+ const client = options.apiClient ?? createApiClient();
46
+ const result = await client.listOrganizationBrands();
47
+ const brands = options.type ? result.brands.filter((brand) => brand.type === options.type) : result.brands;
48
+ const rows = brands.map(toRow);
49
+ printOutput({
50
+ outputFormat,
51
+ json: options.type ? { ...result, brands } : result,
52
+ markdown: [
53
+ "# Organization brands",
54
+ "",
55
+ `- Returned: ${brands.length}${options.type ? ` (filtered to type=${options.type})` : ""}`,
56
+ "",
57
+ ...brands.flatMap((brand) => {
58
+ const parts = [`slug=${brand.slug}`, `type=${brand.type}`];
59
+ if (brand.brandGroupId !== null) parts.push(`groupId=${brand.brandGroupId}`);
60
+ if (brand.websiteUrl) parts.push(`website=${brand.websiteUrl}`);
61
+ if (brand.twitterHandle) parts.push(`twitter=@${brand.twitterHandle}`);
62
+ if (brand.brandColors.length > 0)
63
+ parts.push(`colors=${brand.brandColors.join(", ")}`);
64
+ if (brand.includedTerms.length > 0)
65
+ parts.push(`included=${brand.includedTerms.join(", ")}`);
66
+ if (brand.excludedTerms.length > 0)
67
+ parts.push(`excluded=${brand.excludedTerms.join(", ")}`);
68
+ if (brand.tickers.length > 0) parts.push(`tickers=${brand.tickers.join(", ")}`);
69
+ parts.push(`legal=${summarizeLegalDocs(brand)}`);
70
+ parts.push(`trademarks=${brand.trademarkRegistrations.length}`);
71
+ const lines = [`- #${brand.id} ${brand.name} (${parts.join(", ")})`];
72
+ if (brand.legalDocuments.letterOfAuthorization.fileUrl) {
73
+ lines.push(` - LOA: ${brand.legalDocuments.letterOfAuthorization.fileUrl}`);
74
+ }
75
+ if (brand.legalDocuments.powerOfAttorney.fileUrl) {
76
+ lines.push(` - POA: ${brand.legalDocuments.powerOfAttorney.fileUrl}`);
77
+ }
78
+ for (const tm of brand.trademarkRegistrations) {
79
+ const extras = [];
80
+ if (tm.registrationPageUrl) extras.push(`page: ${tm.registrationPageUrl}`);
81
+ if (tm.certificateFileUrl) extras.push(`certificate: ${tm.certificateFileUrl}`);
82
+ const extraStr = extras.length > 0 ? ` \u2014 ${extras.join(", ")}` : "";
83
+ lines.push(
84
+ ` - trademark #${tm.id} ${tm.brandName} \u2014 ${tm.trademarkOffice} #${tm.registrationNumber}${extraStr}`
85
+ );
86
+ }
87
+ return lines;
88
+ })
89
+ ].join("\n"),
90
+ csv: toCsvRows(rows),
91
+ human: () => {
92
+ if (brands.length === 0) {
93
+ console.log(
94
+ options.type ? `No brands of type ${options.type} found.` : "No brands found."
95
+ );
96
+ return;
97
+ }
98
+ console.log(
99
+ `Organization brands \u2014 returned ${brands.length}${options.type ? ` (type=${options.type})` : ""}`
100
+ );
101
+ for (const brand of brands) {
102
+ const parts = [`slug=${brand.slug}`, `type=${brand.type}`];
103
+ if (brand.brandGroupId !== null) parts.push(`groupId=${brand.brandGroupId}`);
104
+ if (brand.websiteUrl) parts.push(`website=${brand.websiteUrl}`);
105
+ if (brand.twitterHandle) parts.push(`twitter=@${brand.twitterHandle}`);
106
+ if (brand.brandColors.length > 0)
107
+ parts.push(`colors=${brand.brandColors.join(", ")}`);
108
+ if (brand.includedTerms.length > 0)
109
+ parts.push(`included=${brand.includedTerms.join(", ")}`);
110
+ if (brand.excludedTerms.length > 0)
111
+ parts.push(`excluded=${brand.excludedTerms.join(", ")}`);
112
+ if (brand.tickers.length > 0) parts.push(`tickers=${brand.tickers.join(", ")}`);
113
+ parts.push(`legal=${summarizeLegalDocs(brand)}`);
114
+ parts.push(`trademarks=${brand.trademarkRegistrations.length}`);
115
+ console.log(`#${brand.id} ${brand.name} (${parts.join(", ")})`);
116
+ if (brand.legalDocuments.letterOfAuthorization.fileUrl) {
117
+ console.log(` LOA: ${brand.legalDocuments.letterOfAuthorization.fileUrl}`);
118
+ }
119
+ if (brand.legalDocuments.powerOfAttorney.fileUrl) {
120
+ console.log(` POA: ${brand.legalDocuments.powerOfAttorney.fileUrl}`);
121
+ }
122
+ for (const tm of brand.trademarkRegistrations) {
123
+ const extras = [];
124
+ if (tm.registrationPageUrl) extras.push(`page=${tm.registrationPageUrl}`);
125
+ if (tm.certificateFileUrl) extras.push(`certificate=${tm.certificateFileUrl}`);
126
+ const extraStr = extras.length > 0 ? ` (${extras.join(", ")})` : "";
127
+ console.log(
128
+ ` trademark #${tm.id} ${tm.brandName} \u2014 ${tm.trademarkOffice} #${tm.registrationNumber}${extraStr}`
129
+ );
130
+ }
131
+ }
132
+ }
133
+ });
134
+ }
135
+ export {
136
+ runBrandsList
137
+ };
@@ -15,7 +15,7 @@ import "./chunk-U73SABXK.js";
15
15
  // src/commands/orgs/list.ts
16
16
  var VALID_SUBSCRIPTION_STATUSES = /* @__PURE__ */ new Set([
17
17
  "PROSPECT",
18
- "TRIAL",
18
+ "POC",
19
19
  "ACTIVE",
20
20
  "INTEGRATION"
21
21
  ]);
@@ -196,6 +196,29 @@ function summarizePendingApprovals(org) {
196
196
  );
197
197
  return labels.join(", ");
198
198
  }
199
+ function summarizeIntegrations(org) {
200
+ const parts = [];
201
+ if (org.integrations.slack) parts.push("slack");
202
+ if (org.integrations.discord) parts.push("discord");
203
+ if (org.integrations.telegram.connected) {
204
+ parts.push(
205
+ org.integrations.telegram.groupCount === 1 ? "telegram" : `telegram(${org.integrations.telegram.groupCount} groups)`
206
+ );
207
+ }
208
+ if (org.integrations.vercel) parts.push("vercel");
209
+ if (org.integrations.intercom) parts.push("intercom");
210
+ if (org.integrations.moderation) parts.push("moderation-api");
211
+ return parts.length > 0 ? parts.join(" ") : "(none)";
212
+ }
213
+ function summarizeLegalDocuments(org) {
214
+ const loa = org.legalDocuments.letterOfAuthorization.present ? "yes" : "no";
215
+ const poa = org.legalDocuments.powerOfAttorney.present ? "yes" : "no";
216
+ return `LOA=${loa} POA=${poa}`;
217
+ }
218
+ function summarizeTrademarks(org) {
219
+ const count = org.trademarkRegistrations.length;
220
+ return count === 0 ? "0" : `${count}`;
221
+ }
199
222
  async function runOrgsList(options) {
200
223
  const client = options.apiClient ?? createApiClient();
201
224
  const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
@@ -215,7 +238,7 @@ async function runOrgsList(options) {
215
238
  `# Organizations (${organizations.length})`,
216
239
  "",
217
240
  ...organizations.map(
218
- (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)}`
241
+ (org) => `- \`${org.slug}\` \u2014 ${org.name} \u2014 ${org.subscriptionStatus} \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)}`
219
242
  )
220
243
  ].join("\n"),
221
244
  csv: toCsvRows(
@@ -228,22 +251,27 @@ async function runOrgsList(options) {
228
251
  reviewingActive: org.services.reviewing.active,
229
252
  protectionActive: org.services.protection.active,
230
253
  takedownsActive: org.services.takedowns.active,
231
- // `takedowns` is the only service with a meaningful automation flag.
232
254
  takedownsAutomated: org.services.takedowns.automated,
233
255
  detectionActive: org.services.detection.active,
234
256
  darkWebMonitoringActive: org.services.darkWebMonitoring.active,
257
+ slackConnected: org.integrations.slack,
258
+ discordConnected: org.integrations.discord,
259
+ telegramConnected: org.integrations.telegram.connected,
260
+ telegramGroupCount: org.integrations.telegram.groupCount,
261
+ vercelConnected: org.integrations.vercel,
262
+ intercomConnected: org.integrations.intercom,
263
+ moderationApiConnected: org.integrations.moderation,
235
264
  obligatoryAdminApprovalActive: org.obligatoryAdminApproval.active,
236
- // Empty list + active means "applies to all asset types"; we keep
237
- // the cell empty in CSV so spreadsheets render it as the natural
238
- // "no narrowing" value — pair it with the active column.
239
265
  obligatoryAdminApprovalAssetTypes: org.obligatoryAdminApproval.assetTypes.join("|"),
240
- // Pending staff requests to enable Wallet Blocking / Takedowns that
241
- // a higher-up has not yet approved. We emit the raw `serviceType`
242
- // enum values (de-duplicated) so the column is machine-filterable;
243
- // an empty cell means nothing is awaiting approval.
244
266
  pendingServiceApprovals: Array.from(
245
267
  new Set(org.pendingServiceApprovals.map((approval) => approval.serviceType))
246
- ).join("|")
268
+ ).join("|"),
269
+ letterOfAuthorizationPresent: org.legalDocuments.letterOfAuthorization.present,
270
+ letterOfAuthorizationFileUrl: org.legalDocuments.letterOfAuthorization.fileUrl ?? "",
271
+ powerOfAttorneyPresent: org.legalDocuments.powerOfAttorney.present,
272
+ powerOfAttorneyFileUrl: org.legalDocuments.powerOfAttorney.fileUrl ?? "",
273
+ trademarkRegistrationCount: org.trademarkRegistrations.length,
274
+ trademarkBrandNames: org.trademarkRegistrations.map((tm) => tm.brandName).join("|")
247
275
  }))
248
276
  ),
249
277
  human: () => {
@@ -257,7 +285,7 @@ async function runOrgsList(options) {
257
285
  for (const org of organizations) {
258
286
  const contact = org.contactUrl ?? "(no contact)";
259
287
  console.log(
260
- ` ${org.slug.padEnd(slugWidth)} ${org.name.padEnd(nameWidth)} ${org.subscriptionStatus.padEnd(11)} ${contact} ${summarizeServices(org)} admin-approval=${summarizeObligatoryAdminApproval(org)} pending-approval=${summarizePendingApprovals(org)}`
288
+ ` ${org.slug.padEnd(slugWidth)} ${org.name.padEnd(nameWidth)} ${org.subscriptionStatus.padEnd(11)} ${contact} ${summarizeServices(org)} integrations=${summarizeIntegrations(org)} admin-approval=${summarizeObligatoryAdminApproval(org)} pending-approval=${summarizePendingApprovals(org)} legal=${summarizeLegalDocuments(org)} trademarks=${summarizeTrademarks(org)}`
261
289
  );
262
290
  }
263
291
  }
@@ -218,7 +218,12 @@ var ASSET_TYPES = [
218
218
  label: "500px",
219
219
  description: "A 500px photographer profile."
220
220
  },
221
- { type: "LUMA", label: "Luma", description: "A Luma page or profile." }
221
+ { type: "LUMA", label: "Luma", description: "A Luma page or profile." },
222
+ {
223
+ type: "SMARTMONEYMATCH",
224
+ label: "SmartMoneyMatch",
225
+ description: "A SmartMoneyMatch article or event page."
226
+ }
222
227
  ];
223
228
  var ASSET_TYPE_LABELS = Object.freeze(
224
229
  Object.fromEntries(ASSET_TYPES.map((entry) => [entry.type, entry.label]))
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.25.0",
5
+ "version": "1.0.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": "0.9.0",
36
+ "@chainpatrol/sdk": "1.0.0",
37
37
  "ink": "^7.0.1",
38
38
  "meow": "^14.1.0",
39
39
  "open": "^11.0.0",
@@ -1,108 +0,0 @@
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-OFUIYMPD.js";
12
- import "./chunk-EGWK6SRQ.js";
13
- import "./chunk-U73SABXK.js";
14
-
15
- // src/commands/orgs/get.ts
16
- function summarizeServices(org) {
17
- const parts = [];
18
- if (org.services.takedowns.active) {
19
- parts.push(`takedowns=${org.services.takedowns.automated ? "auto" : "manual"}`);
20
- }
21
- if (org.services.reporting.active) parts.push("reporting");
22
- if (org.services.reviewing.active) parts.push("reviewing");
23
- if (org.services.protection.active) parts.push("protection");
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
- function summarizeObligatoryAdminApproval(org) {
29
- if (!org.obligatoryAdminApproval.active) return "off";
30
- const types = org.obligatoryAdminApproval.assetTypes;
31
- return types.length === 0 ? "on (all asset types)" : `on (${types.join(", ")})`;
32
- }
33
- function summarizePendingApprovals(org) {
34
- if (org.pendingServiceApprovals.length === 0) return "none";
35
- const labels = Array.from(
36
- new Set(org.pendingServiceApprovals.map((approval) => approval.serviceName))
37
- );
38
- return labels.join(", ");
39
- }
40
- async function runOrgsGet(options) {
41
- const slug = options.slug?.trim();
42
- if (!slug) {
43
- throw new CliExitError(
44
- "orgs get requires an organization slug. Example: chainpatrol orgs get acme",
45
- ExitCode.USAGE
46
- );
47
- }
48
- const client = options.apiClient ?? createApiClient();
49
- const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
50
- const result = await client.getUserOrg({ slug });
51
- const { organization } = result;
52
- printOutput({
53
- outputFormat,
54
- json: result,
55
- markdown: [
56
- `# ${organization.name} (\`${organization.slug}\`)`,
57
- "",
58
- `- Contact URL: ${organization.contactUrl ?? "(not set)"}`,
59
- `- Subscription status: ${organization.subscriptionStatus}`,
60
- `- Services: ${summarizeServices(organization)}`,
61
- `- Obligatory admin approval: ${summarizeObligatoryAdminApproval(organization)}`,
62
- `- Pending service approvals: ${summarizePendingApprovals(organization)}`
63
- ].join("\n"),
64
- csv: toCsvRows([
65
- {
66
- slug: organization.slug,
67
- name: organization.name,
68
- contactUrl: organization.contactUrl ?? "",
69
- subscriptionStatus: organization.subscriptionStatus,
70
- reportingActive: organization.services.reporting.active,
71
- reviewingActive: organization.services.reviewing.active,
72
- protectionActive: organization.services.protection.active,
73
- takedownsActive: organization.services.takedowns.active,
74
- // `takedowns` is the only service with a meaningful automation flag.
75
- takedownsAutomated: organization.services.takedowns.automated,
76
- detectionActive: organization.services.detection.active,
77
- darkWebMonitoringActive: organization.services.darkWebMonitoring.active,
78
- obligatoryAdminApprovalActive: organization.obligatoryAdminApproval.active,
79
- // Empty list + active=true means "applies to all asset types" — read
80
- // the active column alongside this one to interpret.
81
- obligatoryAdminApprovalAssetTypes: organization.obligatoryAdminApproval.assetTypes.join("|"),
82
- // Pending staff requests to enable Wallet Blocking / Takedowns that a
83
- // higher-up has not yet approved. Raw `serviceType` enum values,
84
- // de-duplicated; an empty cell means nothing is awaiting approval.
85
- pendingServiceApprovals: Array.from(
86
- new Set(
87
- organization.pendingServiceApprovals.map((approval) => approval.serviceType)
88
- )
89
- ).join("|")
90
- }
91
- ]),
92
- human: () => {
93
- console.log(`${organization.name} (slug: ${organization.slug})`);
94
- console.log(` contactUrl: ${organization.contactUrl ?? "(not set)"}`);
95
- console.log(` subscriptionStatus: ${organization.subscriptionStatus}`);
96
- console.log(` services: ${summarizeServices(organization)}`);
97
- console.log(
98
- ` obligatoryAdminApproval: ${summarizeObligatoryAdminApproval(organization)}`
99
- );
100
- console.log(
101
- ` pendingServiceApprovals: ${summarizePendingApprovals(organization)}`
102
- );
103
- }
104
- });
105
- }
106
- export {
107
- runOrgsGet
108
- };
@@ -1,63 +0,0 @@
1
- import {
2
- printOutput,
3
- toCsvRows
4
- } from "./chunk-VFT3TD3E.js";
5
- import {
6
- createApiClient
7
- } from "./chunk-OFUIYMPD.js";
8
- import "./chunk-EGWK6SRQ.js";
9
- import "./chunk-U73SABXK.js";
10
-
11
- // src/commands/brands/list.ts
12
- function toRow(brand) {
13
- return {
14
- id: brand.id,
15
- slug: brand.slug,
16
- name: brand.name,
17
- type: brand.type,
18
- description: brand.description ?? "",
19
- brandGroupId: brand.brandGroupId,
20
- createdAt: brand.createdAt
21
- };
22
- }
23
- async function runBrandsList(options) {
24
- const outputFormat = options.outputFormat ?? (options.json ? "json" : "human");
25
- const client = options.apiClient ?? createApiClient();
26
- const result = await client.listOrganizationBrands();
27
- const brands = options.type ? result.brands.filter((brand) => brand.type === options.type) : result.brands;
28
- const rows = brands.map(toRow);
29
- printOutput({
30
- outputFormat,
31
- json: options.type ? { ...result, brands } : result,
32
- markdown: [
33
- "# Organization brands",
34
- "",
35
- `- Returned: ${brands.length}${options.type ? ` (filtered to type=${options.type})` : ""}`,
36
- "",
37
- ...rows.map(
38
- (row) => `- #${row.id} ${row.name} (slug=${row.slug}, type=${row.type}${row.brandGroupId !== null ? `, groupId=${row.brandGroupId}` : ""})`
39
- )
40
- ].join("\n"),
41
- csv: toCsvRows(rows),
42
- human: () => {
43
- if (brands.length === 0) {
44
- console.log(
45
- options.type ? `No brands of type ${options.type} found.` : "No brands found."
46
- );
47
- return;
48
- }
49
- console.log(
50
- `Organization brands \u2014 returned ${brands.length}${options.type ? ` (type=${options.type})` : ""}`
51
- );
52
- for (const row of rows) {
53
- const group = row.brandGroupId !== null ? ` groupId=${row.brandGroupId}` : "";
54
- console.log(
55
- `#${row.id} ${row.name} (slug=${row.slug}, type=${row.type}${group})`
56
- );
57
- }
58
- }
59
- });
60
- }
61
- export {
62
- runBrandsList
63
- };