shipmail-cli 0.1.9 → 0.1.10

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 +9 -1
  2. package/dist/cli.js +116 -10
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -14,7 +14,7 @@ Most commands require an API key. Create one in the Shipmail dashboard and expor
14
14
  export SHIPMAIL_API_KEY="sm_live_..."
15
15
  ```
16
16
 
17
- You can also pass `--api-key` to any command. Use `SHIPMAIL_BASE_URL` or `--base-url` when targeting a non-production API.
17
+ You can also pass `--api-key` to any command. Use `SHIPMAIL_BASE_URL` or `--base-url` when targeting a non-production API. Approved partners can set `SHIPMAIL_ORGANIZATION_ID` or pass `--organization-id` for delegated infrastructure calls.
18
18
 
19
19
  ## Common Commands
20
20
 
@@ -79,6 +79,12 @@ shipmail webhooks create \
79
79
  --events message.received,message.bounced
80
80
  shipmail webhooks deliveries whk_123 --status failed
81
81
  shipmail webhooks replay-delivery whk_123 dlv_123 --idempotency-key replay-123
82
+
83
+ # Partner beta lifecycle and delegated infrastructure
84
+ shipmail partner organizations create --name "Operator" --external-reference operator_123 --owner-email owner@example.com --mailbox-limit 3 --data-classification internal_test
85
+ shipmail partner usage
86
+ shipmail domains list --organization-id 00000000-0000-4000-8000-000000000123
87
+ shipmail mailboxes create --organization-id 00000000-0000-4000-8000-000000000123 --domain-id dom_123 --address support --generate-password
82
88
  ```
83
89
 
84
90
  `newsletter-assets list` includes current newsletter media storage usage in the JSON response.
@@ -113,5 +119,7 @@ Use repeatable `--header "Name: value"` flags for request headers. Use `--output
113
119
  - `newsletter-domains list`
114
120
  - `newsletters create|update|preview|preflight|test-send|schedule|cancel|resume`
115
121
  - `newsletter-assets list|upload|register-url`
122
+ - `partner organizations list|create|get|update|invite|suspend|resume|offboard`
123
+ - `partner usage`
116
124
 
117
125
  Run `shipmail --help` for flags and examples.
package/dist/cli.js CHANGED
@@ -6,13 +6,14 @@ import { readFile, writeFile } from "fs/promises";
6
6
  import { env as processEnv, exit, stderr, stdout } from "process";
7
7
  import { fileURLToPath } from "url";
8
8
  import {
9
+ CONFERENCING_PROVIDER_IDS,
9
10
  ShipMailClient,
10
11
  ShipMailError,
11
12
  WEBHOOK_EVENT_TYPES
12
13
  } from "shipmail";
13
14
 
14
15
  // src/version.ts
15
- var VERSION = "0.1.9";
16
+ var VERSION = "0.1.10";
16
17
 
17
18
  // src/cli.ts
18
19
  var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
@@ -123,6 +124,9 @@ function apiKey(parsed, env) {
123
124
  function baseUrl(parsed, env) {
124
125
  return normalizeBaseUrl(getString(parsed.options, "base-url") ?? env["SHIPMAIL_BASE_URL"]);
125
126
  }
127
+ function organizationId(parsed, env) {
128
+ return getString(parsed.options, "organization-id") ?? env["SHIPMAIL_ORGANIZATION_ID"];
129
+ }
126
130
  function requireApiKey(parsed, env) {
127
131
  const key = apiKey(parsed, env);
128
132
  if (!key) {
@@ -135,6 +139,7 @@ function createClient(parsed, options) {
135
139
  const config = {
136
140
  apiKey: requireApiKey(parsed, env),
137
141
  baseUrl: baseUrl(parsed, env),
142
+ organizationId: organizationId(parsed, env),
138
143
  ...options.fetch ? { fetch: options.fetch } : {}
139
144
  };
140
145
  return new ShipMailClient(config);
@@ -264,6 +269,10 @@ async function rawApiRequest(parsed, runOptions, method, path, body, authRequire
264
269
  const key = apiKey(parsed, env);
265
270
  if (key) headers["Authorization"] = `Bearer ${key}`;
266
271
  }
272
+ const targetOrganizationId = organizationId(parsed, env);
273
+ if (targetOrganizationId) {
274
+ headers["X-ShipMail-Organization-Id"] = targetOrganizationId;
275
+ }
267
276
  let requestBody;
268
277
  if (body !== void 0) {
269
278
  headers["Content-Type"] = "application/json";
@@ -315,7 +324,7 @@ Usage:
315
324
  shipmail domains verify <id>
316
325
  shipmail domains delete <id>
317
326
  shipmail mailboxes list [--domain-id dom] [--limit 25] [--cursor cur]
318
- shipmail mailboxes create --domain-id dom --address hello (--password value | --password-file path) [--display-name "Hello"]
327
+ shipmail mailboxes create --domain-id dom --address hello (--password value | --password-file path | --generate-password) [--display-name "Hello"]
319
328
  shipmail mailboxes get <id>
320
329
  shipmail mailboxes suspend <id>
321
330
  shipmail mailboxes resume <id>
@@ -378,19 +387,34 @@ Usage:
378
387
  shipmail calendar events delete <event-id> --mailbox hello@example.com
379
388
  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]
380
389
  shipmail booking-pages list [--limit 25] [--cursor cur]
381
- 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
390
+ 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 [--conferencing-provider zoom|google_meet]
382
391
  shipmail booking-pages get <id>
383
- shipmail booking-pages update <id> [--name "..."] [--active false]
392
+ shipmail booking-pages update <id> [--name "..."] [--conferencing-provider zoom|google_meet|none] [--active false]
384
393
  shipmail booking-pages delete <id>
394
+ shipmail partner organizations list
395
+ shipmail partner organizations create --name "Operator" --external-reference op_123 --owner-email owner@example.com [--mailbox-limit 3] [--data-classification internal_test|customer]
396
+ shipmail partner organizations get <id>
397
+ shipmail partner organizations update <id> [--name "Operator"] [--mailbox-limit 5]
398
+ shipmail partner organizations invite <id> [--owner-email owner@example.com]
399
+ shipmail partner organizations suspend <id>
400
+ shipmail partner organizations resume <id>
401
+ shipmail partner organizations offboard <id>
402
+ shipmail partner usage
403
+
404
+ Booking page conferencing:
405
+ --conferencing-provider <zoom|google_meet> Create a meeting for every booking.
406
+ --conferencing-provider none Stop creating meeting links on update.
385
407
 
386
408
  Global options:
387
409
  --api-key <key> API key. Defaults to SHIPMAIL_API_KEY.
388
410
  --base-url <url> API base URL. Defaults to SHIPMAIL_BASE_URL or ${DEFAULT_BASE_URL}.
411
+ --organization-id Delegated child organization for supported infrastructure commands.
389
412
  --header "K: V" Extra header for the generic api command. Repeatable.
390
413
 
391
414
  Environment:
392
415
  SHIPMAIL_API_KEY API key for authenticated commands.
393
416
  SHIPMAIL_BASE_URL Optional API base URL.
417
+ SHIPMAIL_ORGANIZATION_ID Optional delegated child organization ID.
394
418
  `;
