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.
- package/README.md +26 -4
- package/dist/cli.js +98 -9
- 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|
|
|
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.
|
|
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.
|
|
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.
|
|
44
|
+
"shipmail": "0.4.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"tsup": "^8.4.0",
|