shipmail-cli 0.1.7 → 0.1.9
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 +24 -6
- package/dist/cli.js +229 -9
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -22,19 +22,35 @@ You can also pass `--api-key` to any command. Use `SHIPMAIL_BASE_URL` or `--base
|
|
|
22
22
|
# Check public API health
|
|
23
23
|
shipmail status
|
|
24
24
|
|
|
25
|
-
# Create a domain and verify DNS
|
|
25
|
+
# Create a domain, retrieve records, and verify DNS
|
|
26
26
|
shipmail domains create example.com
|
|
27
|
+
shipmail domains dns-records dom_123
|
|
27
28
|
shipmail domains verify dom_123
|
|
28
29
|
|
|
29
30
|
# Create a mailbox
|
|
30
|
-
shipmail mailboxes create --domain-id dom_123 --address hello --display-name "Hello"
|
|
31
|
+
shipmail mailboxes create --domain-id dom_123 --address hello --password-file ./mailbox-password.txt --display-name "Hello"
|
|
32
|
+
shipmail mailboxes suspend mbx_123
|
|
33
|
+
shipmail mailboxes resume mbx_123
|
|
34
|
+
shipmail mailboxes forwarding-add mbx_123 --destination owner@example.net
|
|
35
|
+
shipmail mailboxes forwarding mbx_123
|
|
36
|
+
shipmail mailboxes forwarding-remove mbx_123 fwd_123
|
|
31
37
|
|
|
32
38
|
# Send email
|
|
33
39
|
shipmail messages send \
|
|
34
40
|
--mailbox-id mbx_123 \
|
|
35
41
|
--to user@example.com \
|
|
36
42
|
--subject "Hello" \
|
|
37
|
-
--text "It works."
|
|
43
|
+
--text "It works." \
|
|
44
|
+
--client-reference crm-123 \
|
|
45
|
+
--metadata '{"campaign":"onboarding"}' \
|
|
46
|
+
--source-rfc-message-id '<crm-123@example.com>'
|
|
47
|
+
|
|
48
|
+
shipmail messages list --client-reference crm-123
|
|
49
|
+
shipmail messages reply msg_123 --to user@example.com --text "Thanks"
|
|
50
|
+
|
|
51
|
+
# Use an sm_test_... key to simulate an outbound result and fake inbound reply
|
|
52
|
+
shipmail messages send --mailbox-id mbx_123 --to user@example.com --subject "Test" --text "Not delivered" --sandbox-outcome bounced
|
|
53
|
+
shipmail mailboxes sandbox-inbound mbx_123 --from customer@example.com --subject "Re: Test" --text "Fake inbound reply"
|
|
38
54
|
|
|
39
55
|
# Read inbound mail
|
|
40
56
|
shipmail inbox list mbx_123 --folder-role inbox --limit 25
|
|
@@ -62,6 +78,7 @@ shipmail webhooks create \
|
|
|
62
78
|
--url https://example.com/shipmail/webhook \
|
|
63
79
|
--events message.received,message.bounced
|
|
64
80
|
shipmail webhooks deliveries whk_123 --status failed
|
|
81
|
+
shipmail webhooks replay-delivery whk_123 dlv_123 --idempotency-key replay-123
|
|
65
82
|
```
|
|
66
83
|
|
|
67
84
|
`newsletter-assets list` includes current newsletter media storage usage in the JSON response.
|
|
@@ -87,10 +104,11 @@ Use repeatable `--header "Name: value"` flags for request headers. Use `--output
|
|
|
87
104
|
- `status`
|
|
88
105
|
- `api <METHOD> <PATH>`
|
|
89
106
|
- `domains list|search|create|get|verify|delete`
|
|
90
|
-
- `mailboxes list|create|get|folders|identities|rules`
|
|
107
|
+
- `mailboxes list|create|get|suspend|resume|forwarding|forwarding-add|forwarding-remove|folders|identities|rules`
|
|
91
108
|
- `inbox list|thread|update|move`
|
|
92
|
-
- `messages send|list|get`
|
|
93
|
-
- `
|
|
109
|
+
- `messages send|list|get|reply` (test keys also support `--sandbox-outcome`)
|
|
110
|
+
- `mailboxes sandbox-inbound`
|
|
111
|
+
- `webhooks list|create|test|deliveries|replay-delivery`
|
|
94
112
|
- `suppressions list|remove`
|
|
95
113
|
- `newsletter-domains list`
|
|
96
114
|
- `newsletters create|update|preview|preflight|test-send|schedule|cancel|resume`
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "shipmail";
|
|
13
13
|
|
|
14
14
|
// src/version.ts
|
|
15
|
-
var VERSION = "0.1.
|
|
15
|
+
var VERSION = "0.1.9";
|
|
16
16
|
|
|
17
17
|
// src/cli.ts
|
|
18
18
|
var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
|
|
@@ -311,21 +311,34 @@ Usage:
|
|
|
311
311
|
shipmail domains search <keyword>
|
|
312
312
|
shipmail domains create <name>
|
|
313
313
|
shipmail domains get <id>
|
|
314
|
+
shipmail domains dns-records <id>
|
|
314
315
|
shipmail domains verify <id>
|
|
315
316
|
shipmail domains delete <id>
|
|
316
317
|
shipmail mailboxes list [--domain-id dom] [--limit 25] [--cursor cur]
|
|
317
|
-
shipmail mailboxes create --domain-id dom --address hello [--display-name "Hello"]
|
|
318
|
+
shipmail mailboxes create --domain-id dom --address hello (--password value | --password-file path) [--display-name "Hello"]
|
|
318
319
|
shipmail mailboxes get <id>
|
|
320
|
+
shipmail mailboxes suspend <id>
|
|
321
|
+
shipmail mailboxes resume <id>
|
|
322
|
+
shipmail mailboxes export <id>
|
|
323
|
+
shipmail mailboxes export-status <id> <export-id>
|
|
319
324
|
shipmail mailboxes folders <id>
|
|
320
325
|
shipmail mailboxes identities <id>
|
|
321
326
|
shipmail mailboxes rules <id>
|
|
327
|
+
shipmail mailboxes forwarding <id>
|
|
328
|
+
shipmail mailboxes forwarding-add <id> --destination owner@example.com
|
|
329
|
+
shipmail mailboxes forwarding-remove <id> <forwarding-id>
|
|
330
|
+
shipmail mailboxes sandbox-inbound <id> --from sender@example.com --subject "Test" (--text "Hello" | --html "<p>Hello</p>") [--to support@example.com]
|
|
322
331
|
shipmail inbox list <mailbox-id> [--folder-role inbox] [--limit 25]
|
|
323
332
|
shipmail inbox thread <mailbox-id> <thread-id>
|
|
324
333
|
shipmail inbox update <mailbox-id> <message-id> [--read true] [--starred false]
|
|
325
334
|
shipmail inbox move <mailbox-id> <message-id> (--target-role archive | --target-folder-id fld)
|
|
326
|
-
shipmail messages send --mailbox-id mbx --to user@example.com --subject "Hi" --text "Hello"
|
|
335
|
+
shipmail messages send --mailbox-id mbx --to user@example.com --subject "Hi" --text "Hello" [--sandbox-outcome delivered|bounced|complained] [--client-reference crm-123] [--metadata JSON|@file] [--source-rfc-message-id '<crm-123@example.com>'] [--header 'X-CRM-ID: 123']
|
|
336
|
+
shipmail messages list (--mailbox-id mbx | --client-reference crm-123) [--limit 25] [--cursor cur]
|
|
337
|
+
shipmail messages get <message-id>
|
|
338
|
+
shipmail messages reply <message-id> --to user@example.com (--text "Hello" | --html "<p>Hello</p>") [--client-reference crm-reply-123]
|
|
327
339
|
shipmail webhooks list
|
|
328
340
|
shipmail webhooks create --url https://example.com/hook --events message.received,message.bounced
|
|
341
|
+
shipmail webhooks replay-delivery <webhook-id> <delivery-id> [--idempotency-key key]
|
|
329
342
|
shipmail suppressions list
|
|
330
343
|
shipmail suppressions remove <email>
|
|
331
344
|
shipmail newsletters create \\
|
|
@@ -359,7 +372,7 @@ Usage:
|
|
|
359
372
|
shipmail audiences subscribers resubscribe <audience-id> <subscriber-id> --consent-source "Signup form"
|
|
360
373
|
shipmail audiences subscribers remove <audience-id> <subscriber-id>
|
|
361
374
|
shipmail calendar events list --mailbox hello@example.com --from 2026-08-01T00:00:00Z --to 2026-08-08T00:00:00Z [--expand true] [--limit 100]
|
|
362
|
-
shipmail calendar events create --mailbox hello@example.com --title "Sync" --start 2026-08-01T14:00:00 [--timezone Europe/Paris] [--duration PT30M] [--
|
|
375
|
+
shipmail calendar events create --mailbox hello@example.com --title "Sync" --start 2026-08-01T14:00:00 [--timezone Europe/Paris] [--duration PT30M] [--attendees JSON|@file] [--reminders JSON|@file]
|
|
363
376
|
shipmail calendar events get <event-id> --mailbox hello@example.com
|
|
364
377
|
shipmail calendar events update <event-id> --mailbox hello@example.com [--title "..."] [--start ...]
|
|
365
378
|
shipmail calendar events delete <event-id> --mailbox hello@example.com
|
|
@@ -425,7 +438,24 @@ function webhookDeliveryParams(options) {
|
|
|
425
438
|
...eventType ? { event_type: eventType } : {}
|
|
426
439
|
};
|
|
427
440
|
}
|
|
428
|
-
function
|
|
441
|
+
function outboundMessageHeaders(options) {
|
|
442
|
+
const headers = getStringList(options, "header");
|
|
443
|
+
return headers.map((value) => {
|
|
444
|
+
const index = value.indexOf(":");
|
|
445
|
+
if (index <= 0) throw new CliUsageError("--header must use 'Name: value' format.");
|
|
446
|
+
return { name: value.slice(0, index).trim(), value: value.slice(index + 1).trim() };
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
async function messageMetadata(options) {
|
|
450
|
+
const raw = getString(options, "metadata");
|
|
451
|
+
if (!raw) return void 0;
|
|
452
|
+
const parsed = await readJsonInput(raw);
|
|
453
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
454
|
+
throw new CliUsageError("--metadata must be a JSON object or @path/to/file.json.");
|
|
455
|
+
}
|
|
456
|
+
return parsed;
|
|
457
|
+
}
|
|
458
|
+
async function sendMessageParams(options) {
|
|
429
459
|
const mailboxId = getString(options, "mailbox-id");
|
|
430
460
|
const from = getString(options, "from");
|
|
431
461
|
const to = messageRecipients(options, "to");
|
|
@@ -435,6 +465,8 @@ function sendMessageParams(options) {
|
|
|
435
465
|
const subject = getRequiredString(options, "subject");
|
|
436
466
|
const text = getString(options, "text");
|
|
437
467
|
const html = getString(options, "html");
|
|
468
|
+
const headers = outboundMessageHeaders(options);
|
|
469
|
+
const metadata = await messageMetadata(options);
|
|
438
470
|
if (!mailboxId && !from) throw new CliUsageError("Provide --mailbox-id or --from.");
|
|
439
471
|
if (to.length === 0) throw new CliUsageError("Provide at least one --to recipient.");
|
|
440
472
|
if (!text && !html) throw new CliUsageError("Provide --text or --html.");
|
|
@@ -447,7 +479,47 @@ function sendMessageParams(options) {
|
|
|
447
479
|
...replyTo ? { reply_to: replyTo } : {},
|
|
448
480
|
subject,
|
|
449
481
|
...html ? { html } : {},
|
|
450
|
-
...text ? { text } : {}
|
|
482
|
+
...text ? { text } : {},
|
|
483
|
+
...getString(options, "client-reference") ? { client_reference: getString(options, "client-reference") } : {},
|
|
484
|
+
...metadata ? { metadata } : {},
|
|
485
|
+
...getString(options, "source-rfc-message-id") ? { source_rfc_message_id: getString(options, "source-rfc-message-id") } : {},
|
|
486
|
+
...headers.length > 0 ? { headers } : {},
|
|
487
|
+
...getString(options, "scheduled-at") ? { scheduled_at: getString(options, "scheduled-at") } : {},
|
|
488
|
+
...getString(options, "sandbox-outcome") ? {
|
|
489
|
+
sandbox_outcome: getEnum(options, "sandbox-outcome", [
|
|
490
|
+
"delivered",
|
|
491
|
+
"bounced",
|
|
492
|
+
"complained"
|
|
493
|
+
])
|
|
494
|
+
} : {}
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
async function replyToMessageParams(options) {
|
|
498
|
+
const to = messageRecipients(options, "to");
|
|
499
|
+
const cc = messageRecipients(options, "cc");
|
|
500
|
+
const text = getString(options, "text");
|
|
501
|
+
const html = getString(options, "html");
|
|
502
|
+
const headers = outboundMessageHeaders(options);
|
|
503
|
+
const metadata = await messageMetadata(options);
|
|
504
|
+
if (to.length === 0) throw new CliUsageError("Provide at least one --to recipient.");
|
|
505
|
+
if (!text && !html) throw new CliUsageError("Provide --text or --html.");
|
|
506
|
+
return {
|
|
507
|
+
to,
|
|
508
|
+
...cc.length > 0 ? { cc } : {},
|
|
509
|
+
...html ? { html } : {},
|
|
510
|
+
...text ? { text } : {},
|
|
511
|
+
...getString(options, "client-reference") ? { client_reference: getString(options, "client-reference") } : {},
|
|
512
|
+
...metadata ? { metadata } : {},
|
|
513
|
+
...getString(options, "source-rfc-message-id") ? { source_rfc_message_id: getString(options, "source-rfc-message-id") } : {},
|
|
514
|
+
...headers.length > 0 ? { headers } : {},
|
|
515
|
+
...getString(options, "scheduled-at") ? { scheduled_at: getString(options, "scheduled-at") } : {},
|
|
516
|
+
...getString(options, "sandbox-outcome") ? {
|
|
517
|
+
sandbox_outcome: getEnum(options, "sandbox-outcome", [
|
|
518
|
+
"delivered",
|
|
519
|
+
"bounced",
|
|
520
|
+
"complained"
|
|
521
|
+
])
|
|
522
|
+
} : {}
|
|
451
523
|
};
|
|
452
524
|
}
|
|
453
525
|
async function runDomains(client, parsed, out) {
|
|
@@ -480,6 +552,13 @@ async function runDomains(client, parsed, out) {
|
|
|
480
552
|
writeJson(out, await client.domains.get(requirePositional(parsed.positional, 2, "domain id")));
|
|
481
553
|
return;
|
|
482
554
|
}
|
|
555
|
+
if (subcommand === "dns-records") {
|
|
556
|
+
writeJson(
|
|
557
|
+
out,
|
|
558
|
+
await client.domains.getDnsRecords(requirePositional(parsed.positional, 2, "domain id"))
|
|
559
|
+
);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
483
562
|
if (subcommand === "verify") {
|
|
484
563
|
writeJson(
|
|
485
564
|
out,
|
|
@@ -510,11 +589,22 @@ async function runMailboxes(client, parsed, out) {
|
|
|
510
589
|
}
|
|
511
590
|
if (subcommand === "create") {
|
|
512
591
|
const displayName = getString(parsed.options, "display-name");
|
|
592
|
+
const passwordValue = getString(parsed.options, "password");
|
|
593
|
+
const passwordFile = getString(parsed.options, "password-file");
|
|
594
|
+
if (passwordValue && passwordFile) {
|
|
595
|
+
throw new CliUsageError("Pass either --password or --password-file, not both.");
|
|
596
|
+
}
|
|
597
|
+
if (!passwordValue && !passwordFile) {
|
|
598
|
+
throw new CliUsageError("Missing --password or --password-file.");
|
|
599
|
+
}
|
|
600
|
+
const password = passwordFile ? (await readFile(passwordFile, "utf8")).replace(/\r?\n$/, "") : passwordValue;
|
|
601
|
+
if (!password) throw new CliUsageError("Mailbox password must not be empty.");
|
|
513
602
|
writeJson(
|
|
514
603
|
out,
|
|
515
604
|
await client.mailboxes.create({
|
|
516
605
|
domain_id: getRequiredString(parsed.options, "domain-id"),
|
|
517
606
|
address: getRequiredString(parsed.options, "address"),
|
|
607
|
+
password,
|
|
518
608
|
...displayName ? { display_name: displayName } : {}
|
|
519
609
|
})
|
|
520
610
|
);
|
|
@@ -527,6 +617,37 @@ async function runMailboxes(client, parsed, out) {
|
|
|
527
617
|
);
|
|
528
618
|
return;
|
|
529
619
|
}
|
|
620
|
+
if (subcommand === "suspend") {
|
|
621
|
+
writeJson(
|
|
622
|
+
out,
|
|
623
|
+
await client.mailboxes.suspend(requirePositional(parsed.positional, 2, "mailbox id"))
|
|
624
|
+
);
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
if (subcommand === "resume") {
|
|
628
|
+
writeJson(
|
|
629
|
+
out,
|
|
630
|
+
await client.mailboxes.resume(requirePositional(parsed.positional, 2, "mailbox id"))
|
|
631
|
+
);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
if (subcommand === "export") {
|
|
635
|
+
writeJson(
|
|
636
|
+
out,
|
|
637
|
+
await client.mailboxes.createExport(requirePositional(parsed.positional, 2, "mailbox id"))
|
|
638
|
+
);
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (subcommand === "export-status") {
|
|
642
|
+
writeJson(
|
|
643
|
+
out,
|
|
644
|
+
await client.mailboxes.getExport(
|
|
645
|
+
requirePositional(parsed.positional, 2, "mailbox id"),
|
|
646
|
+
requirePositional(parsed.positional, 3, "export id")
|
|
647
|
+
)
|
|
648
|
+
);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
530
651
|
if (subcommand === "folders") {
|
|
531
652
|
writeJson(
|
|
532
653
|
out,
|
|
@@ -548,6 +669,58 @@ async function runMailboxes(client, parsed, out) {
|
|
|
548
669
|
);
|
|
549
670
|
return;
|
|
550
671
|
}
|
|
672
|
+
if (subcommand === "forwarding") {
|
|
673
|
+
writeJson(
|
|
674
|
+
out,
|
|
675
|
+
await client.mailboxes.listForwarding(requirePositional(parsed.positional, 2, "mailbox id"))
|
|
676
|
+
);
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if (subcommand === "forwarding-add") {
|
|
680
|
+
writeJson(
|
|
681
|
+
out,
|
|
682
|
+
await client.mailboxes.createForwarding(
|
|
683
|
+
requirePositional(parsed.positional, 2, "mailbox id"),
|
|
684
|
+
{ destination: getRequiredString(parsed.options, "destination") }
|
|
685
|
+
)
|
|
686
|
+
);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
if (subcommand === "forwarding-remove") {
|
|
690
|
+
const mailboxId = requirePositional(parsed.positional, 2, "mailbox id");
|
|
691
|
+
const forwardingId = requirePositional(parsed.positional, 3, "forwarding id");
|
|
692
|
+
await client.mailboxes.deleteForwarding(mailboxId, forwardingId);
|
|
693
|
+
writeJson(out, { ok: true, id: forwardingId });
|
|
694
|
+
return;
|
|
695
|
+
}
|
|
696
|
+
if (subcommand === "sandbox-inbound") {
|
|
697
|
+
const text = getString(parsed.options, "text");
|
|
698
|
+
const html = getString(parsed.options, "html");
|
|
699
|
+
if (!text && !html) throw new CliUsageError("Provide --text or --html.");
|
|
700
|
+
const to = messageRecipients(parsed.options, "to");
|
|
701
|
+
const cc = messageRecipients(parsed.options, "cc");
|
|
702
|
+
const headers = outboundMessageHeaders(parsed.options);
|
|
703
|
+
writeJson(
|
|
704
|
+
out,
|
|
705
|
+
await client.mailboxes.injectSandboxInbound(
|
|
706
|
+
requirePositional(parsed.positional, 2, "mailbox id"),
|
|
707
|
+
{
|
|
708
|
+
from: getRequiredString(parsed.options, "from"),
|
|
709
|
+
...to.length > 0 ? { to } : {},
|
|
710
|
+
...cc.length > 0 ? { cc } : {},
|
|
711
|
+
subject: getRequiredString(parsed.options, "subject"),
|
|
712
|
+
...text ? { text } : {},
|
|
713
|
+
...html ? { html } : {},
|
|
714
|
+
...getString(parsed.options, "message-id") ? { message_id: getString(parsed.options, "message-id") } : {},
|
|
715
|
+
...getString(parsed.options, "in-reply-to") ? { in_reply_to: getString(parsed.options, "in-reply-to") } : {},
|
|
716
|
+
...getStringList(parsed.options, "reference").length > 0 ? { references: getStringList(parsed.options, "reference") } : {},
|
|
717
|
+
...getString(parsed.options, "received-at") ? { received_at: getString(parsed.options, "received-at") } : {},
|
|
718
|
+
...headers.length > 0 ? { headers } : {}
|
|
719
|
+
}
|
|
720
|
+
)
|
|
721
|
+
);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
551
724
|
throw new CliUsageError("Unknown mailboxes command.");
|
|
552
725
|
}
|
|
553
726
|
async function runInbox(client, parsed, out) {
|
|
@@ -616,20 +789,36 @@ async function runInbox(client, parsed, out) {
|
|
|
616
789
|
async function runMessages(client, parsed, out) {
|
|
617
790
|
const subcommand = parsed.positional[1];
|
|
618
791
|
if (subcommand === "send") {
|
|
619
|
-
writeJson(out, await client.messages.send(sendMessageParams(parsed.options)));
|
|
792
|
+
writeJson(out, await client.messages.send(await sendMessageParams(parsed.options)));
|
|
620
793
|
return;
|
|
621
794
|
}
|
|
622
795
|
if (subcommand === "list") {
|
|
796
|
+
const mailboxId = getString(parsed.options, "mailbox-id");
|
|
797
|
+
const clientReference = getString(parsed.options, "client-reference");
|
|
798
|
+
if (!mailboxId && !clientReference) {
|
|
799
|
+
throw new CliUsageError("Provide --mailbox-id or --client-reference.");
|
|
800
|
+
}
|
|
801
|
+
const filter = mailboxId ? { mailbox_id: mailboxId, ...clientReference ? { client_reference: clientReference } : {} } : { client_reference: clientReference };
|
|
623
802
|
writeJson(
|
|
624
803
|
out,
|
|
625
804
|
await client.messages.list({
|
|
626
|
-
|
|
805
|
+
...filter,
|
|
627
806
|
cursor: getString(parsed.options, "cursor"),
|
|
628
807
|
limit: getNumber(parsed.options, "limit")
|
|
629
808
|
})
|
|
630
809
|
);
|
|
631
810
|
return;
|
|
632
811
|
}
|
|
812
|
+
if (subcommand === "reply") {
|
|
813
|
+
writeJson(
|
|
814
|
+
out,
|
|
815
|
+
await client.messages.reply(
|
|
816
|
+
requirePositional(parsed.positional, 2, "message id"),
|
|
817
|
+
await replyToMessageParams(parsed.options)
|
|
818
|
+
)
|
|
819
|
+
);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
633
822
|
if (subcommand === "get") {
|
|
634
823
|
writeJson(
|
|
635
824
|
out,
|
|
@@ -680,6 +869,18 @@ async function runWebhooks(client, parsed, out) {
|
|
|
680
869
|
);
|
|
681
870
|
return;
|
|
682
871
|
}
|
|
872
|
+
if (subcommand === "replay-delivery") {
|
|
873
|
+
const idempotencyKey = getString(parsed.options, "idempotency-key");
|
|
874
|
+
writeJson(
|
|
875
|
+
out,
|
|
876
|
+
await client.webhooks.replayDelivery(
|
|
877
|
+
requirePositional(parsed.positional, 2, "webhook id"),
|
|
878
|
+
requirePositional(parsed.positional, 3, "delivery id"),
|
|
879
|
+
idempotencyKey ? { idempotencyKey } : void 0
|
|
880
|
+
)
|
|
881
|
+
);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
683
884
|
throw new CliUsageError("Unknown webhooks command.");
|
|
684
885
|
}
|
|
685
886
|
async function runSuppressions(client, parsed, out) {
|
|
@@ -704,6 +905,7 @@ async function runSuppressions(client, parsed, out) {
|
|
|
704
905
|
}
|
|
705
906
|
var SUBSCRIBER_STATUS_VALUES = [
|
|
706
907
|
"subscribed",
|
|
908
|
+
"pending",
|
|
707
909
|
"unsubscribed",
|
|
708
910
|
"bounced",
|
|
709
911
|
"complained",
|
|
@@ -1033,6 +1235,22 @@ async function readRemindersInput(value) {
|
|
|
1033
1235
|
}
|
|
1034
1236
|
return parsed;
|
|
1035
1237
|
}
|
|
1238
|
+
function isAttendeeArray(value) {
|
|
1239
|
+
return Array.isArray(value) && value.every((item) => {
|
|
1240
|
+
if (typeof item !== "object" || item === null || Array.isArray(item)) return false;
|
|
1241
|
+
const email = Reflect.get(item, "email");
|
|
1242
|
+
const name = Reflect.get(item, "name");
|
|
1243
|
+
return typeof email === "string" && (name === void 0 || typeof name === "string");
|
|
1244
|
+
});
|
|
1245
|
+
}
|
|
1246
|
+
async function readAttendeesInput(value) {
|
|
1247
|
+
const parsed = await readJsonInput(value);
|
|
1248
|
+
if (parsed === void 0) return void 0;
|
|
1249
|
+
if (!isAttendeeArray(parsed)) {
|
|
1250
|
+
throw new CliUsageError("--attendees must be a JSON array of { email, name? } objects.");
|
|
1251
|
+
}
|
|
1252
|
+
return parsed;
|
|
1253
|
+
}
|
|
1036
1254
|
async function calendarEventFields(options) {
|
|
1037
1255
|
const description = getString(options, "description");
|
|
1038
1256
|
const timezone = getString(options, "timezone");
|
|
@@ -1045,6 +1263,7 @@ async function calendarEventFields(options) {
|
|
|
1045
1263
|
const freeBusyStatus = getEnum(options, "free-busy-status", CALENDAR_FREE_BUSY_STATUSES);
|
|
1046
1264
|
const privacy = getEnum(options, "privacy", CALENDAR_PRIVACIES);
|
|
1047
1265
|
const reminders = await readRemindersInput(getString(options, "reminders"));
|
|
1266
|
+
const attendees = await readAttendeesInput(getString(options, "attendees"));
|
|
1048
1267
|
return {
|
|
1049
1268
|
...description !== void 0 ? { description } : {},
|
|
1050
1269
|
...timezone !== void 0 ? { timezone } : {},
|
|
@@ -1056,7 +1275,8 @@ async function calendarEventFields(options) {
|
|
|
1056
1275
|
...status !== void 0 ? { status } : {},
|
|
1057
1276
|
...freeBusyStatus !== void 0 ? { free_busy_status: freeBusyStatus } : {},
|
|
1058
1277
|
...privacy !== void 0 ? { privacy } : {},
|
|
1059
|
-
...reminders !== void 0 ? { reminders } : {}
|
|
1278
|
+
...reminders !== void 0 ? { reminders } : {},
|
|
1279
|
+
...attendees !== void 0 ? { attendees } : {}
|
|
1060
1280
|
};
|
|
1061
1281
|
}
|
|
1062
1282
|
async function runCalendarEvents(client, parsed, out) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shipmail-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Official CLI for the ShipMail API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"access": "public"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"shipmail": "0.1.
|
|
44
|
+
"shipmail": "0.1.32"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"tsup": "^8.4.0",
|