shipmail-cli 0.1.8 → 0.1.10

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.
Files changed (3) hide show
  1. package/README.md +33 -7
  2. package/dist/cli.js +316 -9
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -14,7 +14,7 @@ Most commands require an API key. Create one in the Shipmail dashboard and expor
14
14
  export SHIPMAIL_API_KEY="sm_live_..."
15
15
  ```
16
16
 
17
- You can also pass `--api-key` to any command. Use `SHIPMAIL_BASE_URL` or `--base-url` when targeting a non-production API.
17
+ You can also pass `--api-key` to any command. Use `SHIPMAIL_BASE_URL` or `--base-url` when targeting a non-production API. Approved partners can set `SHIPMAIL_ORGANIZATION_ID` or pass `--organization-id` for delegated infrastructure calls.
18
18
 
19
19
  ## Common Commands
20
20
 
@@ -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,13 @@ 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
82
+
83
+ # Partner beta lifecycle and delegated infrastructure
84
+ shipmail partner organizations create --name "Operator" --external-reference operator_123 --owner-email owner@example.com --mailbox-limit 3 --data-classification internal_test
85
+ shipmail partner usage
86
+ shipmail domains list --organization-id 00000000-0000-4000-8000-000000000123
87
+ shipmail mailboxes create --organization-id 00000000-0000-4000-8000-000000000123 --domain-id dom_123 --address support --generate-password
65
88
  ```
66
89
 
67
90
  `newsletter-assets list` includes current newsletter media storage usage in the JSON response.