395
419
  }
396
420
  function requirePositional(positional, index, label) {
@@ -591,20 +615,25 @@ async function runMailboxes(client, parsed, out) {
591
615
  const displayName = getString(parsed.options, "display-name");
592
616
  const passwordValue = getString(parsed.options, "password");
593
617
  const passwordFile = getString(parsed.options, "password-file");
594
- if (passwordValue && passwordFile) {
595
- throw new CliUsageError("Pass either --password or --password-file, not both.");
618
+ const generatePassword = getBoolean(parsed.options, "generate-password") === true;
619
+ if ([Boolean(passwordValue), Boolean(passwordFile), generatePassword].filter(Boolean).length > 1) {
620
+ throw new CliUsageError(
621
+ "Pass only one of --password, --password-file, or --generate-password."
622
+ );
596
623
  }
597
- if (!passwordValue && !passwordFile) {
598
- throw new CliUsageError("Missing --password or --password-file.");
624
+ if (!passwordValue && !passwordFile && !generatePassword) {
625
+ throw new CliUsageError("Missing --password, --password-file, or --generate-password.");
599
626
  }
600
627
  const password = passwordFile ? (await readFile(passwordFile, "utf8")).replace(/\r?\n$/, "") : passwordValue;
601
- if (!password) throw new CliUsageError("Mailbox password must not be empty.");
628
+ if (!generatePassword && !password) {
629
+ throw new CliUsageError("Mailbox password must not be empty.");
630
+ }
602
631
  writeJson(
603
632
  out,
604
633
  await client.mailboxes.create({
605
634
  domain_id: getRequiredString(parsed.options, "domain-id"),
606
635
  address: getRequiredString(parsed.options, "address"),
607
- password,
636
+ ...generatePassword ? { generate_password: true } : { password },
608
637
  ...displayName ? { display_name: displayName } : {}
609
638
  })
610
639
  );
@@ -1222,6 +1251,12 @@ function getEnum(options, key, allowed) {
1222
1251
  }
1223
1252
  return match;
1224
1253
  }
1254
+ function getConferencingProvider(options) {
1255
+ const value = getString(options, "conferencing-provider");
1256
+ if (value === void 0) return void 0;
1257
+ if (value === "none") return null;
1258
+ return getEnum(options, "conferencing-provider", CONFERENCING_PROVIDER_IDS);
1259
+ }
1225
1260
  function isReminderArray(value) {
1226
1261
  return Array.isArray(value) && value.every(
1227
1262
  (item) => typeof item === "object" && item !== null && !Array.isArray(item) && Number.isInteger(item.minutes_before)
@@ -1387,6 +1422,7 @@ async function runBookingPages(client, parsed, out) {
1387
1422
  buffer_minutes: getNumber(options, "buffer-minutes"),
1388
1423
  minimum_notice_minutes: getNumber(options, "minimum-notice-minutes"),
1389
1424
  max_advance_days: getNumber(options, "max-advance-days"),
1425
+ conferencing_provider: getConferencingProvider(options),
1390
1426
  active: getBoolean(options, "active")
1391
1427
  };
1392
1428
  writeJson(out, await client.bookingPages.create(params));
@@ -1415,6 +1451,7 @@ async function runBookingPages(client, parsed, out) {
1415
1451
  buffer_minutes: getNumber(options, "buffer-minutes"),
1416
1452
  minimum_notice_minutes: getNumber(options, "minimum-notice-minutes"),
1417
1453
  max_advance_days: getNumber(options, "max-advance-days"),
1454
+ conferencing_provider: getConferencingProvider(options),
1418
1455
  active: getBoolean(options, "active")
1419
1456
  };
1420
1457
  writeJson(out, await client.bookingPages.update(id, params));
@@ -1428,6 +1465,74 @@ async function runBookingPages(client, parsed, out) {
1428
1465
  }
1429
1466
  throw new CliUsageError("Unknown booking-pages command.");
1430
1467
  }
1468
+ async function runPartner(client, parsed, out) {
1469
+ const group = parsed.positional[1];
1470
+ if (group === "usage") {
1471
+ writeJson(out, await client.partner.usage());
1472
+ return;
1473
+ }
1474
+ if (group !== "organizations") throw new CliUsageError("Unknown partner command.");
1475
+ const command = parsed.positional[2];
1476
+ if (command === "list") {
1477
+ writeJson(out, await client.partner.listOrganizations());
1478
+ return;
1479
+ }
1480
+ if (command === "create") {
1481
+ const classification = getString(parsed.options, "data-classification") ?? "customer";
1482
+ if (classification !== "internal_test" && classification !== "customer") {
1483
+ throw new CliUsageError("--data-classification must be internal_test or customer.");
1484
+ }
1485
+ const params = {
1486
+ name: getRequiredString(parsed.options, "name"),
1487
+ external_reference: getRequiredString(parsed.options, "external-reference"),
1488
+ owner_email: getRequiredString(parsed.options, "owner-email"),
1489
+ mailbox_limit: getNumber(parsed.options, "mailbox-limit") ?? 3,
1490
+ data_classification: classification
1491
+ };
1492
+ writeJson(out, await client.partner.createOrganization(params));
1493
+ return;
1494
+ }
1495
+ const id = requirePositional(parsed.positional, 3, "partner organization id");
1496
+ if (command === "get") {
1497
+ writeJson(out, await client.partner.getOrganization(id));
1498
+ return;
1499
+ }
1500
+ if (command === "update") {
1501
+ const name = getString(parsed.options, "name");
1502
+ const mailboxLimit = getNumber(parsed.options, "mailbox-limit");
1503
+ if (name === void 0 && mailboxLimit === void 0) {
1504
+ throw new CliUsageError("Provide --name or --mailbox-limit.");
1505
+ }
1506
+ const params = {
1507
+ ...name !== void 0 ? { name } : {},
1508
+ ...mailboxLimit !== void 0 ? { mailbox_limit: mailboxLimit } : {}
1509
+ };
1510
+ writeJson(out, await client.partner.updateOrganization(id, params));
1511
+ return;
1512
+ }
1513
+ if (command === "invite") {
1514
+ writeJson(
1515
+ out,
1516
+ await client.partner.resendOwnershipInvitation(id, {
1517
+ owner_email: getString(parsed.options, "owner-email")
1518
+ })
1519
+ );
1520
+ return;
1521
+ }
1522
+ if (command === "suspend") {
1523
+ writeJson(out, await client.partner.suspendOrganization(id));
1524
+ return;
1525
+ }
1526
+ if (command === "resume") {
1527
+ writeJson(out, await client.partner.resumeOrganization(id));
1528
+ return;
1529
+ }
1530
+ if (command === "offboard") {
1531
+ writeJson(out, await client.partner.offboardOrganization(id));
1532
+ return;
1533
+ }
1534
+ throw new CliUsageError("Unknown partner organizations command.");
1535
+ }
1431
1536
  function writeError(err, errOut) {
1432
1537
  if (err instanceof CliUsageError) {
1433
1538
  errOut.write(`Error: ${err.message}
@@ -1493,6 +1598,7 @@ async function runCli(argv, options = {}) {
1493
1598
  else if (command === "audiences") await runAudiences(client, parsed, out);
1494
1599
  else if (command === "calendar") await runCalendar(client, parsed, out);
1495
1600
  else if (command === "booking-pages") await runBookingPages(client, parsed, out);
1601
+ else if (command === "partner") await runPartner(client, parsed, out);
1496
1602
  else throw new CliUsageError(`Unknown command: ${command}.`);
1497
1603
  return 0;
1498
1604
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shipmail-cli",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
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.32"
44
+ "shipmail": "0.1.33"
45
45
  },
46
46
  "devDependencies": {
47
47
  "tsup": "^8.4.0",