groundmail-cli 0.16.3 → 0.17.0
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 +1 -0
- package/dist/cli.js +124 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -58,6 +58,7 @@ groundmail domains list
|
|
|
58
58
|
groundmail domains verify example.com
|
|
59
59
|
groundmail webhooks list
|
|
60
60
|
groundmail emails list
|
|
61
|
+
SENDGRID_API_KEY=… groundmail migrate sendgrid --dry-run # import from SendGrid, Postmark or Mailgun (beta)
|
|
61
62
|
```
|
|
62
63
|
|
|
63
64
|
Run `groundmail --help` or `groundmail <command> --help` for flags.
|
package/dist/cli.js
CHANGED
|
@@ -1288,7 +1288,7 @@ function trimUrl(value) {
|
|
|
1288
1288
|
}
|
|
1289
1289
|
|
|
1290
1290
|
// package.json
|
|
1291
|
-
var version = "0.
|
|
1291
|
+
var version = "0.17.0";
|
|
1292
1292
|
|
|
1293
1293
|
// src/user-agent.ts
|
|
1294
1294
|
function buildUserAgent(own) {
|
|
@@ -3111,6 +3111,124 @@ async function switchWorkspace(argv, deps) {
|
|
|
3111
3111
|
return { exitCode: EXIT_OK };
|
|
3112
3112
|
}
|
|
3113
3113
|
|
|
3114
|
+
// src/commands/migrate.ts
|
|
3115
|
+
var PROVIDERS = ["sendgrid", "postmark", "mailgun"];
|
|
3116
|
+
var RESOURCES = ["domains", "webhooks", "templates", "suppressions"];
|
|
3117
|
+
var KEY_ENV = {
|
|
3118
|
+
sendgrid: "SENDGRID_API_KEY",
|
|
3119
|
+
postmark: "POSTMARK_SERVER_TOKEN",
|
|
3120
|
+
mailgun: "MAILGUN_API_KEY"
|
|
3121
|
+
};
|
|
3122
|
+
var LABELS = { sendgrid: "SendGrid", postmark: "Postmark", mailgun: "Mailgun" };
|
|
3123
|
+
function readFlag21(args, flag) {
|
|
3124
|
+
const index = args.indexOf(flag);
|
|
3125
|
+
if (index === -1) return void 0;
|
|
3126
|
+
return args[index + 1];
|
|
3127
|
+
}
|
|
3128
|
+
function n(value) {
|
|
3129
|
+
return (value ?? 0).toLocaleString("en-US");
|
|
3130
|
+
}
|
|
3131
|
+
function formatMigrationSummary(m) {
|
|
3132
|
+
const label = LABELS[m.provider];
|
|
3133
|
+
const lines = [
|
|
3134
|
+
m.dry_run ? `Preview of your ${label} import (nothing written):` : `Imported from ${label} (${m.status}):`
|
|
3135
|
+
];
|
|
3136
|
+
if (m.domains) {
|
|
3137
|
+
lines.push(` Domains ${n(m.domains.totals.imported)} of ${n(m.domains.totals.found)}`);
|
|
3138
|
+
}
|
|
3139
|
+
if (m.webhooks) {
|
|
3140
|
+
lines.push(` Webhooks ${n(m.webhooks.totals.imported)} of ${n(m.webhooks.totals.found)} (created disabled)`);
|
|
3141
|
+
}
|
|
3142
|
+
if (m.templates) {
|
|
3143
|
+
const t = m.templates.totals;
|
|
3144
|
+
lines.push(` Templates ${n(t.imported)} ready, ${n(t.needs_attention)} to review, of ${n(t.found)}`);
|
|
3145
|
+
}
|
|
3146
|
+
if (m.suppressions) {
|
|
3147
|
+
const s = m.suppressions.totals;
|
|
3148
|
+
lines.push(
|
|
3149
|
+
m.dry_run ? ` Suppressions ${n(s.found)}${m.suppressions.estimated ? "+ (first page of each list)" : ""}` : ` Suppressions ${n(s.imported)} added, ${n(s.already_suppressed)} already suppressed`
|
|
3150
|
+
);
|
|
3151
|
+
}
|
|
3152
|
+
if (m.errors.length) {
|
|
3153
|
+
lines.push("", "Could not read:");
|
|
3154
|
+
for (const e of m.errors) lines.push(` ${e.resource}: ${e.message}`);
|
|
3155
|
+
}
|
|
3156
|
+
const failed = [
|
|
3157
|
+
...(m.domains?.items ?? []).filter((i) => i.status === "failed").map((i) => `domain ${i.name}: ${i.message ?? "failed"}`),
|
|
3158
|
+
...(m.webhooks?.items ?? []).filter((i) => i.status === "failed").map((i) => `webhook ${i.url}: ${i.message ?? "failed"}`),
|
|
3159
|
+
...(m.templates?.items ?? []).filter((i) => i.status === "failed").map((i) => `template ${i.name}: ${i.message ?? "failed"}`)
|
|
3160
|
+
];
|
|
3161
|
+
if (failed.length) {
|
|
3162
|
+
lines.push("", "Not imported:");
|
|
3163
|
+
for (const line of failed.slice(0, 20)) lines.push(` ${line}`);
|
|
3164
|
+
if (failed.length > 20) lines.push(` \u2026and ${failed.length - 20} more (see the JSON report)`);
|
|
3165
|
+
}
|
|
3166
|
+
if (m.attention.length) {
|
|
3167
|
+
lines.push("", "Next:");
|
|
3168
|
+
for (const line of m.attention) lines.push(` - ${line}`);
|
|
3169
|
+
}
|
|
3170
|
+
const node = m.code_changes?.find((c) => c.stack === "node");
|
|
3171
|
+
if (node) lines.push("", "Code change (Node.js):", node.diff);
|
|
3172
|
+
if (m.agent_prompt) lines.push("", "Prompt for your coding agent is in the JSON report (`agent_prompt`).");
|
|
3173
|
+
return lines.join("\n");
|
|
3174
|
+
}
|
|
3175
|
+
async function runMigrateCommand(provider, args, env = process.env) {
|
|
3176
|
+
if (!provider || !PROVIDERS.includes(provider)) {
|
|
3177
|
+
return { exitCode: 1, error: "Usage: groundmail migrate <sendgrid|postmark|mailgun> [--dry-run] [--only templates,suppressions]" };
|
|
3178
|
+
}
|
|
3179
|
+
const id = provider;
|
|
3180
|
+
const apiKey = readFlag21(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
3181
|
+
if (!apiKey) {
|
|
3182
|
+
return { exitCode: 1, error: "Not signed in. Run `groundmail login`, or set GROUNDMAIL_API_KEY." };
|
|
3183
|
+
}
|
|
3184
|
+
const providerKey = readFlag21(args, "--key") || env[KEY_ENV[id]];
|
|
3185
|
+
if (!providerKey) {
|
|
3186
|
+
return {
|
|
3187
|
+
exitCode: 1,
|
|
3188
|
+
error: `Missing ${LABELS[id]} key. Set ${KEY_ENV[id]} (or pass --key). It is used for this import only and never stored.`
|
|
3189
|
+
};
|
|
3190
|
+
}
|
|
3191
|
+
const params = { provider: id, api_key: providerKey };
|
|
3192
|
+
if (id === "postmark") {
|
|
3193
|
+
const accountToken = readFlag21(args, "--account-token") || env.POSTMARK_ACCOUNT_TOKEN;
|
|
3194
|
+
if (accountToken) params.account_api_key = accountToken;
|
|
3195
|
+
} else {
|
|
3196
|
+
const region = readFlag21(args, "--region");
|
|
3197
|
+
if (region === "us" || region === "eu") params.region = region;
|
|
3198
|
+
else if (region) return { exitCode: 1, error: "--region must be us or eu" };
|
|
3199
|
+
}
|
|
3200
|
+
const only = readFlag21(args, "--only");
|
|
3201
|
+
if (only) {
|
|
3202
|
+
const list = only.split(",").map((s) => s.trim()).filter(Boolean);
|
|
3203
|
+
const bad = list.filter((r) => !RESOURCES.includes(r));
|
|
3204
|
+
if (bad.length || !list.length) {
|
|
3205
|
+
return { exitCode: 1, error: `--only takes a comma-separated list of: ${RESOURCES.join(", ")}` };
|
|
3206
|
+
}
|
|
3207
|
+
params.resources = list;
|
|
3208
|
+
}
|
|
3209
|
+
if (args.includes("--dry-run")) params.dry_run = true;
|
|
3210
|
+
try {
|
|
3211
|
+
const { Groundmail } = await import("groundmail");
|
|
3212
|
+
const baseUrl = readFlag21(args, "--base-url");
|
|
3213
|
+
const client = new Groundmail(apiKey, baseUrl ? { baseUrl } : void 0);
|
|
3214
|
+
console.error(`${params.dry_run ? "Previewing" : "Importing"} from ${LABELS[id]} (Beta)\u2026`);
|
|
3215
|
+
const report = await client.migrations.run(params, {
|
|
3216
|
+
onProgress: (m) => {
|
|
3217
|
+
if (m.has_more) {
|
|
3218
|
+
const s = m.suppressions?.totals;
|
|
3219
|
+
console.error(` \u2026still working${s ? ` (${n(s.found)} suppressions read)` : ""}`);
|
|
3220
|
+
}
|
|
3221
|
+
}
|
|
3222
|
+
});
|
|
3223
|
+
console.log(JSON.stringify(report));
|
|
3224
|
+
console.error("");
|
|
3225
|
+
console.error(formatMigrationSummary(report));
|
|
3226
|
+
return { exitCode: report.status === "failed" ? 1 : 0 };
|
|
3227
|
+
} catch (error) {
|
|
3228
|
+
return { exitCode: 1, error: error instanceof Error ? error.message : String(error) };
|
|
3229
|
+
}
|
|
3230
|
+
}
|
|
3231
|
+
|
|
3114
3232
|
// src/help.ts
|
|
3115
3233
|
var USAGE_LINES = [
|
|
3116
3234
|
" groundmail login [--scope <scopes>] [--workspace <name>] sign in through the browser (OAuth; no key to copy)",
|
|
@@ -3171,6 +3289,9 @@ var USAGE_LINES = [
|
|
|
3171
3289
|
" groundmail keys list",
|
|
3172
3290
|
" groundmail keys create [--name Default] [--scopes emails:send,domains:read] [--allowed-domains example.com]",
|
|
3173
3291
|
" groundmail keys delete --id <api-key-id>",
|
|
3292
|
+
" groundmail migrate sendgrid [--dry-run] [--only templates,suppressions,domains,webhooks] [--region eu] import from SendGrid (Beta)",
|
|
3293
|
+
" groundmail migrate postmark [--dry-run] [--only \u2026] import from Postmark (Beta)",
|
|
3294
|
+
" groundmail migrate mailgun [--dry-run] [--only \u2026] [--region eu] import from Mailgun (Beta)",
|
|
3174
3295
|
" groundmail integrations twilio status",
|
|
3175
3296
|
" groundmail integrations twilio connect --account-sid AC\u2026 --auth-token \u2026 --from +15551234567",
|
|
3176
3297
|
" groundmail integrations twilio disconnect"
|
|
@@ -3183,6 +3304,7 @@ var FOOTER_LINES = [
|
|
|
3183
3304
|
" CLOUDFLARE_API_TOKEN=\u2026 (optional local apply; else uses dashboard Integrations)",
|
|
3184
3305
|
" VERCEL_API_TOKEN=\u2026 VERCEL_TEAM_ID=\u2026 (optional local apply; else uses dashboard Integrations)",
|
|
3185
3306
|
" GODADDY_API_KEY=\u2026 GODADDY_API_SECRET=\u2026 (domains apply --provider godaddy)",
|
|
3307
|
+
" SENDGRID_API_KEY / POSTMARK_SERVER_TOKEN (+ POSTMARK_ACCOUNT_TOKEN) / MAILGUN_API_KEY (migrate; used once, never stored)",
|
|
3186
3308
|
"",
|
|
3187
3309
|
"Scopes: full, emails:send, emails:read, domains:read, domains:write, webhooks:write, templates:write,",
|
|
3188
3310
|
" suppressions:write, account:read",
|
|
@@ -3258,6 +3380,7 @@ async function main() {
|
|
|
3258
3380
|
const session = await applySavedSession(process.argv.slice(4));
|
|
3259
3381
|
if (session.notice) console.error(session.notice);
|
|
3260
3382
|
const rest = session.argv;
|
|
3383
|
+
if (command === "migrate") await finish(await runMigrateCommand(subcommand, rest));
|
|
3261
3384
|
if (command === "domains") {
|
|
3262
3385
|
if (subcommand === "add" || subcommand === "create") {
|
|
3263
3386
|
const result = await runAddCommand(rest);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groundmail-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"description": "Groundmail CLI — send transactional email from the terminal",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"prepublishOnly": "npm run build"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"groundmail": "0.14.
|
|
41
|
+
"groundmail": "0.14.6"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"tsup": "^8.5.0",
|