@@ -87,13 +110,16 @@ Use repeatable `--header "Name: value"` flags for request headers. Use `--output
87
110
  - `status`
88
111
  - `api <METHOD> <PATH>`
89
112
  - `domains list|search|create|get|verify|delete`
90
- - `mailboxes list|create|get|folders|identities|rules`
113
+ - `mailboxes list|create|get|suspend|resume|forwarding|forwarding-add|forwarding-remove|folders|identities|rules`
91
114
  - `inbox list|thread|update|move`
92
- - `messages send|list|get`
93
- - `webhooks list|create|test|deliveries`
115
+ - `messages send|list|get|reply` (test keys also support `--sandbox-outcome`)
116
+ - `mailboxes sandbox-inbound`
117
+ - `webhooks list|create|test|deliveries|replay-delivery`
94
118
  - `suppressions list|remove`
95
119
  - `newsletter-domains list`
96
120
  - `newsletters create|update|preview|preflight|test-send|schedule|cancel|resume`
97
121
  - `newsletter-assets list|upload|register-url`
122
+ - `partner organizations list|create|get|update|invite|suspend|resume|offboard`
123
+ - `partner usage`
98
124
 
99
125
  Run `shipmail --help` for flags and examples.
package/dist/cli.js CHANGED
@@ -6,13 +6,14 @@ import { readFile, writeFile } from "fs/promises";
6
6
  import { env as processEnv, exit, stderr, stdout } from "process";
7
7
  import { fileURLToPath } from "url";
8
8
  import {
9
+ CONFERENCING_PROVIDER_IDS,
9
10
  ShipMailClient,
10
11
  ShipMailError,
11
12
  WEBHOOK_EVENT_TYPES
12
13
  } from "shipmail";
13
14
 
14
15
  // src/version.ts
15
- var VERSION = "0.1.8";
16
+ var VERSION = "0.1.10";
16
17
 
17
18
  // src/cli.ts
18
19
  var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
@@ -123,6 +124,9 @@ function apiKey(parsed, env) {
123
124
  function baseUrl(parsed, env) {
124
125
  return normalizeBaseUrl(getString(parsed.options, "base-url") ?? env["SHIPMAIL_BASE_URL"]);
125
126
  }
127
+ function organizationId(parsed, env) {
128
+ return getString(parsed.options, "organization-id") ?? env["SHIPMAIL_ORGANIZATION_ID"];
129
+ }
126
130
  function requireApiKey(parsed, env) {
127
131
  const key = apiKey(parsed, env);
128
132
  if (!key) {
@@ -135,6 +139,7 @@ function createClient(parsed, options) {
135
139
  const config = {
136
140
  apiKey: requireApiKey(parsed, env),
137
141
  baseUrl: baseUrl(parsed, env),
142
+ organizationId: organizationId(parsed, env),
138
143
  ...options.fetch ? { fetch: options.fetch } : {}
139
144
  };
140
145
  return new ShipMailClient(config);
@@ -264,6 +269,10 @@ async function rawApiRequest(parsed, runOptions, method, path, body, authRequire
264
269
  const key = apiKey(parsed, env);
265
270
  if (key) headers["Authorization"] = `Bearer ${key}`;
266
271
  }
272
+ const targetOrganizationId = organizationId(parsed, env);
273
+ if (targetOrganizationId) {
274
+ headers["X-ShipMail-Organization-Id"] = targetOrganizationId;
275
+ }
267
276
  let requestBody;
268
277
  if (body !== void 0) {
269
278
  headers["Content-Type"] = "application/json";
@@ -311,21 +320,34 @@ Usage:
311
320
  shipmail domains search <keyword>
312
321
  shipmail domains create <name>
313
322
  shipmail domains get <id>
323
+ shipmail domains dns-records <id>
314
324
  shipmail domains verify <id>
315
325
  shipmail domains delete <id>
316
326
  shipmail mailboxes list [--domain-id dom] [--limit 25] [--cursor cur]
317
- shipmail mailboxes create --domain-id dom --address hello [--display-name "Hello"]
327
+ shipmail mailboxes create --domain-id dom --address hello (--password value | --password-file path | --generate-password) [--display-name "Hello"]
318
328
  shipmail mailboxes get <id>
329
+ shipmail mailboxes suspend <id>
330
+ shipmail mailboxes resume <id>
331
+ shipmail mailboxes export <id>
332
+ shipmail mailboxes export-status <id> <export-id>
319
333
  shipmail mailboxes folders <id>
320
334
  shipmail mailboxes identities <id>
321
335
  shipmail mailboxes rules <id>
336
+ shipmail mailboxes forwarding <id>
337
+ shipmail mailboxes forwarding-add <id> --destination owner@example.com
338
+ shipmail mailboxes forwarding-remove <id> <forwarding-id>
339
+ shipmail mailboxes sandbox-inbound <id> --from sender@example.com --subject "Test" (--text "Hello" | --html "<p>Hello</p>") [--to support@example.com]
322
340
  shipmail inbox list <mailbox-id> [--folder-role inbox] [--limit 25]
323
341
  shipmail inbox thread <mailbox-id> <thread-id>
324
342
  shipmail inbox update <mailbox-id> <message-id> [--read true] [--starred false]
325
343
  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"
344
+ 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']
345
+ shipmail messages list (--mailbox-id mbx | --client-reference crm-123) [--limit 25] [--cursor cur]
346
+ shipmail messages get <message-id>
347
+ shipmail messages reply <message-id> --to user@example.com (--text "Hello" | --html "<p>Hello</p>") [--client-reference crm-reply-123]
327
348
  shipmail webhooks list
328
349
  shipmail webhooks create --url https://example.com/hook --events message.received,message.bounced
350
+ shipmail webhooks replay-delivery <webhook-id> <delivery-id> [--idempotency-key key]
329
351
  shipmail suppressions list
330
352
  shipmail suppressions remove <email>
331
353
  shipmail newsletters create \\
@@ -365,19 +387,34 @@ Usage:
365
387
  shipmail calendar events delete <event-id> --mailbox hello@example.com
366
388
  shipmail calendar availability --mailbox hello@example.com --from 2026-08-03T00:00:00Z --to 2026-08-04T00:00:00Z [--duration 30] [--days 1,2,3,4,5] [--window-start 540] [--window-end 1020]
367
389
  shipmail booking-pages list [--limit 25] [--cursor cur]
368
- shipmail booking-pages create --name "Intro" --mailbox hello@example.com --slug intro --duration-minutes 30 --availability-days 1,2,3,4,5 --window-start-minutes 540 --window-end-minutes 1020 --timezone America/New_York
390
+ shipmail booking-pages create --name "Intro" --mailbox hello@example.com --slug intro --duration-minutes 30 --availability-days 1,2,3,4,5 --window-start-minutes 540 --window-end-minutes 1020 --timezone America/New_York [--conferencing-provider zoom|google_meet]
369
391
  shipmail booking-pages get <id>
370
- shipmail booking-pages update <id> [--name "..."] [--active false]
392
+ shipmail booking-pages update <id> [--name "..."] [--conferencing-provider zoom|google_meet|none] [--active false]
371
393
  shipmail booking-pages delete <id>
394
+ shipmail partner organizations list
395
+ shipmail partner organizations create --name "Operator" --external-reference op_123 --owner-email owner@example.com [--mailbox-limit 3] [--data-classification internal_test|customer]
396
+ shipmail partner organizations get <id>
397
+ shipmail partner organizations update <id> [--name "Operator"] [--mailbox-limit 5]
398
+ shipmail partner organizations invite <id> [--owner-email owner@example.com]
399
+ shipmail partner organizations suspend <id>
400
+ shipmail partner organizations resume <id>
401
+ shipmail partner organizations offboard <id>
402
+ shipmail partner usage
403
+
404
+ Booking page conferencing:
405
+ --conferencing-provider <zoom|google_meet> Create a meeting for every booking.
406
+ --conferencing-provider none Stop creating meeting links on update.
372
407
 
373
408
  Global options:
374
409
  --api-key <key> API key. Defaults to SHIPMAIL_API_KEY.
375
410
  --base-url <url> API base URL. Defaults to SHIPMAIL_BASE_URL or ${DEFAULT_BASE_URL}.
411
+ --organization-id Delegated child organization for supported infrastructure commands.
376
412
  --header "K: V" Extra header for the generic api command. Repeatable.
377
413
 
378
414
  Environment:
379
415
  SHIPMAIL_API_KEY API key for authenticated commands.
380
416
  SHIPMAIL_BASE_URL Optional API base URL.
417
+ SHIPMAIL_ORGANIZATION_ID Optional delegated child organization ID.
381
418
  `;
