@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 +6 -2
- package/package.json +1 -1
- package/skills/codex/clankmates/SKILL.md +5 -1
- package/src/commands/inbox.ts +82 -15
- package/src/lib/client.ts +40 -0
- package/src/lib/help.ts +15 -1
- package/src/types/api.ts +5 -1
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.
|
|
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
|
|
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
|
@@ -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
|
|
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
|
package/src/commands/inbox.ts
CHANGED
|
@@ -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,
|
|
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`,
|
|
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 } }
|