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.
- package/README.md +9 -1
- package/dist/cli.js +116 -10
- 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.
|
|
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
|
-
|
|
595
|
-
|
|
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
|
|
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 (!
|
|
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.
|
|
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.
|
|
44
|
+
"shipmail": "0.1.33"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"tsup": "^8.4.0",
|