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.
Files changed (3) hide show
  1. package/README.md +4 -1
  2. package/dist/cli.js +261 -1
  3. 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.5";
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.5",
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.26"
44
+ "shipmail": "0.1.30"
45
45
  },
46
46
  "devDependencies": {
47
47
  "tsup": "^8.4.0",