shipmail-cli 0.1.5 → 0.1.7
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 +4 -1
- package/dist/cli.js +261 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -55,6 +55,7 @@ shipmail newsletters preflight nws_123
|
|
|
55
55
|
shipmail newsletters schedule nws_123 --scheduled-at 2026-08-01T09:00:00.000Z
|
|
56
56
|
shipmail newsletter-assets list --kind image
|
|
57
57
|
shipmail newsletter-assets upload ./hero.png --content-type image/png
|
|
58
|
+
shipmail newsletter-assets register-url https://cdn.shipmail.to/newsletter-images/org_123/hero.png
|
|
58
59
|
|
|
59
60
|
# Manage webhooks
|
|
60
61
|
shipmail webhooks create \
|
|
@@ -63,6 +64,8 @@ shipmail webhooks create \
|
|
|
63
64
|
shipmail webhooks deliveries whk_123 --status failed
|
|
64
65
|
```
|
|
65
66
|
|
|
67
|
+
`newsletter-assets list` includes current newsletter media storage usage in the JSON response.
|
|
68
|
+
|
|
66
69
|
## Generic API Command
|
|
67
70
|
|
|
68
71
|
The `api` command reaches every Shipmail REST endpoint, including endpoints that do not have a shorthand command yet.
|
|
@@ -91,6 +94,6 @@ Use repeatable `--header "Name: value"` flags for request headers. Use `--output
|
|
|
91
94
|
- `suppressions list|remove`
|
|
92
95
|
- `newsletter-domains list`
|
|
93
96
|
- `newsletters create|update|preview|preflight|test-send|schedule|cancel|resume`
|
|
94
|
-
- `newsletter-assets list|upload`
|
|
97
|
+
- `newsletter-assets list|upload|register-url`
|
|
95
98
|
|
|
96
99
|
Run `shipmail --help` for flags and examples.
|
package/dist/cli.js
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "shipmail";
|
|
13
13
|
|
|
14
14
|
// src/version.ts
|
|
15
|
-
var VERSION = "0.1.
|
|
15
|
+
var VERSION = "0.1.7";
|
|
16
16
|
|
|
17
17
|
// src/cli.ts
|
|
18
18
|
var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
|
|
@@ -343,6 +343,7 @@ Usage:
|
|
|
343
343
|
shipmail newsletters resume <id>
|
|
344
344
|
shipmail newsletter-assets list [--kind image|video] [--q text] [--limit 25] [--cursor cur]
|
|
345
345
|
shipmail newsletter-assets upload <file> [--filename name] [--content-type type]
|
|
346
|
+
shipmail newsletter-assets register-url <url> [--filename name] [--thumbnail-url url]
|
|
346
347
|
shipmail newsletter-domains list [--limit 25] [--cursor cur]
|
|
347
348
|
shipmail audiences list [--limit 25] [--cursor cur]
|
|
348
349
|
shipmail audiences create <name> --consent-source "Signup form" [--description "..."]
|
|
@@ -355,7 +356,19 @@ Usage:
|
|
|
355
356
|
shipmail audiences subscribers by-email <audience-id> <email>
|
|
356
357
|
shipmail audiences subscribers update <audience-id> <subscriber-id> [--name "..."]
|
|
357
358
|
shipmail audiences subscribers unsubscribe <audience-id> <subscriber-id>
|
|
359
|
+
shipmail audiences subscribers resubscribe <audience-id> <subscriber-id> --consent-source "Signup form"
|
|
358
360
|
shipmail audiences subscribers remove <audience-id> <subscriber-id>
|
|
361
|
+
shipmail calendar events list --mailbox hello@example.com --from 2026-08-01T00:00:00Z --to 2026-08-08T00:00:00Z [--expand true] [--limit 100]
|
|
362
|
+
shipmail calendar events create --mailbox hello@example.com --title "Sync" --start 2026-08-01T14:00:00 [--timezone Europe/Paris] [--duration PT30M] [--all-day true] [--location "HQ"] [--video-url https://...] [--reminders JSON|@file]
|
|
363
|
+
shipmail calendar events get <event-id> --mailbox hello@example.com
|
|
364
|
+
shipmail calendar events update <event-id> --mailbox hello@example.com [--title "..."] [--start ...]
|
|
365
|
+
shipmail calendar events delete <event-id> --mailbox hello@example.com
|
|
366
|
+
shipmail calendar availability --mailbox hello@example.com --from 2026-08-03T00:00:00Z --to 2026-08-04T00:00:00Z [--duration 30] [--days 1,2,3,4,5] [--window-start 540] [--window-end 1020]
|
|
367
|
+
shipmail booking-pages list [--limit 25] [--cursor cur]
|
|
368
|
+
shipmail booking-pages create --name "Intro" --mailbox hello@example.com --slug intro --duration-minutes 30 --availability-days 1,2,3,4,5 --window-start-minutes 540 --window-end-minutes 1020 --timezone America/New_York
|
|
369
|
+
shipmail booking-pages get <id>
|
|
370
|
+
shipmail booking-pages update <id> [--name "..."] [--active false]
|
|
371
|
+
shipmail booking-pages delete <id>
|
|
359
372
|
|
|
360
373
|
Global options:
|
|
361
374
|
--api-key <key> API key. Defaults to SHIPMAIL_API_KEY.
|
|
@@ -777,6 +790,17 @@ async function runSubscribers(client, parsed, out) {
|
|
|
777
790
|
);
|
|
778
791
|
return;
|
|
779
792
|
}
|
|
793
|
+
if (action === "resubscribe") {
|
|
794
|
+
writeJson(
|
|
795
|
+
out,
|
|
796
|
+
await client.audiences.subscribers.resubscribe(
|
|
797
|
+
audienceId,
|
|
798
|
+
requirePositional(parsed.positional, 4, "subscriber id"),
|
|
799
|
+
{ consent_source: getRequiredString(parsed.options, "consent-source") }
|
|
800
|
+
)
|
|
801
|
+
);
|
|
802
|
+
return;
|
|
803
|
+
}
|
|
780
804
|
if (action === "remove") {
|
|
781
805
|
const subscriberId = requirePositional(parsed.positional, 4, "subscriber id");
|
|
782
806
|
await client.audiences.subscribers.remove(audienceId, subscriberId);
|
|
@@ -948,8 +972,242 @@ async function runNewsletterAssets(client, parsed, out) {
|
|
|
948
972
|
);
|
|
949
973
|
return;
|
|
950
974
|
}
|
|
975
|
+
if (subcommand === "register-url") {
|
|
976
|
+
writeJson(
|
|
977
|
+
out,
|
|
978
|
+
await client.newsletters.assets.registerFromUrl({
|
|
979
|
+
url: requirePositional(parsed.positional, 2, "url"),
|
|
980
|
+
filename: getString(parsed.options, "filename"),
|
|
981
|
+
thumbnail_url: getString(parsed.options, "thumbnail-url")
|
|
982
|
+
})
|
|
983
|
+
);
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
951
986
|
throw new CliUsageError("Unknown newsletter-assets command.");
|
|
952
987
|
}
|
|
988
|
+
var CALENDAR_EVENT_STATUSES = [
|
|
989
|
+
"confirmed",
|
|
990
|
+
"tentative",
|
|
991
|
+
"cancelled"
|
|
992
|
+
];
|
|
993
|
+
var CALENDAR_FREE_BUSY_STATUSES = ["free", "busy"];
|
|
994
|
+
var CALENDAR_PRIVACIES = ["public", "private", "secret"];
|
|
995
|
+
function requireNumber(options, key) {
|
|
996
|
+
const value = getNumber(options, key);
|
|
997
|
+
if (value === void 0) throw new CliUsageError(`Missing --${key}.`);
|
|
998
|
+
return value;
|
|
999
|
+
}
|
|
1000
|
+
function getIntList(options, key) {
|
|
1001
|
+
return getStringList(options, key).map((raw) => {
|
|
1002
|
+
const value = Number(raw);
|
|
1003
|
+
if (!Number.isInteger(value)) {
|
|
1004
|
+
throw new CliUsageError(`--${key} must be a comma-separated list of integers.`);
|
|
1005
|
+
}
|
|
1006
|
+
return value;
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
function requireIntList(options, key) {
|
|
1010
|
+
const values = getIntList(options, key);
|
|
1011
|
+
if (values.length === 0) throw new CliUsageError(`Missing --${key}.`);
|
|
1012
|
+
return values;
|
|
1013
|
+
}
|
|
1014
|
+
function getEnum(options, key, allowed) {
|
|
1015
|
+
const value = getString(options, key);
|
|
1016
|
+
if (value === void 0) return void 0;
|
|
1017
|
+
const match = allowed.find((candidate) => candidate === value);
|
|
1018
|
+
if (match === void 0) {
|
|
1019
|
+
throw new CliUsageError(`--${key} must be one of: ${allowed.join(", ")}.`);
|
|
1020
|
+
}
|
|
1021
|
+
return match;
|
|
1022
|
+
}
|
|
1023
|
+
function isReminderArray(value) {
|
|
1024
|
+
return Array.isArray(value) && value.every(
|
|
1025
|
+
(item) => typeof item === "object" && item !== null && !Array.isArray(item) && Number.isInteger(item.minutes_before)
|
|
1026
|
+
);
|
|
1027
|
+
}
|
|
1028
|
+
async function readRemindersInput(value) {
|
|
1029
|
+
const parsed = await readJsonInput(value);
|
|
1030
|
+
if (parsed === void 0) return void 0;
|
|
1031
|
+
if (!isReminderArray(parsed)) {
|
|
1032
|
+
throw new CliUsageError("--reminders must be a JSON array of { minutes_before } objects.");
|
|
1033
|
+
}
|
|
1034
|
+
return parsed;
|
|
1035
|
+
}
|
|
1036
|
+
async function calendarEventFields(options) {
|
|
1037
|
+
const description = getString(options, "description");
|
|
1038
|
+
const timezone = getString(options, "timezone");
|
|
1039
|
+
const duration = getString(options, "duration");
|
|
1040
|
+
const allDay = getBoolean(options, "all-day");
|
|
1041
|
+
const location = getString(options, "location");
|
|
1042
|
+
const videoUrl = getString(options, "video-url");
|
|
1043
|
+
const color = getString(options, "color");
|
|
1044
|
+
const status = getEnum(options, "status", CALENDAR_EVENT_STATUSES);
|
|
1045
|
+
const freeBusyStatus = getEnum(options, "free-busy-status", CALENDAR_FREE_BUSY_STATUSES);
|
|
1046
|
+
const privacy = getEnum(options, "privacy", CALENDAR_PRIVACIES);
|
|
1047
|
+
const reminders = await readRemindersInput(getString(options, "reminders"));
|
|
1048
|
+
return {
|
|
1049
|
+
...description !== void 0 ? { description } : {},
|
|
1050
|
+
...timezone !== void 0 ? { timezone } : {},
|
|
1051
|
+
...duration !== void 0 ? { duration } : {},
|
|
1052
|
+
...allDay !== void 0 ? { all_day: allDay } : {},
|
|
1053
|
+
...location !== void 0 ? { location } : {},
|
|
1054
|
+
...videoUrl !== void 0 ? { video_url: videoUrl } : {},
|
|
1055
|
+
...color !== void 0 ? { color } : {},
|
|
1056
|
+
...status !== void 0 ? { status } : {},
|
|
1057
|
+
...freeBusyStatus !== void 0 ? { free_busy_status: freeBusyStatus } : {},
|
|
1058
|
+
...privacy !== void 0 ? { privacy } : {},
|
|
1059
|
+
...reminders !== void 0 ? { reminders } : {}
|
|
1060
|
+
};
|
|
1061
|
+
}
|
|
1062
|
+
async function runCalendarEvents(client, parsed, out) {
|
|
1063
|
+
const subcommand = parsed.positional[2];
|
|
1064
|
+
const options = parsed.options;
|
|
1065
|
+
if (subcommand === "list") {
|
|
1066
|
+
const params = {
|
|
1067
|
+
mailbox: getRequiredString(options, "mailbox"),
|
|
1068
|
+
from: getRequiredString(options, "from"),
|
|
1069
|
+
to: getRequiredString(options, "to"),
|
|
1070
|
+
expand: getBoolean(options, "expand"),
|
|
1071
|
+
calendar_id: getString(options, "calendar-id"),
|
|
1072
|
+
cursor: getString(options, "cursor"),
|
|
1073
|
+
limit: getNumber(options, "limit")
|
|
1074
|
+
};
|
|
1075
|
+
writeJson(out, await client.calendar.events.list(params));
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
if (subcommand === "create") {
|
|
1079
|
+
const params = {
|
|
1080
|
+
mailbox: getRequiredString(options, "mailbox"),
|
|
1081
|
+
title: getRequiredString(options, "title"),
|
|
1082
|
+
start: getRequiredString(options, "start"),
|
|
1083
|
+
calendar_id: getString(options, "calendar-id"),
|
|
1084
|
+
...await calendarEventFields(options)
|
|
1085
|
+
};
|
|
1086
|
+
writeJson(out, await client.calendar.events.create(params));
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
if (subcommand === "get") {
|
|
1090
|
+
const id = requirePositional(parsed.positional, 3, "event id");
|
|
1091
|
+
writeJson(
|
|
1092
|
+
out,
|
|
1093
|
+
await client.calendar.events.get(id, { mailbox: getRequiredString(options, "mailbox") })
|
|
1094
|
+
);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1097
|
+
if (subcommand === "update") {
|
|
1098
|
+
const id = requirePositional(parsed.positional, 3, "event id");
|
|
1099
|
+
const params = {
|
|
1100
|
+
mailbox: getRequiredString(options, "mailbox"),
|
|
1101
|
+
title: getString(options, "title"),
|
|
1102
|
+
start: getString(options, "start"),
|
|
1103
|
+
...await calendarEventFields(options)
|
|
1104
|
+
};
|
|
1105
|
+
writeJson(out, await client.calendar.events.update(id, params));
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
if (subcommand === "delete") {
|
|
1109
|
+
const id = requirePositional(parsed.positional, 3, "event id");
|
|
1110
|
+
await client.calendar.events.delete(id, { mailbox: getRequiredString(options, "mailbox") });
|
|
1111
|
+
writeJson(out, { ok: true, id });
|
|
1112
|
+
return;
|
|
1113
|
+
}
|
|
1114
|
+
throw new CliUsageError("Unknown calendar events command.");
|
|
1115
|
+
}
|
|
1116
|
+
async function runCalendar(client, parsed, out) {
|
|
1117
|
+
const subcommand = parsed.positional[1];
|
|
1118
|
+
if (subcommand === "events") {
|
|
1119
|
+
await runCalendarEvents(client, parsed, out);
|
|
1120
|
+
return;
|
|
1121
|
+
}
|
|
1122
|
+
if (subcommand === "availability") {
|
|
1123
|
+
const options = parsed.options;
|
|
1124
|
+
const days = getIntList(options, "days");
|
|
1125
|
+
const params = {
|
|
1126
|
+
mailbox: getRequiredString(options, "mailbox"),
|
|
1127
|
+
from: getRequiredString(options, "from"),
|
|
1128
|
+
to: getRequiredString(options, "to"),
|
|
1129
|
+
duration: getNumber(options, "duration"),
|
|
1130
|
+
interval: getNumber(options, "interval"),
|
|
1131
|
+
timezone: getString(options, "timezone"),
|
|
1132
|
+
...days.length > 0 ? { days } : {},
|
|
1133
|
+
window_start: getNumber(options, "window-start"),
|
|
1134
|
+
window_end: getNumber(options, "window-end"),
|
|
1135
|
+
buffer: getNumber(options, "buffer"),
|
|
1136
|
+
minimum_notice: getNumber(options, "minimum-notice")
|
|
1137
|
+
};
|
|
1138
|
+
writeJson(out, await client.calendar.availability(params));
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
throw new CliUsageError("Unknown calendar command.");
|
|
1142
|
+
}
|
|
1143
|
+
async function runBookingPages(client, parsed, out) {
|
|
1144
|
+
const subcommand = parsed.positional[1];
|
|
1145
|
+
const options = parsed.options;
|
|
1146
|
+
if (subcommand === "list") {
|
|
1147
|
+
writeJson(
|
|
1148
|
+
out,
|
|
1149
|
+
await client.bookingPages.list({
|
|
1150
|
+
cursor: getString(options, "cursor"),
|
|
1151
|
+
limit: getNumber(options, "limit")
|
|
1152
|
+
})
|
|
1153
|
+
);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
if (subcommand === "create") {
|
|
1157
|
+
const params = {
|
|
1158
|
+
name: getRequiredString(options, "name"),
|
|
1159
|
+
mailbox: getRequiredString(options, "mailbox"),
|
|
1160
|
+
slug: getRequiredString(options, "slug"),
|
|
1161
|
+
duration_minutes: requireNumber(options, "duration-minutes"),
|
|
1162
|
+
availability_days: requireIntList(options, "availability-days"),
|
|
1163
|
+
window_start_minutes: requireNumber(options, "window-start-minutes"),
|
|
1164
|
+
window_end_minutes: requireNumber(options, "window-end-minutes"),
|
|
1165
|
+
timezone: getRequiredString(options, "timezone"),
|
|
1166
|
+
description: getString(options, "description"),
|
|
1167
|
+
buffer_minutes: getNumber(options, "buffer-minutes"),
|
|
1168
|
+
minimum_notice_minutes: getNumber(options, "minimum-notice-minutes"),
|
|
1169
|
+
max_advance_days: getNumber(options, "max-advance-days"),
|
|
1170
|
+
active: getBoolean(options, "active")
|
|
1171
|
+
};
|
|
1172
|
+
writeJson(out, await client.bookingPages.create(params));
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
if (subcommand === "get") {
|
|
1176
|
+
writeJson(
|
|
1177
|
+
out,
|
|
1178
|
+
await client.bookingPages.get(requirePositional(parsed.positional, 2, "booking page id"))
|
|
1179
|
+
);
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
if (subcommand === "update") {
|
|
1183
|
+
const id = requirePositional(parsed.positional, 2, "booking page id");
|
|
1184
|
+
const days = getIntList(options, "availability-days");
|
|
1185
|
+
const params = {
|
|
1186
|
+
name: getString(options, "name"),
|
|
1187
|
+
mailbox: getString(options, "mailbox"),
|
|
1188
|
+
slug: getString(options, "slug"),
|
|
1189
|
+
duration_minutes: getNumber(options, "duration-minutes"),
|
|
1190
|
+
...days.length > 0 ? { availability_days: days } : {},
|
|
1191
|
+
window_start_minutes: getNumber(options, "window-start-minutes"),
|
|
1192
|
+
window_end_minutes: getNumber(options, "window-end-minutes"),
|
|
1193
|
+
timezone: getString(options, "timezone"),
|
|
1194
|
+
description: getString(options, "description"),
|
|
1195
|
+
buffer_minutes: getNumber(options, "buffer-minutes"),
|
|
1196
|
+
minimum_notice_minutes: getNumber(options, "minimum-notice-minutes"),
|
|
1197
|
+
max_advance_days: getNumber(options, "max-advance-days"),
|
|
1198
|
+
active: getBoolean(options, "active")
|
|
1199
|
+
};
|
|
1200
|
+
writeJson(out, await client.bookingPages.update(id, params));
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
if (subcommand === "delete") {
|
|
1204
|
+
const id = requirePositional(parsed.positional, 2, "booking page id");
|
|
1205
|
+
await client.bookingPages.delete(id);
|
|
1206
|
+
writeJson(out, { ok: true, id });
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
throw new CliUsageError("Unknown booking-pages command.");
|
|
1210
|
+
}
|
|
953
1211
|
function writeError(err, errOut) {
|
|
954
1212
|
if (err instanceof CliUsageError) {
|
|
955
1213
|
errOut.write(`Error: ${err.message}
|
|
@@ -1013,6 +1271,8 @@ async function runCli(argv, options = {}) {
|
|
|
1013
1271
|
else if (command === "newsletter-assets") await runNewsletterAssets(client, parsed, out);
|
|
1014
1272
|
else if (command === "newsletter-domains") await runNewsletterDomains(client, parsed, out);
|
|
1015
1273
|
else if (command === "audiences") await runAudiences(client, parsed, out);
|
|
1274
|
+
else if (command === "calendar") await runCalendar(client, parsed, out);
|
|
1275
|
+
else if (command === "booking-pages") await runBookingPages(client, parsed, out);
|
|
1016
1276
|
else throw new CliUsageError(`Unknown command: ${command}.`);
|
|
1017
1277
|
return 0;
|
|
1018
1278
|
} catch (err) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "shipmail-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
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.1.
|
|
44
|
+
"shipmail": "0.1.30"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"tsup": "^8.4.0",
|