shipmail-cli 0.2.2 → 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 -4
  2. package/dist/cli.js +98 -9
  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"
@@ -117,9 +141,6 @@ shipmail api GET /domains
117
141
 
118
142
  shipmail api PATCH /mailboxes/mbx_123/spam-filter \
119
143
  --data '{"threshold":6}'
120
-
121
- shipmail api PUT /mailboxes/mbx_123/rules \
122
- --data @rules.json
123
144
  ```
124
145
 
125
146
  Use repeatable `--header "Name: value"` flags for request headers. Use `--output path` to write non-JSON responses, such as attachment downloads.
@@ -129,10 +150,11 @@ Use repeatable `--header "Name: value"` flags for request headers. Use `--output
129
150
  - `status`
130
151
  - `api <METHOD> <PATH>`
131
152
  - `domains list|search|create|get|verify|delete`
132
- - `mailboxes list|create|get|suspend|resume|forwarding|forwarding-add|forwarding-remove|app-passwords|folders|identities|rules`
153
+ - `mailboxes list|create|get|suspend|resume|forwarding|forwarding-add|forwarding-remove|app-passwords|folders|identities|stage-attachment|prepare-attachment-upload`
133
154
  - `inbox list|message|threads|thread|thread-state|draft|send-draft|reply-message|reply-thread|update|move`
134
155
  - `reply-scans create|status|results`
135
156
  - `messages send|list|get|reply` (test keys also support `--sandbox-outcome`)
157
+ - `scheduled-messages list|get|update|cancel`
136
158
  - `mailboxes sandbox-inbound`
137
159
  - `webhooks list|create|test|deliveries|replay-delivery`
138
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.2.2";
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>
@@ -335,10 +337,11 @@ Usage:
335
337
  shipmail mailboxes app-passwords revoke <id> <app-password-id>
336
338
  shipmail mailboxes folders <id>
337
339
  shipmail mailboxes identities <id>
338
- shipmail mailboxes rules <id>
339
340
  shipmail mailboxes forwarding <id>
340
341
  shipmail mailboxes forwarding-add <id> --destination owner@example.com
341
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]
342
345
  shipmail mailboxes sandbox-inbound <id> --from sender@example.com --subject "Test" (--text "Hello" | --html "<p>Hello</p>") [--to support@example.com]
343
346
  shipmail inbox list <mailbox-id> [--folder-role inbox] [--after ISO] [--before ISO] [--limit 25] [--cursor cur]
344
347
  shipmail inbox message <mailbox-id> <message-id>
@@ -355,6 +358,10 @@ Usage:
355
358
  shipmail messages list (--mailbox-id mbx | --client-reference crm-123) [--limit 25] [--cursor cur]
356
359
  shipmail messages get <message-id>
357
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>
358
365
  shipmail reply-scans create --mailbox-id mbx --after ISO [--before ISO]
359
366
  shipmail reply-scans status <scan-id>
360
367
  shipmail reply-scans results <scan-id> [--limit 25] [--cursor cur]
@@ -527,6 +534,7 @@ async function sendMessageParams(options) {
527
534
  ...metadata ? { metadata } : {},
528
535
  ...getString(options, "source-rfc-message-id") ? { source_rfc_message_id: getString(options, "source-rfc-message-id") } : {},
529
536
  ...headers.length > 0 ? { headers } : {},
537
+ ...getStringList(options, "staged-attachment-id").length > 0 ? { staged_attachment_ids: getStringList(options, "staged-attachment-id") } : {},
530
538
  ...getString(options, "scheduled-at") ? { scheduled_at: getString(options, "scheduled-at") } : {},
531
539
  ...getString(options, "sandbox-outcome") ? {
532
540
  sandbox_outcome: getEnum(options, "sandbox-outcome", [
@@ -736,13 +744,6 @@ async function runMailboxes(client, parsed, out) {
736
744
  );
737
745
  return;
738
746
  }
739
- if (subcommand === "rules") {
740
- writeJson(
741
- out,
742
- await client.mailboxes.getRules(requirePositional(parsed.positional, 2, "mailbox id"))
743
- );
744
- return;
745
- }
746
747
  if (subcommand === "forwarding") {
747
748
  writeJson(
748
749
  out,
@@ -767,6 +768,35 @@ async function runMailboxes(client, parsed, out) {
767
768
  writeJson(out, { ok: true, id: forwardingId });
768
769
  return;
769
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
+ }
770
800
  if (subcommand === "sandbox-inbound") {
771
801
  const text = getString(parsed.options, "text");
772
802
  const html = getString(parsed.options, "html");
@@ -1045,6 +1075,52 @@ async function runMessages(client, parsed, out) {
1045
1075
  }
1046
1076
  throw new CliUsageError("Unknown messages command.");
1047
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
+ }
1048
1124
  async function runWebhooks(client, parsed, out) {
1049
1125
  const subcommand = parsed.positional[1];
1050
1126
  if (subcommand === "list") {
@@ -1787,6 +1863,18 @@ async function runCli(argv, options = {}) {
1787
1863
  await writeRawResponse(parsed, { stdout: out }, response);
1788
1864
  return 0;
1789
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
+ }
1790
1878
  if (command === "api") {
1791
1879
  const method = requirePositional(parsed.positional, 1, "method").toUpperCase();
1792
1880
  const path = apiPath(requirePositional(parsed.positional, 2, "path"));
@@ -1800,6 +1888,7 @@ async function runCli(argv, options = {}) {
1800
1888
  else if (command === "mailboxes") await runMailboxes(client, parsed, out);
1801
1889
  else if (command === "inbox") await runInbox(client, parsed, out);
1802
1890
  else if (command === "messages") await runMessages(client, parsed, out);
1891
+ else if (command === "scheduled-messages") await runScheduledMessages(client, parsed, out);
1803
1892
  else if (command === "reply-scans") await runReplyScans(client, parsed, out);
1804
1893
  else if (command === "webhooks") await runWebhooks(client, parsed, out);
1805
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.2.2",
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.2.3"
44
+ "shipmail": "0.4.1"
45
45
  },
46
46
  "devDependencies": {
47
47
  "tsup": "^8.4.0",