shipmail-cli 0.3.0 → 0.4.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.
Files changed (3) hide show
  1. package/README.md +26 -1
  2. package/dist/cli.js +98 -1
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -51,6 +51,30 @@ shipmail messages send \
51
51
  shipmail messages list --client-reference crm-123
52
52
  shipmail messages reply msg_123 --to user@example.com --text "Thanks"
53
53
 
54
+ # Stage a raw file, then pass the returned sat_... ID when sending
55
+ shipmail mailboxes stage-attachment mbx_123 ./invoice.pdf --content-type application/pdf
56
+
57
+ # Prepare a five-minute, single-use upload URL for a browser-hosted component
58
+ shipmail mailboxes prepare-attachment-upload mbx_123 ./invoice.pdf --content-type application/pdf
59
+
60
+ shipmail messages send \
61
+ --mailbox-id mbx_123 \
62
+ --to customer@example.com \
63
+ --subject "Invoice" \
64
+ --text "Attached." \
65
+ --staged-attachment-id sat_123 \
66
+ --scheduled-at 2026-08-01T08:00:00.000Z
67
+
68
+ shipmail scheduled-messages list
69
+ shipmail scheduled-messages get msg_123
70
+ shipmail scheduled-messages update msg_123 \
71
+ --to customer@example.com \
72
+ --subject "Updated invoice" \
73
+ --text "Attached." \
74
+ --staged-attachment-id sat_123 \
75
+ --scheduled-at 2026-08-02T08:00:00.000Z
76
+ shipmail scheduled-messages cancel msg_123
77
+
54
78
  # Use an sm_test_... key to simulate an outbound result and fake inbound reply
55
79
  shipmail messages send --mailbox-id mbx_123 --to user@example.com --subject "Test" --text "Not delivered" --sandbox-outcome bounced
56
80
  shipmail mailboxes sandbox-inbound mbx_123 --from customer@example.com --subject "Re: Test" --text "Fake inbound reply"
