shipmail-cli 0.3.0 → 0.4.1
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 +27 -1
- package/dist/cli.js +124 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ shipmail mailboxes resume mbx_123
|
|
|
34
34
|
shipmail mailboxes forwarding-add mbx_123 --destination owner@example.net
|
|
35
35
|
shipmail mailboxes forwarding mbx_123
|
|
36
36
|
shipmail mailboxes forwarding-remove mbx_123 fwd_123
|
|
37
|
+
shipmail mailboxes rules mbx_123
|
|
37
38
|
shipmail mailboxes app-passwords list mbx_123
|
|
38
39
|
shipmail mailboxes app-passwords create mbx_123 --name "Desktop mail" --expires-at 2026-10-01T00:00:00Z
|
|
39
40
|
shipmail mailboxes app-passwords revoke mbx_123 appwd_123
|
|
@@ -51,6 +52,30 @@ shipmail messages send \
|
|
|
51
52
|
shipmail messages list --client-reference crm-123
|
|
52
53
|
shipmail messages reply msg_123 --to user@example.com --text "Thanks"
|
|
53
54
|
|
|
55
|
+
# Stage a raw file, then pass the returned sat_... ID when sending
|
|
56
|
+
shipmail mailboxes stage-attachment mbx_123 ./invoice.pdf --content-type application/pdf
|
|
57
|
+
|
|
58
|
+
# Prepare a five-minute, single-use upload URL for a browser-hosted component
|
|
59
|
+
shipmail mailboxes prepare-attachment-upload mbx_123 ./invoice.pdf --content-type application/pdf
|
|
60
|
+
|
|
61
|
+
shipmail messages send \
|
|
62
|
+
--mailbox-id mbx_123 \
|
|
63
|
+
--to customer@example.com \
|
|
64
|
+
--subject "Invoice" \
|
|
65
|
+
--text "Attached." \
|
|
66
|
+
--staged-attachment-id sat_123 \
|
|
67
|
+
--scheduled-at 2026-08-01T08:00:00.000Z
|
|
68
|
+
|
|
69
|
+
shipmail scheduled-messages list
|
|
70
|
+
shipmail scheduled-messages get msg_123
|
|
71
|
+
shipmail scheduled-messages update msg_123 \
|
|
72
|
+
--to customer@example.com \
|
|
73
|
+
--subject "Updated invoice" \
|
|
74
|
+
--text "Attached." \
|
|
75
|
+
--staged-attachment-id sat_123 \
|
|
76
|
+
--scheduled-at 2026-08-02T08:00:00.000Z
|
|
77
|
+
shipmail scheduled-messages cancel msg_123
|
|
78
|
+
|
|
54
79
|
# Use an sm_test_... key to simulate an outbound result and fake inbound reply
|
|
55
80
|
shipmail messages send --mailbox-id mbx_123 --to user@example.com --subject "Test" --text "Not delivered" --sandbox-outcome bounced
|
|
56
81
|
shipmail mailboxes sandbox-inbound mbx_123 --from customer@example.com --subject "Re: Test" --text "Fake inbound reply"
|
|
@@ -126,10 +151,11 @@ Use repeatable `--header "Name: value"` flags for request headers. Use `--output
|
|
|
126
151
|
- `status`
|
|
127
152
|
- `api <METHOD> <PATH>`
|
|
128
153
|
- `domains list|search|create|get|verify|delete`
|
|
129
|
-
- `mailboxes list|create|get|suspend|resume|forwarding|forwarding-add|forwarding-remove|app-passwords|folders|identities`
|
|
154
|
+
- `mailboxes list|create|get|suspend|resume|forwarding|forwarding-add|forwarding-remove|app-passwords|folders|identities|stage-attachment|prepare-attachment-upload`
|
|
130
155
|
- `inbox list|message|threads|thread|thread-state|draft|send-draft|reply-message|reply-thread|update|move`
|
|
131
156
|
- `reply-scans create|status|results`
|
|
132
157
|
- `messages send|list|get|reply` (test keys also support `--sandbox-outcome`)
|
|
158
|
+
- `scheduled-messages list|get|update|cancel`
|
|
133
159
|
- `mailboxes sandbox-inbound`
|
|
134
160
|
- `webhooks list|create|test|deliveries|replay-delivery`
|
|
135
161
|
- `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.
|
|
17
|
+
var VERSION = "0.4.1";
|
|
17
18
|
|
|
18
19
|
// src/cli.ts
|
|
19
20
|
var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
|
|
@@ -178,6 +179,12 @@ function parseArchiveVisibility(options) {
|
|
|
178
179
|
if (value === "private" || value === "public") return value;
|
|
179
180
|
throw new CliUsageError("--archive-visibility must be private or public.");
|
|
180
181
|
}
|
|
182
|
+
function parseStylingMode(options) {
|
|
183
|
+
const value = getString(options, "styling-mode");
|
|
184
|
+
if (value === void 0) return void 0;
|
|
185
|
+
if (value === "styled" || value === "plain") return value;
|
|
186
|
+
throw new CliUsageError("--styling-mode must be styled or plain.");
|
|
187
|
+
}
|
|
181
188
|
async function newsletterCreateParams(options) {
|
|
182
189
|
const audience_id = getRequiredString(options, "audience-id");
|
|
183
190
|
const newsletter_domain_id = getRequiredString(options, "newsletter-domain-id");
|
|
@@ -194,6 +201,7 @@ async function newsletterCreateParams(options) {
|
|
|
194
201
|
}
|
|
195
202
|
const send_window_hours = getNumber(options, "send-window-hours");
|
|
196
203
|
const archive_visibility = parseArchiveVisibility(options);
|
|
204
|
+
const styling_mode = parseStylingMode(options);
|
|
197
205
|
const preview_text = getString(options, "preview-text");
|
|
198
206
|
const from_name = getString(options, "from-name");
|
|
199
207
|
return {
|
|
@@ -207,7 +215,8 @@ async function newsletterCreateParams(options) {
|
|
|
207
215
|
...body_html !== void 0 ? { body_html } : {},
|
|
208
216
|
...body_text !== void 0 ? { body_text } : {},
|
|
209
217
|
...send_window_hours !== void 0 ? { send_window_hours } : {},
|
|
210
|
-
...archive_visibility !== void 0 ? { archive_visibility } : {}
|
|
218
|
+
...archive_visibility !== void 0 ? { archive_visibility } : {},
|
|
219
|
+
...styling_mode !== void 0 ? { styling_mode } : {}
|
|
211
220
|
};
|
|
212
221
|
}
|
|
213
222
|
async function newsletterUpdateParams(options) {
|
|
@@ -219,6 +228,7 @@ async function newsletterUpdateParams(options) {
|
|
|
219
228
|
const from_name = getString(options, "from-name");
|
|
220
229
|
const send_window_hours = getNumber(options, "send-window-hours");
|
|
221
230
|
const archive_visibility = parseArchiveVisibility(options);
|
|
231
|
+
const styling_mode = parseStylingMode(options);
|
|
222
232
|
const blocksInput = getString(options, "blocks");
|
|
223
233
|
const bodyHtmlInput = getString(options, "body-html");
|
|
224
234
|
const bodyTextInput = getString(options, "body-text");
|
|
@@ -236,7 +246,8 @@ async function newsletterUpdateParams(options) {
|
|
|
236
246
|
...body_html !== void 0 ? { body_html } : {},
|
|
237
247
|
...body_text !== void 0 ? { body_text } : {},
|
|
238
248
|
...send_window_hours !== void 0 ? { send_window_hours } : {},
|
|
239
|
-
...archive_visibility !== void 0 ? { archive_visibility } : {}
|
|
249
|
+
...archive_visibility !== void 0 ? { archive_visibility } : {},
|
|
250
|
+
...styling_mode !== void 0 ? { styling_mode } : {}
|
|
240
251
|
};
|
|
241
252
|
if (Object.keys(params).length === 0) {
|
|
242
253
|
throw new CliUsageError("Provide at least one field to update.");
|
|
@@ -315,6 +326,7 @@ function usage() {
|
|
|
315
326
|
|
|
316
327
|
Usage:
|
|
317
328
|
shipmail status
|
|
329
|
+
shipmail capabilities
|
|
318
330
|
shipmail api <METHOD> <PATH> [--data JSON|@file] [--output file]
|
|
319
331
|
shipmail domains list [--limit 25] [--cursor cur]
|
|
320
332
|
shipmail domains search <keyword>
|
|
@@ -335,9 +347,12 @@ Usage:
|
|
|
335
347
|
shipmail mailboxes app-passwords revoke <id> <app-password-id>
|
|
336
348
|
shipmail mailboxes folders <id>
|
|
337
349
|
shipmail mailboxes identities <id>
|
|
350
|
+
shipmail mailboxes rules <id>
|
|
338
351
|
shipmail mailboxes forwarding <id>
|
|
339
352
|
shipmail mailboxes forwarding-add <id> --destination owner@example.com
|
|
340
353
|
shipmail mailboxes forwarding-remove <id> <forwarding-id>
|
|
354
|
+
shipmail mailboxes prepare-attachment-upload <id> <file> [--filename name] [--content-type type]
|
|
355
|
+
shipmail mailboxes stage-attachment <id> <file> [--filename name] [--content-type type]
|
|
341
356
|
shipmail mailboxes sandbox-inbound <id> --from sender@example.com --subject "Test" (--text "Hello" | --html "<p>Hello</p>") [--to support@example.com]
|
|
342
357
|
shipmail inbox list <mailbox-id> [--folder-role inbox] [--after ISO] [--before ISO] [--limit 25] [--cursor cur]
|
|
343
358
|
shipmail inbox message <mailbox-id> <message-id>
|
|
@@ -354,6 +369,10 @@ Usage:
|
|
|
354
369
|
shipmail messages list (--mailbox-id mbx | --client-reference crm-123) [--limit 25] [--cursor cur]
|
|
355
370
|
shipmail messages get <message-id>
|
|
356
371
|
shipmail messages reply <message-id> --to user@example.com (--text "Hello" | --html "<p>Hello</p>") [--client-reference crm-reply-123]
|
|
372
|
+
shipmail scheduled-messages list [--include-held] [--search text] [--limit 100]
|
|
373
|
+
shipmail scheduled-messages get <message-id>
|
|
374
|
+
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_...]
|
|
375
|
+
shipmail scheduled-messages cancel <message-id>
|
|
357
376
|
shipmail reply-scans create --mailbox-id mbx --after ISO [--before ISO]
|
|
358
377
|
shipmail reply-scans status <scan-id>
|
|
359
378
|
shipmail reply-scans results <scan-id> [--limit 25] [--cursor cur]
|
|
@@ -367,8 +386,9 @@ Usage:
|
|
|
367
386
|
--newsletter-domain-id nwsdom_456 \\
|
|
368
387
|
--name "July changelog" \\
|
|
369
388
|
--subject "What shipped in July" \\
|
|
370
|
-
--blocks @blocks.json
|
|
371
|
-
|
|
389
|
+
--blocks @blocks.json \\
|
|
390
|
+
[--styling-mode styled|plain]
|
|
391
|
+
shipmail newsletters update nws_789 --subject "Updated subject" --blocks @blocks-v2.json [--styling-mode styled|plain]
|
|
372
392
|
shipmail newsletters preview <id>
|
|
373
393
|
shipmail newsletters preflight <id>
|
|
374
394
|
shipmail newsletters test-send <id> --recipient-email owner@example.com
|
|
@@ -419,6 +439,9 @@ Booking page conferencing:
|
|
|
419
439
|
--conferencing-provider <zoom|google_meet> Create a meeting for every booking.
|
|
420
440
|
--conferencing-provider none Stop creating meeting links on update.
|
|
421
441
|
|
|
442
|
+
Newsletter styling:
|
|
443
|
+
--styling-mode <styled|plain> styled applies Shipmail's email theme. plain sends your HTML without injected styles, width, or centering, so the reader's email client styles it.
|
|
444
|
+
|
|
422
445
|
Global options:
|
|
423
446
|
--api-key <key> API key. Defaults to SHIPMAIL_API_KEY.
|
|
424
447
|
--base-url <url> API base URL. Defaults to SHIPMAIL_BASE_URL or ${DEFAULT_BASE_URL}.
|
|
@@ -526,6 +549,7 @@ async function sendMessageParams(options) {
|
|
|
526
549
|
...metadata ? { metadata } : {},
|
|
527
550
|
...getString(options, "source-rfc-message-id") ? { source_rfc_message_id: getString(options, "source-rfc-message-id") } : {},
|
|
528
551
|
...headers.length > 0 ? { headers } : {},
|
|
552
|
+
...getStringList(options, "staged-attachment-id").length > 0 ? { staged_attachment_ids: getStringList(options, "staged-attachment-id") } : {},
|
|
529
553
|
...getString(options, "scheduled-at") ? { scheduled_at: getString(options, "scheduled-at") } : {},
|
|
530
554
|
...getString(options, "sandbox-outcome") ? {
|
|
531
555
|
sandbox_outcome: getEnum(options, "sandbox-outcome", [
|
|
@@ -735,6 +759,13 @@ async function runMailboxes(client, parsed, out) {
|
|
|
735
759
|
);
|
|
736
760
|
return;
|
|
737
761
|
}
|
|
762
|
+
if (subcommand === "rules") {
|
|
763
|
+
writeJson(
|
|
764
|
+
out,
|
|
765
|
+
await client.mailboxes.getRules(requirePositional(parsed.positional, 2, "mailbox id"))
|
|
766
|
+
);
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
738
769
|
if (subcommand === "forwarding") {
|
|
739
770
|
writeJson(
|
|
740
771
|
out,
|
|
@@ -759,6 +790,35 @@ async function runMailboxes(client, parsed, out) {
|
|
|
759
790
|
writeJson(out, { ok: true, id: forwardingId });
|
|
760
791
|
return;
|
|
761
792
|
}
|
|
793
|
+
if (subcommand === "stage-attachment") {
|
|
794
|
+
const mailboxId = requirePositional(parsed.positional, 2, "mailbox id");
|
|
795
|
+
const filePath = requirePositional(parsed.positional, 3, "file");
|
|
796
|
+
const data = await readFile(realpathSync(filePath));
|
|
797
|
+
writeJson(
|
|
798
|
+
out,
|
|
799
|
+
await client.mailboxes.stageAttachment(mailboxId, {
|
|
800
|
+
filename: getString(parsed.options, "filename") ?? filePath.split(/[\\/]/).at(-1) ?? "attachment",
|
|
801
|
+
data,
|
|
802
|
+
content_type: getString(parsed.options, "content-type")
|
|
803
|
+
})
|
|
804
|
+
);
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
if (subcommand === "prepare-attachment-upload") {
|
|
808
|
+
const mailboxId = requirePositional(parsed.positional, 2, "mailbox id");
|
|
809
|
+
const filePath = requirePositional(parsed.positional, 3, "file");
|
|
810
|
+
const data = await readFile(realpathSync(filePath));
|
|
811
|
+
writeJson(
|
|
812
|
+
out,
|
|
813
|
+
await client.mailboxes.prepareStagedAttachmentUpload(mailboxId, {
|
|
814
|
+
filename: getString(parsed.options, "filename") ?? filePath.split(/[\\/]/).at(-1) ?? "attachment",
|
|
815
|
+
content_type: getString(parsed.options, "content-type") ?? "application/octet-stream",
|
|
816
|
+
size: data.byteLength,
|
|
817
|
+
sha256: createHash("sha256").update(data).digest("hex")
|
|
818
|
+
})
|
|
819
|
+
);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
762
822
|
if (subcommand === "sandbox-inbound") {
|
|
763
823
|
const text = getString(parsed.options, "text");
|
|
764
824
|
const html = getString(parsed.options, "html");
|
|
@@ -1037,6 +1097,52 @@ async function runMessages(client, parsed, out) {
|
|
|
1037
1097
|
}
|
|
1038
1098
|
throw new CliUsageError("Unknown messages command.");
|
|
1039
1099
|
}
|
|
1100
|
+
async function runScheduledMessages(client, parsed, out) {
|
|
1101
|
+
const subcommand = parsed.positional[1];
|
|
1102
|
+
if (subcommand === "list") {
|
|
1103
|
+
writeJson(
|
|
1104
|
+
out,
|
|
1105
|
+
await client.scheduledMessages.list({
|
|
1106
|
+
include_held: getBoolean(parsed.options, "include-held"),
|
|
1107
|
+
search: getString(parsed.options, "search"),
|
|
1108
|
+
limit: getNumber(parsed.options, "limit")
|
|
1109
|
+
})
|
|
1110
|
+
);
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
const messageId = requirePositional(parsed.positional, 2, "message id");
|
|
1114
|
+
if (subcommand === "get") {
|
|
1115
|
+
writeJson(out, await client.scheduledMessages.get(messageId));
|
|
1116
|
+
return;
|
|
1117
|
+
}
|
|
1118
|
+
if (subcommand === "cancel") {
|
|
1119
|
+
await client.scheduledMessages.cancel(messageId);
|
|
1120
|
+
writeJson(out, { ok: true, id: messageId });
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
if (subcommand === "update") {
|
|
1124
|
+
const to = messageRecipients(parsed.options, "to");
|
|
1125
|
+
const cc = messageRecipients(parsed.options, "cc");
|
|
1126
|
+
const bcc = messageRecipients(parsed.options, "bcc");
|
|
1127
|
+
const text = getString(parsed.options, "text");
|
|
1128
|
+
const html = getString(parsed.options, "html");
|
|
1129
|
+
if (to.length === 0) throw new CliUsageError("Provide at least one --to recipient.");
|
|
1130
|
+
if (!text && !html) throw new CliUsageError("Provide --text or --html.");
|
|
1131
|
+
const update = {
|
|
1132
|
+
to,
|
|
1133
|
+
...cc.length > 0 ? { cc } : {},
|
|
1134
|
+
...bcc.length > 0 ? { bcc } : {},
|
|
1135
|
+
subject: getRequiredString(parsed.options, "subject"),
|
|
1136
|
+
...text ? { text } : {},
|
|
1137
|
+
...html ? { html } : {},
|
|
1138
|
+
...getStringList(parsed.options, "staged-attachment-id").length > 0 ? { staged_attachment_ids: getStringList(parsed.options, "staged-attachment-id") } : {},
|
|
1139
|
+
scheduled_at: getRequiredString(parsed.options, "scheduled-at")
|
|
1140
|
+
};
|
|
1141
|
+
writeJson(out, await client.scheduledMessages.update(messageId, update));
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
throw new CliUsageError("Unknown scheduled-messages command.");
|
|
1145
|
+
}
|
|
1040
1146
|
async function runWebhooks(client, parsed, out) {
|
|
1041
1147
|
const subcommand = parsed.positional[1];
|
|
1042
1148
|
if (subcommand === "list") {
|
|
@@ -1779,6 +1885,18 @@ async function runCli(argv, options = {}) {
|
|
|
1779
1885
|
await writeRawResponse(parsed, { stdout: out }, response);
|
|
1780
1886
|
return 0;
|
|
1781
1887
|
}
|
|
1888
|
+
if (command === "capabilities") {
|
|
1889
|
+
const response = await rawApiRequest(
|
|
1890
|
+
parsed,
|
|
1891
|
+
options,
|
|
1892
|
+
"GET",
|
|
1893
|
+
"/capabilities",
|
|
1894
|
+
void 0,
|
|
1895
|
+
true
|
|
1896
|
+
);
|
|
1897
|
+
await writeRawResponse(parsed, { stdout: out }, response);
|
|
1898
|
+
return 0;
|
|
1899
|
+
}
|
|
1782
1900
|
if (command === "api") {
|
|
1783
1901
|
const method = requirePositional(parsed.positional, 1, "method").toUpperCase();
|
|
1784
1902
|
const path = apiPath(requirePositional(parsed.positional, 2, "path"));
|
|
@@ -1792,6 +1910,7 @@ async function runCli(argv, options = {}) {
|
|
|
1792
1910
|
else if (command === "mailboxes") await runMailboxes(client, parsed, out);
|
|
1793
1911
|
else if (command === "inbox") await runInbox(client, parsed, out);
|
|
1794
1912
|
else if (command === "messages") await runMessages(client, parsed, out);
|
|
1913
|
+
else if (command === "scheduled-messages") await runScheduledMessages(client, parsed, out);
|
|
1795
1914
|
else if (command === "reply-scans") await runReplyScans(client, parsed, out);
|
|
1796
1915
|
else if (command === "webhooks") await runWebhooks(client, parsed, out);
|
|
1797
1916
|
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
|
+
"version": "0.4.1",
|
|
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.
|
|
44
|
+
"shipmail": "0.4.4"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"tsup": "^8.4.0",
|