382
419
  }
383
420
  function requirePositional(positional, index, label) {
@@ -425,7 +462,24 @@ function webhookDeliveryParams(options) {
425
462
  ...eventType ? { event_type: eventType } : {}
426
463
  };
427
464
  }
428
- function sendMessageParams(options) {
465
+ function outboundMessageHeaders(options) {
466
+ const headers = getStringList(options, "header");
467
+ return headers.map((value) => {
468
+ const index = value.indexOf(":");
469
+ if (index <= 0) throw new CliUsageError("--header must use 'Name: value' format.");
470
+ return { name: value.slice(0, index).trim(), value: value.slice(index + 1).trim() };
471
+ });
472
+ }
473
+ async function messageMetadata(options) {
474
+ const raw = getString(options, "metadata");
475
+ if (!raw) return void 0;
476
+ const parsed = await readJsonInput(raw);
477
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
478
+ throw new CliUsageError("--metadata must be a JSON object or @path/to/file.json.");
479
+ }
480
+ return parsed;
481
+ }
482
+ async function sendMessageParams(options) {
429
483
  const mailboxId = getString(options, "mailbox-id");
430
484
  const from = getString(options, "from");
431
485
  const to = messageRecipients(options, "to");
@@ -435,6 +489,8 @@ function sendMessageParams(options) {
435
489
  const subject = getRequiredString(options, "subject");
436
490
  const text = getString(options, "text");
437
491
  const html = getString(options, "html");
492
+ const headers = outboundMessageHeaders(options);
493
+ const metadata = await messageMetadata(options);
438
494
  if (!mailboxId && !from) throw new CliUsageError("Provide --mailbox-id or --from.");
439
495
  if (to.length === 0) throw new CliUsageError("Provide at least one --to recipient.");
440
496
  if (!text && !html) throw new CliUsageError("Provide --text or --html.");
@@ -447,7 +503,47 @@ function sendMessageParams(options) {
447
503
  ...replyTo ? { reply_to: replyTo } : {},
448
504
  subject,
449
505
  ...html ? { html } : {},
450
- ...text ? { text } : {}
506
+ ...text ? { text } : {},
507
+ ...getString(options, "client-reference") ? { client_reference: getString(options, "client-reference") } : {},
508
+ ...metadata ? { metadata } : {},
509
+ ...getString(options, "source-rfc-message-id") ? { source_rfc_message_id: getString(options, "source-rfc-message-id") } : {},
510
+ ...headers.length > 0 ? { headers } : {},
511
+ ...getString(options, "scheduled-at") ? { scheduled_at: getString(options, "scheduled-at") } : {},
512
+ ...getString(options, "sandbox-outcome") ? {
513
+ sandbox_outcome: getEnum(options, "sandbox-outcome", [
514
+ "delivered",
515
+ "bounced",
516
+ "complained"
517
+ ])
518
+ } : {}
519
+ };
520
+ }
521
+ async function replyToMessageParams(options) {
522
+ const to = messageRecipients(options, "to");
523
+ const cc = messageRecipients(options, "cc");
524
+ const text = getString(options, "text");
525
+ const html = getString(options, "html");
526
+ const headers = outboundMessageHeaders(options);
527
+ const metadata = await messageMetadata(options);
528
+ if (to.length === 0) throw new CliUsageError("Provide at least one --to recipient.");
529
+ if (!text && !html) throw new CliUsageError("Provide --text or --html.");
530
+ return {
531
+ to,
532
+ ...cc.length > 0 ? { cc } : {},
533
+ ...html ? { html } : {},
534
+ ...text ? { text } : {},
535
+ ...getString(options, "client-reference") ? { client_reference: getString(options, "client-reference") } : {},
536
+ ...metadata ? { metadata } : {},
537
+ ...getString(options, "source-rfc-message-id") ? { source_rfc_message_id: getString(options, "source-rfc-message-id") } : {},
538
+ ...headers.length > 0 ? { headers } : {},
539
+ ...getString(options, "scheduled-at") ? { scheduled_at: getString(options, "scheduled-at") } : {},
540
+ ...getString(options, "sandbox-outcome") ? {
541
+ sandbox_outcome: getEnum(options, "sandbox-outcome", [
542
+ "delivered",
543
+ "bounced",
544
+ "complained"
545
+ ])
546
+ } : {}
451
547
  };
452
548
  }
453
549
  async function runDomains(client, parsed, out) {
@@ -480,6 +576,13 @@ async function runDomains(client, parsed, out) {
480
576
  writeJson(out, await client.domains.get(requirePositional(parsed.positional, 2, "domain id")));
481
577
  return;
482
578
  }
579
+ if (subcommand === "dns-records") {
580
+ writeJson(
581
+ out,
582
+ await client.domains.getDnsRecords(requirePositional(parsed.positional, 2, "domain id"))
583
+ );
584
+ return;
585
+ }
483
586
  if (subcommand === "verify") {
484
587
  writeJson(
485
588
  out,
@@ -510,11 +613,27 @@ async function runMailboxes(client, parsed, out) {
510
613
  }
511
614
  if (subcommand === "create") {
512
615
  const displayName = getString(parsed.options, "display-name");
616
+ const passwordValue = getString(parsed.options, "password");
617
+ const passwordFile = getString(parsed.options, "password-file");
618
+ const generatePassword = getBoolean(parsed.options, "generate-password") === true;
619
+ if ([Boolean(passwordValue), Boolean(passwordFile), generatePassword].filter(Boolean).length > 1) {
620
+ throw new CliUsageError(
621
+ "Pass only one of --password, --password-file, or --generate-password."
622
+ );
623
+ }
624
+ if (!passwordValue && !passwordFile && !generatePassword) {
625
+ throw new CliUsageError("Missing --password, --password-file, or --generate-password.");
626
+ }
627
+ const password = passwordFile ? (await readFile(passwordFile, "utf8")).replace(/\r?\n$/, "") : passwordValue;
628
+ if (!generatePassword && !password) {
629
+ throw new CliUsageError("Mailbox password must not be empty.");
630
+ }
513
631
  writeJson(
514
632
  out,
515
633
  await client.mailboxes.create({
516
634
  domain_id: getRequiredString(parsed.options, "domain-id"),
517
635
  address: getRequiredString(parsed.options, "address"),
636
+ ...generatePassword ? { generate_password: true } : { password },
518
637
  ...displayName ? { display_name: displayName } : {}
519
638
  })
520
639
  );
@@ -527,6 +646,37 @@ async function runMailboxes(client, parsed, out) {
527
646
  );
528
647
  return;
529
648
  }
649
+ if (subcommand === "suspend") {
650
+ writeJson(
651
+ out,
652
+ await client.mailboxes.suspend(requirePositional(parsed.positional, 2, "mailbox id"))
653
+ );
654
+ return;
655
+ }
656
+ if (subcommand === "resume") {
657
+ writeJson(
658
+ out,
659
+ await client.mailboxes.resume(requirePositional(parsed.positional, 2, "mailbox id"))
660
+ );
661
+ return;
662
+ }
663
+ if (subcommand === "export") {
664
+ writeJson(
665
+ out,
666
+ await client.mailboxes.createExport(requirePositional(parsed.positional, 2, "mailbox id"))
667
+ );
668
+ return;
669
+ }
670
+ if (subcommand === "export-status") {
671
+ writeJson(
672
+ out,
673
+ await client.mailboxes.getExport(
674
+ requirePositional(parsed.positional, 2, "mailbox id"),
675
+ requirePositional(parsed.positional, 3, "export id")
676
+ )
677
+ );
678
+ return;
679
+ }
530
680
  if (subcommand === "folders") {
531
681
  writeJson(
532
682
  out,
@@ -548,6 +698,58 @@ async function runMailboxes(client, parsed, out) {
548
698
  );
549
699
  return;
550
700
  }
701
+ if (subcommand === "forwarding") {
702
+ writeJson(
703
+ out,
704
+ await client.mailboxes.listForwarding(requirePositional(parsed.positional, 2, "mailbox id"))
705
+ );
706
+ return;
707
+ }
708
+ if (subcommand === "forwarding-add") {
709
+ writeJson(
710
+ out,
711
+ await client.mailboxes.createForwarding(
712
+ requirePositional(parsed.positional, 2, "mailbox id"),
713
+ { destination: getRequiredString(parsed.options, "destination") }
714
+ )
715
+ );
716
+ return;
717
+ }
718
+ if (subcommand === "forwarding-remove") {
719
+ const mailboxId = requirePositional(parsed.positional, 2, "mailbox id");
720
+ const forwardingId = requirePositional(parsed.positional, 3, "forwarding id");
721
+ await client.mailboxes.deleteForwarding(mailboxId, forwardingId);
722
+ writeJson(out, { ok: true, id: forwardingId });
723
+ return;
724
+ }
725
+ if (subcommand === "sandbox-inbound") {
726
+ const text = getString(parsed.options, "text");
727
+ const html = getString(parsed.options, "html");
728
+ if (!text && !html) throw new CliUsageError("Provide --text or --html.");
729
+ const to = messageRecipients(parsed.options, "to");
730
+ const cc = messageRecipients(parsed.options, "cc");
731
+ const headers = outboundMessageHeaders(parsed.options);
732
+ writeJson(
733
+ out,
734
+ await client.mailboxes.injectSandboxInbound(
735
+ requirePositional(parsed.positional, 2, "mailbox id"),
736
+ {
737
+ from: getRequiredString(parsed.options, "from"),
738
+ ...to.length > 0 ? { to } : {},
739
+ ...cc.length > 0 ? { cc } : {},
740
+ subject: getRequiredString(parsed.options, "subject"),
741
+ ...text ? { text } : {},
742
+ ...html ? { html } : {},
743
+ ...getString(parsed.options, "message-id") ? { message_id: getString(parsed.options, "message-id") } : {},
744
+ ...getString(parsed.options, "in-reply-to") ? { in_reply_to: getString(parsed.options, "in-reply-to") } : {},
745
+ ...getStringList(parsed.options, "reference").length > 0 ? { references: getStringList(parsed.options, "reference") } : {},
746
+ ...getString(parsed.options, "received-at") ? { received_at: getString(parsed.options, "received-at") } : {},
747
+ ...headers.length > 0 ? { headers } : {}
748
+ }
749
+ )
750
+ );
751
+ return;
752
+ }
551
753
  throw new CliUsageError("Unknown mailboxes command.");
552
754
  }
553
755
  async function runInbox(client, parsed, out) {
@@ -616,20 +818,36 @@ async function runInbox(client, parsed, out) {
616
818
  async function runMessages(client, parsed, out) {
617
819
  const subcommand = parsed.positional[1];
618
820
  if (subcommand === "send") {
619
- writeJson(out, await client.messages.send(sendMessageParams(parsed.options)));
821
+ writeJson(out, await client.messages.send(await sendMessageParams(parsed.options)));
620
822
  return;
621
823
  }
622
824
  if (subcommand === "list") {
825
+ const mailboxId = getString(parsed.options, "mailbox-id");
826
+ const clientReference = getString(parsed.options, "client-reference");
827
+ if (!mailboxId && !clientReference) {
828
+ throw new CliUsageError("Provide --mailbox-id or --client-reference.");
829
+ }
830
+ const filter = mailboxId ? { mailbox_id: mailboxId, ...clientReference ? { client_reference: clientReference } : {} } : { client_reference: clientReference };
623
831
  writeJson(
624
832
  out,
625
833
  await client.messages.list({
626
- mailbox_id: getRequiredString(parsed.options, "mailbox-id"),
834
+ ...filter,
627
835
  cursor: getString(parsed.options, "cursor"),
628
836
  limit: getNumber(parsed.options, "limit")
629
837
  })
630
838
  );
631
839
  return;
632
840
  }
841
+ if (subcommand === "reply") {
842
+ writeJson(
843
+ out,
844
+ await client.messages.reply(
845
+ requirePositional(parsed.positional, 2, "message id"),
846
+ await replyToMessageParams(parsed.options)
847
+ )
848
+ );
849
+ return;
850
+ }
633
851
  if (subcommand === "get") {
634
852
  writeJson(
635
853
  out,
@@ -680,6 +898,18 @@ async function runWebhooks(client, parsed, out) {
680
898
  );
681
899
  return;
682
900
  }
901
+ if (subcommand === "replay-delivery") {
902
+ const idempotencyKey = getString(parsed.options, "idempotency-key");
903
+ writeJson(
904
+ out,
905
+ await client.webhooks.replayDelivery(
906
+ requirePositional(parsed.positional, 2, "webhook id"),
907
+ requirePositional(parsed.positional, 3, "delivery id"),
908
+ idempotencyKey ? { idempotencyKey } : void 0
909
+ )
910
+ );
911
+ return;
912
+ }
683
913
  throw new CliUsageError("Unknown webhooks command.");
684
914
  }
685
915
  async function runSuppressions(client, parsed, out) {
@@ -1021,6 +1251,12 @@ function getEnum(options, key, allowed) {
1021
1251
  }
1022
1252
  return match;
1023
1253
  }
1254
+ function getConferencingProvider(options) {
1255
+ const value = getString(options, "conferencing-provider");
1256
+ if (value === void 0) return void 0;
1257
+ if (value === "none") return null;
1258
+ return getEnum(options, "conferencing-provider", CONFERENCING_PROVIDER_IDS);
1259
+ }
1024
1260
  function isReminderArray(value) {
1025
1261
  return Array.isArray(value) && value.every(
1026
1262
  (item) => typeof item === "object" && item !== null && !Array.isArray(item) && Number.isInteger(item.minutes_before)
@@ -1186,6 +1422,7 @@ async function runBookingPages(client, parsed, out) {
1186
1422
  buffer_minutes: getNumber(options, "buffer-minutes"),
1187
1423
  minimum_notice_minutes: getNumber(options, "minimum-notice-minutes"),
1188
1424
  max_advance_days: getNumber(options, "max-advance-days"),
1425
+ conferencing_provider: getConferencingProvider(options),
1189
1426
  active: getBoolean(options, "active")
1190
1427
  };
1191
1428
  writeJson(out, await client.bookingPages.create(params));
@@ -1214,6 +1451,7 @@ async function runBookingPages(client, parsed, out) {
1214
1451
  buffer_minutes: getNumber(options, "buffer-minutes"),
1215
1452
  minimum_notice_minutes: getNumber(options, "minimum-notice-minutes"),
1216
1453
  max_advance_days: getNumber(options, "max-advance-days"),
1454
+ conferencing_provider: getConferencingProvider(options),
1217
1455
  active: getBoolean(options, "active")
1218
1456
  };
1219
1457
  writeJson(out, await client.bookingPages.update(id, params));
@@ -1227,6 +1465,74 @@ async function runBookingPages(client, parsed, out) {
1227
1465
  }
1228
1466
  throw new CliUsageError("Unknown booking-pages command.");
1229
1467
  }
1468
+ async function runPartner(client, parsed, out) {
1469
+ const group = parsed.positional[1];
1470
+ if (group === "usage") {
1471
+ writeJson(out, await client.partner.usage());
1472
+ return;
1473
+ }
1474
+ if (group !== "organizations") throw new CliUsageError("Unknown partner command.");
1475
+ const command = parsed.positional[2];
1476
+ if (command === "list") {
1477
+ writeJson(out, await client.partner.listOrganizations());
1478
+ return;
1479
+ }
1480
+ if (command === "create") {
1481
+ const classification = getString(parsed.options, "data-classification") ?? "customer";
1482
+ if (classification !== "internal_test" && classification !== "customer") {
1483
+ throw new CliUsageError("--data-classification must be internal_test or customer.");
1484
+ }
1485
+ const params = {
1486
+ name: getRequiredString(parsed.options, "name"),
1487
+ external_reference: getRequiredString(parsed.options, "external-reference"),
1488
+ owner_email: getRequiredString(parsed.options, "owner-email"),
1489
+ mailbox_limit: getNumber(parsed.options, "mailbox-limit") ?? 3,
1490
+ data_classification: classification
1491
+ };
1492
+ writeJson(out, await client.partner.createOrganization(params));
1493
+ return;
1494
+ }
1495
+ const id = requirePositional(parsed.positional, 3, "partner organization id");
1496
+ if (command === "get") {
1497
+ writeJson(out, await client.partner.getOrganization(id));
1498
+ return;
1499
+ }
1500
+ if (command === "update") {
1501
+ const name = getString(parsed.options, "name");
1502
+ const mailboxLimit = getNumber(parsed.options, "mailbox-limit");
1503
+ if (name === void 0 && mailboxLimit === void 0) {
1504
+ throw new CliUsageError("Provide --name or --mailbox-limit.");
1505
+ }
1506
+ const params = {
1507
+ ...name !== void 0 ? { name } : {},
1508
+ ...mailboxLimit !== void 0 ? { mailbox_limit: mailboxLimit } : {}
1509
+ };
1510
+ writeJson(out, await client.partner.updateOrganization(id, params));
1511
+ return;
1512
+ }
1513
+ if (command === "invite") {
1514
+ writeJson(
1515
+ out,
1516
+ await client.partner.resendOwnershipInvitation(id, {
1517
+ owner_email: getString(parsed.options, "owner-email")
1518
+ })
1519
+ );
1520
+ return;
1521
+ }
1522
+ if (command === "suspend") {
1523
+ writeJson(out, await client.partner.suspendOrganization(id));
1524
+ return;
1525
+ }
1526
+ if (command === "resume") {
1527
+ writeJson(out, await client.partner.resumeOrganization(id));
1528
+ return;
1529
+ }
1530
+ if (command === "offboard") {
1531
+ writeJson(out, await client.partner.offboardOrganization(id));
1532
+ return;
1533
+ }
1534
+ throw new CliUsageError("Unknown partner organizations command.");
1535
+ }
1230
1536
  function writeError(err, errOut) {
1231
1537
  if (err instanceof CliUsageError) {
1232
1538
  errOut.write(`Error: ${err.message}
@@ -1292,6 +1598,7 @@ async function runCli(argv, options = {}) {
1292
1598
  else if (command === "audiences") await runAudiences(client, parsed, out);
1293
1599
  else if (command === "calendar") await runCalendar(client, parsed, out);
1294
1600
  else if (command === "booking-pages") await runBookingPages(client, parsed, out);
1601
+ else if (command === "partner") await runPartner(client, parsed, out);
1295
1602
  else throw new CliUsageError(`Unknown command: ${command}.`);
1296
1603
  return 0;
1297
1604
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipmail-cli",
3
- "version": "0.1.8",
3
+ "version": "0.1.10",
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.31"
44
+ "shipmail": "0.1.33"
45
45
  },
46
46
  "devDependencies": {
47
47
  "tsup": "^8.4.0",