shipmail-cli 0.4.9 → 0.4.11
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 +11 -4
- package/dist/cli.js +109 -31
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -84,12 +84,12 @@ shipmail mailboxes sandbox-inbound mbx_123 --from customer@example.com --subject
|
|
|
84
84
|
# Read inbound mail
|
|
85
85
|
shipmail inbox list 550e8400-e29b-41d4-a716-446655440000 --folder-role inbox --after 2025-07-20T00:00:00Z --limit 25
|
|
86
86
|
shipmail inbox message 550e8400-e29b-41d4-a716-446655440000 eml_123
|
|
87
|
-
shipmail inbox threads 550e8400-e29b-41d4-a716-446655440000 --
|
|
87
|
+
shipmail inbox threads 550e8400-e29b-41d4-a716-446655440000 --attention-state needs_reply --after 2025-07-20T00:00:00Z --limit 50
|
|
88
88
|
shipmail inbox thread 550e8400-e29b-41d4-a716-446655440000 thr_123
|
|
89
|
-
shipmail inbox draft 550e8400-e29b-41d4-a716-446655440000 thr_123 --expected-
|
|
89
|
+
shipmail inbox draft 550e8400-e29b-41d4-a716-446655440000 thr_123 --expected-version 7 --text "Thanks for the note"
|
|
90
90
|
# Apply your approval policy; stale drafts fail with 409 without delivery.
|
|
91
91
|
shipmail inbox send-draft 550e8400-e29b-41d4-a716-446655440000 thr_123 drf_123
|
|
92
|
-
shipmail inbox thread-
|
|
92
|
+
shipmail inbox thread-attention 550e8400-e29b-41d4-a716-446655440000 thr_123 --done true --expected-version 8
|
|
93
93
|
shipmail inbox reply-message 550e8400-e29b-41d4-a716-446655440000 eml_123 --to user@example.com --text "Thanks"
|
|
94
94
|
shipmail inbox reply-thread 550e8400-e29b-41d4-a716-446655440000 thr_123 --to user@example.com --text "Following up"
|
|
95
95
|
|
|
@@ -114,6 +114,7 @@ shipmail newsletters preflight nws_123
|
|
|
114
114
|
shipmail newsletters schedule nws_123 --scheduled-at 2026-08-01T09:00:00.000Z
|
|
115
115
|
shipmail newsletter-assets list --kind image
|
|
116
116
|
shipmail newsletter-assets upload ./hero.png --content-type image/png
|
|
117
|
+
shipmail newsletter-assets upload ./demo.mp4 --content-type video/mp4 --thumbnail ./demo-poster.jpg
|
|
117
118
|
shipmail newsletter-assets register-url https://cdn.shipmail.to/newsletter-images/org_123/hero.png
|
|
118
119
|
|
|
119
120
|
# Manage webhooks
|
|
@@ -132,7 +133,13 @@ shipmail domains list --organization-id 00000000-0000-4000-8000-000000000123
|
|
|
132
133
|
shipmail mailboxes create --organization-id 00000000-0000-4000-8000-000000000123 --domain-id dom_123 --address support --generate-password
|
|
133
134
|
```
|
|
134
135
|
|
|
135
|
-
|
|
136
|
+
Paragraph, quote, callout, list-item, and column bodies in `--blocks` accept bare text or
|
|
137
|
+
sanitized inline HTML. Use `<p>` and `<br>` for line breaks. Shipmail keeps `a`, `b`, `br`,
|
|
138
|
+
`code`, `em`, `i`, `p`, `s`, `span`, `strong`, and `u`, with safe attributes and URLs.
|
|
139
|
+
|
|
140
|
+
`newsletter-assets upload` uses a five-minute direct storage PUT, then calls the single-use
|
|
141
|
+
completion URL. Video uploads require a JPEG poster through `--thumbnail`. `newsletter-assets list`
|
|
142
|
+
includes current newsletter media storage usage in the JSON response.
|
|
136
143
|
|
|
137
144
|
## Generic API Command
|
|
138
145
|
|
package/dist/cli.js
CHANGED
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
} from "shipmail";
|
|
15
15
|
|
|
16
16
|
// src/version.ts
|
|
17
|
-
var VERSION = "0.4.
|
|
17
|
+
var VERSION = "0.4.11";
|
|
18
18
|
|
|
19
19
|
// src/cli.ts
|
|
20
20
|
var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
|
|
@@ -372,12 +372,12 @@ Usage:
|
|
|
372
372
|
shipmail mailboxes prepare-attachment-upload <id> <file> [--filename name] [--content-type type]
|
|
373
373
|
shipmail mailboxes stage-attachment <id> <file> [--filename name] [--content-type type]
|
|
374
374
|
shipmail mailboxes sandbox-inbound <id> --from sender@example.com --subject "Test" (--text "Hello" | --html "<p>Hello</p>") [--to support@example.com]
|
|
375
|
-
shipmail inbox list <mailbox-id> [--folder-role inbox] [--after ISO] [--before ISO] [--limit 25] [--cursor cur]
|
|
375
|
+
shipmail inbox list <mailbox-id> [--folder-role inbox] [--search text] [--after ISO] [--before ISO] [--limit 25] [--cursor cur]
|
|
376
376
|
shipmail inbox message <mailbox-id> <message-id>
|
|
377
|
-
shipmail inbox threads <mailbox-id> [--
|
|
377
|
+
shipmail inbox threads <mailbox-id> [--attention-state needs_reply] [--after ISO] [--before ISO] [--limit 25] [--cursor cur]
|
|
378
378
|
shipmail inbox thread <mailbox-id> <thread-id>
|
|
379
|
-
shipmail inbox thread-
|
|
380
|
-
shipmail inbox draft <mailbox-id> <thread-id> --expected-
|
|
379
|
+
shipmail inbox thread-attention <mailbox-id> <thread-id> --done true --expected-version 3
|
|
380
|
+
shipmail inbox draft <mailbox-id> <thread-id> --expected-version 3 (--text "Hello" | --html "<p>Hello</p>") [--reply-mode reply_all]
|
|
381
381
|
shipmail inbox send-draft <mailbox-id> <thread-id> <draft-id>
|
|
382
382
|
shipmail inbox reply-message <mailbox-id> <message-id> --to user@example.com (--text "Hello" | --html "<p>Hello</p>")
|
|
383
383
|
shipmail inbox reply-thread <mailbox-id> <thread-id> --to user@example.com (--text "Hello" | --html "<p>Hello</p>")
|
|
@@ -415,7 +415,7 @@ Usage:
|
|
|
415
415
|
shipmail newsletters cancel <id>
|
|
416
416
|
shipmail newsletters resume <id>
|
|
417
417
|
shipmail newsletter-assets list [--kind image|video] [--q text] [--limit 25] [--cursor cur]
|
|
418
|
-
shipmail newsletter-assets upload <file> [--filename name] [--content-type type]
|
|
418
|
+
shipmail newsletter-assets upload <file> [--filename name] [--content-type type] [--thumbnail poster.jpg]
|
|
419
419
|
shipmail newsletter-assets register-url <url> [--filename name] [--thumbnail-url url]
|
|
420
420
|
shipmail newsletter-sender-identities list [--limit 25] [--cursor cur]
|
|
421
421
|
shipmail newsletter-domains list [--limit 25] [--cursor cur]
|
|
@@ -466,6 +466,9 @@ Booking page conferencing:
|
|
|
466
466
|
Newsletter styling:
|
|
467
467
|
--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.
|
|
468
468
|
|
|
469
|
+
Newsletter block prose:
|
|
470
|
+
Paragraph, quote, callout, list-item, and column bodies accept bare text or sanitized inline HTML. Use <p> and <br> for line breaks.
|
|
471
|
+
|
|
469
472
|
Newsletter feeds:
|
|
470
473
|
--feed-entry-url <url> Link this issue's Atom feed title to an http(s) URL instead of its Shipmail archive.
|
|
471
474
|
|
|
@@ -909,7 +912,7 @@ async function runInbox(client, parsed, out) {
|
|
|
909
912
|
return;
|
|
910
913
|
}
|
|
911
914
|
if (subcommand === "threads") {
|
|
912
|
-
const
|
|
915
|
+
const attentionState = getString(parsed.options, "attention-state");
|
|
913
916
|
const sortBy = getString(parsed.options, "sort-by");
|
|
914
917
|
const order = getString(parsed.options, "order");
|
|
915
918
|
writeJson(
|
|
@@ -917,8 +920,8 @@ async function runInbox(client, parsed, out) {
|
|
|
917
920
|
await client.mailboxes.listInboxThreads(
|
|
918
921
|
requirePositional(parsed.positional, 2, "mailbox id"),
|
|
919
922
|
{
|
|
920
|
-
...
|
|
921
|
-
|
|
923
|
+
...attentionState ? {
|
|
924
|
+
attention_state: attentionState
|
|
922
925
|
} : {},
|
|
923
926
|
...sortBy ? { sort_by: sortBy } : {},
|
|
924
927
|
...order ? { order } : {},
|
|
@@ -931,29 +934,31 @@ async function runInbox(client, parsed, out) {
|
|
|
931
934
|
);
|
|
932
935
|
return;
|
|
933
936
|
}
|
|
934
|
-
if (subcommand === "thread-
|
|
935
|
-
const
|
|
936
|
-
const
|
|
937
|
-
|
|
938
|
-
|
|
937
|
+
if (subcommand === "thread-attention") {
|
|
938
|
+
const done = getBoolean(parsed.options, "done");
|
|
939
|
+
const followUpAt = getString(parsed.options, "follow-up-at");
|
|
940
|
+
const expectedVersion = getNumber(parsed.options, "expected-version");
|
|
941
|
+
if (done === void 0 && followUpAt === void 0 || expectedVersion === void 0) {
|
|
942
|
+
throw new CliUsageError("Provide --done or --follow-up-at, and provide --expected-version.");
|
|
939
943
|
}
|
|
940
944
|
writeJson(
|
|
941
945
|
out,
|
|
942
|
-
await client.mailboxes.
|
|
946
|
+
await client.mailboxes.updateInboxThreadAttention(
|
|
943
947
|
requirePositional(parsed.positional, 2, "mailbox id"),
|
|
944
948
|
requirePositional(parsed.positional, 3, "thread id"),
|
|
945
949
|
{
|
|
946
|
-
|
|
947
|
-
|
|
950
|
+
...done === void 0 ? {} : { done },
|
|
951
|
+
...followUpAt === void 0 ? {} : { follow_up_at: followUpAt === "null" ? null : followUpAt },
|
|
952
|
+
expected_version: expectedVersion
|
|
948
953
|
}
|
|
949
954
|
)
|
|
950
955
|
);
|
|
951
956
|
return;
|
|
952
957
|
}
|
|
953
958
|
if (subcommand === "draft") {
|
|
954
|
-
const
|
|
955
|
-
if (
|
|
956
|
-
throw new CliUsageError("Provide --expected-
|
|
959
|
+
const expectedVersion = getNumber(parsed.options, "expected-version");
|
|
960
|
+
if (expectedVersion === void 0) {
|
|
961
|
+
throw new CliUsageError("Provide --expected-version.");
|
|
957
962
|
}
|
|
958
963
|
const text = await readTextInput(getString(parsed.options, "text"));
|
|
959
964
|
const html = await readTextInput(getString(parsed.options, "html"));
|
|
@@ -964,7 +969,7 @@ async function runInbox(client, parsed, out) {
|
|
|
964
969
|
requirePositional(parsed.positional, 2, "mailbox id"),
|
|
965
970
|
requirePositional(parsed.positional, 3, "thread id"),
|
|
966
971
|
{
|
|
967
|
-
|
|
972
|
+
expected_version: expectedVersion,
|
|
968
973
|
...text ? { text } : {},
|
|
969
974
|
...html ? { html } : {},
|
|
970
975
|
...getString(parsed.options, "reply-mode") ? { reply_mode: getString(parsed.options, "reply-mode") } : {}
|
|
@@ -1571,7 +1576,7 @@ async function runNewsletters(client, parsed, out) {
|
|
|
1571
1576
|
}
|
|
1572
1577
|
throw new CliUsageError("Unknown newsletters command.");
|
|
1573
1578
|
}
|
|
1574
|
-
async function runNewsletterAssets(client, parsed, out) {
|
|
1579
|
+
async function runNewsletterAssets(client, parsed, out, fetchImpl) {
|
|
1575
1580
|
const subcommand = parsed.positional[1];
|
|
1576
1581
|
if (subcommand === "list") {
|
|
1577
1582
|
const kind = getString(parsed.options, "kind");
|
|
@@ -1592,14 +1597,37 @@ async function runNewsletterAssets(client, parsed, out) {
|
|
|
1592
1597
|
if (subcommand === "upload") {
|
|
1593
1598
|
const filePath = requirePositional(parsed.positional, 2, "file");
|
|
1594
1599
|
const data = await readFile(realpathSync(filePath));
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1600
|
+
const filename = getString(parsed.options, "filename") ?? filePath.split(/[\\/]/).at(-1) ?? "newsletter-asset";
|
|
1601
|
+
const contentType = newsletterAssetContentType(
|
|
1602
|
+
filePath,
|
|
1603
|
+
getString(parsed.options, "content-type")
|
|
1604
|
+
);
|
|
1605
|
+
const thumbnail = contentType.startsWith("video/") ? await readNewsletterVideoThumbnail(parsed.options) : void 0;
|
|
1606
|
+
const prepared = await client.newsletters.assets.prepareUpload({
|
|
1607
|
+
filename,
|
|
1608
|
+
content_type: contentType,
|
|
1609
|
+
size: data.byteLength,
|
|
1610
|
+
sha256: createHash("sha256").update(data).digest("hex")
|
|
1611
|
+
});
|
|
1612
|
+
await putPreparedNewsletterBytes(fetchImpl, prepared.upload_url, prepared.upload_headers, data);
|
|
1613
|
+
if (prepared.kind === "video") {
|
|
1614
|
+
if (!thumbnail) throw new CliUsageError("Video uploads require --thumbnail <jpeg-file>.");
|
|
1615
|
+
await putPreparedNewsletterBytes(
|
|
1616
|
+
fetchImpl,
|
|
1617
|
+
prepared.thumbnail_upload_url,
|
|
1618
|
+
prepared.thumbnail_upload_headers,
|
|
1619
|
+
thumbnail
|
|
1620
|
+
);
|
|
1621
|
+
}
|
|
1622
|
+
const completion = await fetchImpl(prepared.complete_url, {
|
|
1623
|
+
method: prepared.complete_method,
|
|
1624
|
+
redirect: "error"
|
|
1625
|
+
});
|
|
1626
|
+
if (!completion.ok) {
|
|
1627
|
+
throw new CliHttpError(completion.status, { response: await completion.text() });
|
|
1628
|
+
}
|
|
1629
|
+
const asset = await completion.json();
|
|
1630
|
+
writeJson(out, asset);
|
|
1603
1631
|
return;
|
|
1604
1632
|
}
|
|
1605
1633
|
if (subcommand === "register-url") {
|
|
@@ -1615,6 +1643,55 @@ async function runNewsletterAssets(client, parsed, out) {
|
|
|
1615
1643
|
}
|
|
1616
1644
|
throw new CliUsageError("Unknown newsletter-assets command.");
|
|
1617
1645
|
}
|
|
1646
|
+
function newsletterAssetContentType(filePath, declared) {
|
|
1647
|
+
const value = declared?.trim().toLowerCase() ?? "";
|
|
1648
|
+
switch (value) {
|
|
1649
|
+
case "image/gif":
|
|
1650
|
+
case "image/jpeg":
|
|
1651
|
+
case "image/png":
|
|
1652
|
+
case "image/webp":
|
|
1653
|
+
case "video/mp4":
|
|
1654
|
+
case "video/quicktime":
|
|
1655
|
+
case "video/webm":
|
|
1656
|
+
return value;
|
|
1657
|
+
default:
|
|
1658
|
+
if (value) throw new CliUsageError("Unsupported newsletter asset content type.");
|
|
1659
|
+
}
|
|
1660
|
+
const lowerPath = filePath.toLowerCase();
|
|
1661
|
+
if (lowerPath.endsWith(".gif")) return "image/gif";
|
|
1662
|
+
if (lowerPath.endsWith(".jpg") || lowerPath.endsWith(".jpeg")) return "image/jpeg";
|
|
1663
|
+
if (lowerPath.endsWith(".png")) return "image/png";
|
|
1664
|
+
if (lowerPath.endsWith(".webp")) return "image/webp";
|
|
1665
|
+
if (lowerPath.endsWith(".mp4")) return "video/mp4";
|
|
1666
|
+
if (lowerPath.endsWith(".mov")) return "video/quicktime";
|
|
1667
|
+
if (lowerPath.endsWith(".webm")) return "video/webm";
|
|
1668
|
+
throw new CliUsageError("Use --content-type for this newsletter asset file.");
|
|
1669
|
+
}
|
|
1670
|
+
async function readNewsletterVideoThumbnail(options) {
|
|
1671
|
+
const thumbnailPath = getString(options, "thumbnail");
|
|
1672
|
+
if (!thumbnailPath) throw new CliUsageError("Video uploads require --thumbnail <jpeg-file>.");
|
|
1673
|
+
const bytes = await readFile(realpathSync(thumbnailPath));
|
|
1674
|
+
if (bytes.byteLength < 3 || bytes[0] !== 255 || bytes[1] !== 216 || bytes[2] !== 255) {
|
|
1675
|
+
throw new CliUsageError("--thumbnail must be a JPEG file.");
|
|
1676
|
+
}
|
|
1677
|
+
return bytes;
|
|
1678
|
+
}
|
|
1679
|
+
function uploadBody(bytes) {
|
|
1680
|
+
const body = new ArrayBuffer(bytes.byteLength);
|
|
1681
|
+
new Uint8Array(body).set(bytes);
|
|
1682
|
+
return body;
|
|
1683
|
+
}
|
|
1684
|
+
async function putPreparedNewsletterBytes(fetchImpl, url, headers, bytes) {
|
|
1685
|
+
const response = await fetchImpl(url, {
|
|
1686
|
+
method: "PUT",
|
|
1687
|
+
headers,
|
|
1688
|
+
body: uploadBody(bytes),
|
|
1689
|
+
redirect: "error"
|
|
1690
|
+
});
|
|
1691
|
+
if (!response.ok) {
|
|
1692
|
+
throw new CliHttpError(response.status, { response: await response.text() });
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1618
1695
|
var CALENDAR_EVENT_STATUSES = [
|
|
1619
1696
|
"confirmed",
|
|
1620
1697
|
"tentative",
|
|
@@ -2079,7 +2156,8 @@ async function runCli(argv, options = {}) {
|
|
|
2079
2156
|
else if (command === "webhooks") await runWebhooks(client, parsed, out);
|
|
2080
2157
|
else if (command === "suppressions") await runSuppressions(client, parsed, out);
|
|
2081
2158
|
else if (command === "newsletters") await runNewsletters(client, parsed, out);
|
|
2082
|
-
else if (command === "newsletter-assets")
|
|
2159
|
+
else if (command === "newsletter-assets")
|
|
2160
|
+
await runNewsletterAssets(client, parsed, out, options.fetch ?? globalThis.fetch);
|
|
2083
2161
|
else if (command === "newsletter-sender-identities")
|
|
2084
2162
|
await runNewsletterSenderIdentities(client, parsed, out);
|
|
2085
2163
|
else if (command === "newsletter-domains") await runNewsletterDomains(client, parsed, out);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shipmail-cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.11",
|
|
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.4.
|
|
44
|
+
"shipmail": "0.4.17"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"tsup": "^8.4.0",
|