@@ -126,10 +150,11 @@ Use repeatable `--header "Name: value"` flags for request headers. Use `--output
126
150
  - `status`
127
151
  - `api <METHOD> <PATH>`
128
152
  - `domains list|search|create|get|verify|delete`
129
- - `mailboxes list|create|get|suspend|resume|forwarding|forwarding-add|forwarding-remove|app-passwords|folders|identities`
153
+ - `mailboxes list|create|get|suspend|resume|forwarding|forwarding-add|forwarding-remove|app-passwords|folders|identities|stage-attachment|prepare-attachment-upload`
130
154
  - `inbox list|message|threads|thread|thread-state|draft|send-draft|reply-message|reply-thread|update|move`
131
155
  - `reply-scans create|status|results`
132
156
  - `messages send|list|get|reply` (test keys also support `--sandbox-outcome`)
157
+ - `scheduled-messages list|get|update|cancel`
133
158
  - `mailboxes sandbox-inbound`
134
159
  - `webhooks list|create|test|deliveries|replay-delivery`
135
160
  - `suppressions list|remove`
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
+ import { createHash } from "crypto";
4
5
  import { realpathSync } from "fs";
5
6
  import { readFile, writeFile } from "fs/promises";
6
7
  import { env as processEnv, exit, stderr, stdout } from "process";
@@ -13,7 +14,7 @@ import {
13
14
  } from "shipmail";
14
15
 
15
16
  // src/version.ts
16
- var VERSION = "0.3.0";
17
+ var VERSION = "0.4.0";
17
18
 
18
19
  // src/cli.ts
19
20
  var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
@@ -315,6 +316,7 @@ function usage() {
315
316
 
316
317
  Usage:
317
318
  shipmail status
319
+ shipmail capabilities
318
320
  shipmail api <METHOD> <PATH> [--data JSON|@file] [--output file]
319
321
  shipmail domains list [--limit 25] [--cursor cur]
320
322
  shipmail domains search <keyword>
@@ -338,6 +340,8 @@ Usage:
338
340
  shipmail mailboxes forwarding <id>
339
341
  shipmail mailboxes forwarding-add <id> --destination owner@example.com
340
342
  shipmail mailboxes forwarding-remove <id> <forwarding-id>
343
+ shipmail mailboxes prepare-attachment-upload <id> <file> [--filename name] [--content-type type]
344
+ shipmail mailboxes stage-attachment <id> <file> [--filename name] [--content-type type]
341
345
  shipmail mailboxes sandbox-inbound <id> --from sender@example.com --subject "Test" (--text "Hello" | --html "<p>Hello</p>") [--to support@example.com]
342
346
  shipmail inbox list <mailbox-id> [--folder-role inbox] [--after ISO] [--before ISO] [--limit 25] [--cursor cur]
343
347
  shipmail inbox message <mailbox-id> <message-id>
@@ -354,6 +358,10 @@ Usage:
354
358
  shipmail messages list (--mailbox-id mbx | --client-reference crm-123) [--limit 25] [--cursor cur]
355
359
  shipmail messages get <message-id>
356
360
  shipmail messages reply <message-id> --to user@example.com (--text "Hello" | --html "<p>Hello</p>") [--client-reference crm-reply-123]
361
+ shipmail scheduled-messages list [--include-held] [--search text] [--limit 100]
362
+ shipmail scheduled-messages get <message-id>
363
+ shipmail scheduled-messages update <message-id> --to user@example.com --subject "Hi" --scheduled-at ISO (--text "Hello" | --html "<p>Hello</p>") [--staged-attachment-id sat_...]
364
+ shipmail scheduled-messages cancel <message-id>
357
365
  shipmail reply-scans create --mailbox-id mbx --after ISO [--before ISO]
358
366
  shipmail reply-scans status <scan-id>
359
367
  shipmail reply-scans results <scan-id> [--limit 25] [--cursor cur]
@@ -526,6 +534,7 @@ async function sendMessageParams(options) {
526
534
  ...metadata ? { metadata } : {},
527
535
  ...getString(options, "source-rfc-message-id") ? { source_rfc_message_id: getString(options, "source-rfc-message-id") } : {},
528
536
  ...headers.length > 0 ? { headers } : {},
537
+ ...getStringList(options, "staged-attachment-id").length > 0 ? { staged_attachment_ids: getStringList(options, "staged-attachment-id") } : {},
529
538
  ...getString(options, "scheduled-at") ? { scheduled_at: getString(options, "scheduled-at") } : {},
530
539
  ...getString(options, "sandbox-outcome") ? {
531
540
  sandbox_outcome: getEnum(options, "sandbox-outcome", [
@@ -759,6 +768,35 @@ async function runMailboxes(client, parsed, out) {
759
768
  writeJson(out, { ok: true, id: forwardingId });
760
769
  return;
761
770
  }
771
+ if (subcommand === "stage-attachment") {
772
+ const mailboxId = requirePositional(parsed.positional, 2, "mailbox id");
773
+ const filePath = requirePositional(parsed.positional, 3, "file");
774
+ const data = await readFile(realpathSync(filePath));
775
+ writeJson(
776
+ out,
777
+ await client.mailboxes.stageAttachment(mailboxId, {
778
+ filename: getString(parsed.options, "filename") ?? filePath.split(/[\\/]/).at(-1) ?? "attachment",
779
+ data,
780
+ content_type: getString(parsed.options, "content-type")
781
+ })
782
+ );
783
+ return;
784
+ }
785
+ if (subcommand === "prepare-attachment-upload") {
786
+ const mailboxId = requirePositional(parsed.positional, 2, "mailbox id");
787
+ const filePath = requirePositional(parsed.positional, 3, "file");
788
+ const data = await readFile(realpathSync(filePath));
789
+ writeJson(
790
+ out,
791
+ await client.mailboxes.prepareStagedAttachmentUpload(mailboxId, {
792
+ filename: getString(parsed.options, "filename") ?? filePath.split(/[\\/]/).at(-1) ?? "attachment",
793
+ content_type: getString(parsed.options, "content-type") ?? "application/octet-stream",
794
+ size: data.byteLength,
795
+ sha256: createHash("sha256").update(data).digest("hex")
796
+ })
797
+ );
798
+ return;
799
+ }
762
800
  if (subcommand === "sandbox-inbound") {
763
801
  const text = getString(parsed.options, "text");
764
802
  const html = getString(parsed.options, "html");
@@ -1037,6 +1075,52 @@ async function runMessages(client, parsed, out) {
1037
1075
  }
1038
1076
  throw new CliUsageError("Unknown messages command.");
1039
1077
  }
1078
+ async function runScheduledMessages(client, parsed, out) {
1079
+ const subcommand = parsed.positional[1];
1080
+ if (subcommand === "list") {
1081
+ writeJson(
1082
+ out,
1083
+ await client.scheduledMessages.list({
1084
+ include_held: getBoolean(parsed.options, "include-held"),
1085
+ search: getString(parsed.options, "search"),
1086
+ limit: getNumber(parsed.options, "limit")
1087
+ })
1088
+ );
1089
+ return;
1090
+ }
1091
+ const messageId = requirePositional(parsed.positional, 2, "message id");
1092
+ if (subcommand === "get") {
1093
+ writeJson(out, await client.scheduledMessages.get(messageId));
1094
+ return;
1095
+ }
1096
+ if (subcommand === "cancel") {
1097
+ await client.scheduledMessages.cancel(messageId);
1098
+ writeJson(out, { ok: true, id: messageId });
1099
+ return;
1100
+ }
1101
+ if (subcommand === "update") {
1102
+ const to = messageRecipients(parsed.options, "to");
1103
+ const cc = messageRecipients(parsed.options, "cc");
1104
+ const bcc = messageRecipients(parsed.options, "bcc");
1105
+ const text = getString(parsed.options, "text");
1106
+ const html = getString(parsed.options, "html");
1107
+ if (to.length === 0) throw new CliUsageError("Provide at least one --to recipient.");
1108
+ if (!text && !html) throw new CliUsageError("Provide --text or --html.");
1109
+ const update = {
1110
+ to,
1111
+ ...cc.length > 0 ? { cc } : {},
1112
+ ...bcc.length > 0 ? { bcc } : {},
1113
+ subject: getRequiredString(parsed.options, "subject"),
1114
+ ...text ? { text } : {},
1115
+ ...html ? { html } : {},
1116
+ ...getStringList(parsed.options, "staged-attachment-id").length > 0 ? { staged_attachment_ids: getStringList(parsed.options, "staged-attachment-id") } : {},
1117
+ scheduled_at: getRequiredString(parsed.options, "scheduled-at")
1118
+ };
1119
+ writeJson(out, await client.scheduledMessages.update(messageId, update));
1120
+ return;
1121
+ }
1122
+ throw new CliUsageError("Unknown scheduled-messages command.");
1123
+ }
1040
1124
  async function runWebhooks(client, parsed, out) {
1041
1125
  const subcommand = parsed.positional[1];
1042
1126
  if (subcommand === "list") {
@@ -1779,6 +1863,18 @@ async function runCli(argv, options = {}) {
1779
1863
  await writeRawResponse(parsed, { stdout: out }, response);
1780
1864
  return 0;
1781
1865
  }
1866
+ if (command === "capabilities") {
1867
+ const response = await rawApiRequest(
1868
+ parsed,
1869
+ options,
1870
+ "GET",
1871
+ "/capabilities",
1872
+ void 0,
1873
+ true
1874
+ );
1875
+ await writeRawResponse(parsed, { stdout: out }, response);
1876
+ return 0;
1877
+ }
1782
1878
  if (command === "api") {
1783
1879
  const method = requirePositional(parsed.positional, 1, "method").toUpperCase();
1784
1880
  const path = apiPath(requirePositional(parsed.positional, 2, "path"));
@@ -1792,6 +1888,7 @@ async function runCli(argv, options = {}) {
1792
1888
  else if (command === "mailboxes") await runMailboxes(client, parsed, out);
1793
1889
  else if (command === "inbox") await runInbox(client, parsed, out);
1794
1890
  else if (command === "messages") await runMessages(client, parsed, out);
1891
+ else if (command === "scheduled-messages") await runScheduledMessages(client, parsed, out);
1795
1892
  else if (command === "reply-scans") await runReplyScans(client, parsed, out);
1796
1893
  else if (command === "webhooks") await runWebhooks(client, parsed, out);
1797
1894
  else if (command === "suppressions") await runSuppressions(client, parsed, out);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipmail-cli",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
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.3.0"
44
+ "shipmail": "0.4.1"
45
45
  },
46
46
  "devDependencies": {
47
47
  "tsup": "^8.4.0",