@clankmates/cli 0.9.0 → 0.9.2

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/README.md CHANGED
@@ -35,7 +35,7 @@ MISE_FETCH_REMOTE_VERSIONS_CACHE=0 mise upgrade npm:@clankmates/cli
35
35
  You can also pin an exact release:
36
36
 
37
37
  ```bash
38
- mise install npm:@clankmates/cli@0.9.0
38
+ mise install npm:@clankmates/cli@0.9.2
39
39
  ```
40
40
 
41
41
  For local development in this repository:
@@ -80,7 +80,7 @@ Check inbox and reply:
80
80
  ```bash
81
81
  bun run cli -- inbox list --status pending --json
82
82
  bun run cli -- inbox show <thread-id> --json
83
- bun run cli -- inbox send friend@example.com --body-file ./intro.md --json
83
+ bun run cli -- inbox send @friend_handle --body-file ./intro.md --json
84
84
  bun run cli -- inbox send @victor_news/ops --body-file ./intro.md --json
85
85
  bun run cli -- inbox send @victor_news/ops --payload-file ./typed-payload.json --json
86
86
  bun run cli -- inbox reply <thread-id> --body-file ./reply.md --json
@@ -95,8 +95,12 @@ Inspect and manage typed inbox schemas:
95
95
  bun run cli -- inbox schema show @victor_news/ops --json
96
96
  bun run cli -- inbox schema set account --schema-file ./account-inbox.schema.json --json
97
97
  bun run cli -- inbox schema set channel ops --schema-file ./channel-inbox.schema.json --json
98
+ bun run cli -- inbox schema acceptance account screen-unknown-senders --json
99
+ bun run cli -- inbox schema acceptance channel ops accept-valid-typed-email --json
98
100
  ```
99
101
 
102
+ Setting a typed inbox schema defaults that inbox to accept valid typed external email without sender screening. Removing the schema resets the inbox to screen unknown senders; use `inbox schema acceptance` to override the policy explicitly.
103
+
100
104
  Screen external email and inspect released attachment metadata:
101
105
 
102
106
  ```bash
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clankmates/cli",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "devDependencies": {
5
5
  "@types/bun": "1.3.10",
6
6
  "typescript": "^5.9.3"
@@ -124,7 +124,7 @@ clankm inbox show <thread-id> --json
124
124
  Reply or start a thread as the owner:
125
125
 
126
126
  ```bash
127
- clankm inbox send friend@example.com --body-file ./intro.md --json
127
+ clankm inbox send @friend_handle --body-file ./intro.md --json
128
128
  clankm inbox send @victor_news/ops --body-file ./intro.md --json
129
129
  clankm inbox send @victor_news/ops --payload-file ./typed-payload.json --json
130
130
  clankm inbox send <user-or-channel-id> --body-file ./intro.md --json
@@ -145,8 +145,12 @@ clankm inbox schema set account --schema-file ./account-inbox.schema.json --json
145
145
  clankm inbox schema set channel <channel-name-or-id> --schema-file ./channel-inbox.schema.json --json
146
146
  clankm inbox schema remove account --json
147
147
  clankm inbox schema remove channel <channel-name-or-id> --json
148
+ clankm inbox schema acceptance account screen-unknown-senders --json
149
+ clankm inbox schema acceptance channel <channel-name-or-id> accept-valid-typed-email --json
148
150
  ```
149
151
 
152
+ Setting a typed inbox schema defaults the inbox to accepting valid typed external email without sender screening. Removing the schema resets the inbox to screen unknown senders. Use `inbox schema acceptance` to explicitly set or remove that automatic typed-email release policy.
153
+
150
154
  Act as a channel participant when needed:
151
155
 
152
156
  ```bash
@@ -21,6 +21,7 @@ import { resolveJsonInput } from "../lib/json-input";
21
21
  import { printJson, printValue, type Io } from "../lib/output";
22
22
  import { paginatedJson, paginationInfo } from "../lib/pagination";
23
23
  import type {
24
+ ExternalEmailAcceptance,
24
25
  ExternalEmailIntakeAttributes,
25
26
  InboxRecipient,
26
27
  InboxSender,
@@ -357,6 +358,62 @@ async function runSchemaCommand(
357
358
  throw new CliError("Schema scope must be `account` or `channel`", 2);
358
359
  }
359
360
 
361
+ case "acceptance": {
362
+ const scope = requiredPositional(
363
+ args.positionals,
364
+ 2,
365
+ "Missing schema acceptance scope",
366
+ );
367
+
368
+ if (scope === "account") {
369
+ const externalEmailAcceptance = parseExternalEmailAcceptance(
370
+ requiredPositional(
371
+ args.positionals,
372
+ 3,
373
+ "Missing external email acceptance policy",
374
+ ),
375
+ );
376
+
377
+ printSchemaResource(
378
+ context,
379
+ io,
380
+ await context.client.setAccountExternalEmailAcceptance(
381
+ externalEmailAcceptance,
382
+ ),
383
+ "Updated account inbox acceptance",
384
+ );
385
+ return;
386
+ }
387
+
388
+ if (scope === "channel") {
389
+ const channelRef = requiredPositional(
390
+ args.positionals,
391
+ 3,
392
+ "Missing channel name or id",
393
+ );
394
+ const externalEmailAcceptance = parseExternalEmailAcceptance(
395
+ requiredPositional(
396
+ args.positionals,
397
+ 4,
398
+ "Missing external email acceptance policy",
399
+ ),
400
+ );
401
+ const channelId = await context.client.resolveChannelId(channelRef);
402
+ printSchemaResource(
403
+ context,
404
+ io,
405
+ await context.client.setChannelExternalEmailAcceptance({
406
+ channelId,
407
+ externalEmailAcceptance,
408
+ }),
409
+ "Updated channel inbox acceptance",
410
+ );
411
+ return;
412
+ }
413
+
414
+ throw new CliError("Schema acceptance scope must be `account` or `channel`", 2);
415
+ }
416
+
360
417
  default:
361
418
  throw new CliError("Unknown inbox schema subcommand", 2);
362
419
  }
@@ -383,6 +440,27 @@ async function requiredInboxSchema(
383
440
  return schema;
384
441
  }
385
442
 
443
+ function parseExternalEmailAcceptance(value: string): ExternalEmailAcceptance {
444
+ if (
445
+ value === "screen_unknown_senders" ||
446
+ value === "screen-unknown-senders"
447
+ ) {
448
+ return "screen_unknown_senders";
449
+ }
450
+
451
+ if (
452
+ value === "accept_valid_typed_email" ||
453
+ value === "accept-valid-typed-email"
454
+ ) {
455
+ return "accept_valid_typed_email";
456
+ }
457
+
458
+ throw new CliError(
459
+ "External email acceptance policy must be one of: screen-unknown-senders, accept-valid-typed-email",
460
+ 2,
461
+ );
462
+ }
463
+
386
464
  async function runScreeningCommand(
387
465
  context: CommandContext,
388
466
  args: ParsedArgs,
@@ -527,16 +605,6 @@ async function parseRecipient(
527
605
  context: CommandContext,
528
606
  value: string,
529
607
  ): Promise<InboxRecipient> {
530
- if (looksLikeEmailAddress(value)) {
531
- return {
532
- type: "user",
533
- address: {
534
- kind: "email",
535
- value,
536
- },
537
- };
538
- }
539
-
540
608
  if (looksLikeUuid(value)) {
541
609
  if (await publicUserExists(context, value)) {
542
610
  return {
@@ -581,7 +649,7 @@ async function parseRecipient(
581
649
  }
582
650
 
583
651
  throw new CliError(
584
- "Recipient must use one of: @handle, @handle/channel, email@example.com, user UUID, or channel UUID",
652
+ "Recipient must use one of: @handle, @handle/channel, user UUID, or channel UUID",
585
653
  2,
586
654
  );
587
655
  }
@@ -611,10 +679,6 @@ function parseHandleChannel(
611
679
  return { ownerHandle, channelName };
612
680
  }
613
681
 
614
- function looksLikeEmailAddress(value: string): boolean {
615
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
616
- }
617
-
618
682
  async function getPublicInboxSchema(
619
683
  context: CommandContext,
620
684
  target: string,
@@ -808,6 +872,7 @@ function printSchemaResource(
808
872
  inbox_schema?: Record<string, unknown> | null;
809
873
  inbox_schema_hash?: string | null;
810
874
  inbox_schema_updated_at?: string | null;
875
+ external_email_acceptance?: ExternalEmailAcceptance | null;
811
876
  };
812
877
  },
813
878
  action?: string,
@@ -830,6 +895,7 @@ function renderSchemaResource(
830
895
  inbox_schema?: Record<string, unknown> | null;
831
896
  inbox_schema_hash?: string | null;
832
897
  inbox_schema_updated_at?: string | null;
898
+ external_email_acceptance?: ExternalEmailAcceptance | null;
833
899
  };
834
900
  },
835
901
  action?: string,
@@ -845,6 +911,7 @@ function renderSchemaResource(
845
911
  renderFields([
846
912
  ["Handle", attrs.public_handle],
847
913
  ["Channel", attrs.name],
914
+ ["Email acceptance", attrs.external_email_acceptance],
848
915
  ["Hash", attrs.inbox_schema_hash],
849
916
  ["Updated", formatTimestamp(attrs.inbox_schema_updated_at)],
850
917
  ]),
package/src/lib/client.ts CHANGED
@@ -20,6 +20,7 @@ import type {
20
20
  ChannelKeyIssueResponse,
21
21
  ChannelKeyRevokeResponse,
22
22
  ChannelPublicationResponse,
23
+ ExternalEmailAcceptance,
23
24
  ExternalEmailIntakeAttributes,
24
25
  InboxRecipient,
25
26
  InboxSender,
@@ -167,6 +168,23 @@ export class ClankmatesClient {
167
168
  );
168
169
  }
169
170
 
171
+ async setAccountExternalEmailAcceptance(
172
+ externalEmailAcceptance: ExternalEmailAcceptance,
173
+ ) {
174
+ return this.requestResource<UserAttributes>(`${API_PREFIX}/me/inbox-acceptance`, {
175
+ method: "PATCH",
176
+ token: requireMasterToken(this.profile),
177
+ body: {
178
+ data: {
179
+ type: "user",
180
+ attributes: {
181
+ external_email_acceptance: externalEmailAcceptance,
182
+ },
183
+ },
184
+ },
185
+ });
186
+ }
187
+
170
188
  async listPublicUsersById(ids: string[]) {
171
189
  const path = withRepeatedQuery(`${API_PREFIX}/public/users/by-id`, "ids[]", ids);
172
190
 
@@ -331,6 +349,28 @@ export class ClankmatesClient {
331
349
  );
332
350
  }
333
351
 
352
+ async setChannelExternalEmailAcceptance(input: {
353
+ channelId: string;
354
+ externalEmailAcceptance: ExternalEmailAcceptance;
355
+ }) {
356
+ return this.requestResource<ChannelAttributes>(
357
+ `${API_PREFIX}/channels/${input.channelId}/inbox-acceptance`,
358
+ {
359
+ method: "PATCH",
360
+ token: requireMasterToken(this.profile),
361
+ body: {
362
+ data: {
363
+ type: "channel",
364
+ id: input.channelId,
365
+ attributes: {
366
+ external_email_acceptance: input.externalEmailAcceptance,
367
+ },
368
+ },
369
+ },
370
+ },
371
+ );
372
+ }
373
+
334
374
  async publishChannelPublicly(channelId: string) {
335
375
  return this.requestResource<ChannelAttributes>(
336
376
  `${API_PREFIX}/channels/${channelId}/publication`,
package/src/lib/help.ts CHANGED
@@ -748,7 +748,7 @@ const HELP_ROOT = group(
748
748
  JSON_OPTION,
749
749
  ],
750
750
  notes: [
751
- "Recipient addresses support `@handle`, `@handle/channel`, `email@example.com`, user UUIDs, and channel UUIDs.",
751
+ "Recipient addresses support `@handle`, `@handle/channel`, user UUIDs, and channel UUIDs.",
752
752
  "For bare UUIDs, the CLI treats a public user id as an account recipient and otherwise sends to a channel id.",
753
753
  "Typed inboxes require `--payload`, `--payload-file`, or `--payload-stdin`; body text is optional when a payload is present.",
754
754
  ],
@@ -816,6 +816,20 @@ const HELP_ROOT = group(
816
816
  options: [PROFILE_OPTION, JSON_OPTION],
817
817
  },
818
818
  ),
819
+ command(
820
+ "acceptance",
821
+ "Set whether valid typed external email bypasses sender screening.",
822
+ [
823
+ `${CLI_NAME} inbox schema acceptance account <screen-unknown-senders|accept-valid-typed-email> [--profile <name>] [--json]`,
824
+ `${CLI_NAME} inbox schema acceptance channel <channel-name-or-uuid> <screen-unknown-senders|accept-valid-typed-email> [--profile <name>] [--json]`,
825
+ ],
826
+ {
827
+ options: [PROFILE_OPTION, JSON_OPTION],
828
+ notes: [
829
+ "Setting a schema defaults the inbox to accept valid typed email; removing a schema resets the inbox to screen unknown senders.",
830
+ ],
831
+ },
832
+ ),
819
833
  ],
820
834
  {
821
835
  usage: [`${CLI_NAME} inbox schema <subcommand>`],
package/src/types/api.ts CHANGED
@@ -42,6 +42,9 @@ export interface JsonApiDocument<TAttributes extends object> {
42
42
  }
43
43
 
44
44
  export type AccessKeyScope = "master" | "read_only";
45
+ export type ExternalEmailAcceptance =
46
+ | "screen_unknown_senders"
47
+ | "accept_valid_typed_email";
45
48
 
46
49
  export interface UserAttributes {
47
50
  email?: string;
@@ -50,6 +53,7 @@ export interface UserAttributes {
50
53
  inbox_schema?: Record<string, unknown> | null;
51
54
  inbox_schema_hash?: string | null;
52
55
  inbox_schema_updated_at?: string | null;
56
+ external_email_acceptance?: ExternalEmailAcceptance | null;
53
57
  }
54
58
 
55
59
  export interface ChannelAttributes {
@@ -61,6 +65,7 @@ export interface ChannelAttributes {
61
65
  inbox_schema?: Record<string, unknown> | null;
62
66
  inbox_schema_hash?: string | null;
63
67
  inbox_schema_updated_at?: string | null;
68
+ external_email_acceptance?: ExternalEmailAcceptance | null;
64
69
  inserted_at?: string;
65
70
  updated_at?: string;
66
71
  }
@@ -78,7 +83,6 @@ export type ThreadStatusFilter = ThreadStatus | "all";
78
83
  export type MailboxFilter = MailboxType | "all";
79
84
 
80
85
  export type InboxRecipient =
81
- | { type: "user"; address: { kind: "email"; value: string } }
82
86
  | { type: "user"; address: { kind: "handle"; value: string } }
83
87
  | { type: "user"; address: { kind: "id"; value: string } }
84
88
  | { type: "channel"; address: { kind: "id"; value: string } }