groundmail-cli 0.43.0 → 0.44.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 +10 -0
- package/dist/cli.js +304 -194
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -147,6 +147,16 @@ groundmail suppressions list --type category_opt_out --tenant cus_123
|
|
|
147
147
|
groundmail suppressions remove --type category_opt_out --email reader@example.com --category newsletter --tenant cus_123
|
|
148
148
|
```
|
|
149
149
|
|
|
150
|
+
## Updating
|
|
151
|
+
|
|
152
|
+
The CLI tells you when a newer version is out, at most once a day and never in CI or a pipe. To update:
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
npm install -g groundmail-cli@latest
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Set `GROUNDMAIL_NO_UPDATE_NOTIFIER=1` to turn the notice off.
|
|
159
|
+
|
|
150
160
|
## Docs
|
|
151
161
|
|
|
152
162
|
- CLI: https://docs.groundmail.dev/cli
|
package/dist/cli.js
CHANGED
|
@@ -39,10 +39,10 @@ function idLike(token) {
|
|
|
39
39
|
}
|
|
40
40
|
function shortenReply(reply, max) {
|
|
41
41
|
if (reply.length <= LABEL_WIDTH) return reply;
|
|
42
|
-
const
|
|
42
|
+
const parts2 = reply.split(/(\s+)/);
|
|
43
43
|
const out = [];
|
|
44
44
|
let inRun = false;
|
|
45
|
-
for (const part of
|
|
45
|
+
for (const part of parts2) {
|
|
46
46
|
if (/^\s+$/.test(part)) {
|
|
47
47
|
if (!inRun) out.push(part);
|
|
48
48
|
continue;
|
|
@@ -2089,6 +2089,213 @@ async function runListKeysCommandWithClient(args, env = process.env) {
|
|
|
2089
2089
|
}
|
|
2090
2090
|
}
|
|
2091
2091
|
|
|
2092
|
+
// package.json
|
|
2093
|
+
var version = "0.44.0";
|
|
2094
|
+
|
|
2095
|
+
// src/help.ts
|
|
2096
|
+
var USAGE_LINES = [
|
|
2097
|
+
" groundmail login [--scope <scopes>] [--workspace <name>] sign in through the browser (OAuth; no key to copy)",
|
|
2098
|
+
" groundmail logout [--workspace <name>|--all] revokes the sign-in server-side",
|
|
2099
|
+
" groundmail whoami",
|
|
2100
|
+
" groundmail auth token [--scope <scopes>] print a short-lived access token (for agents and scripts)",
|
|
2101
|
+
" groundmail workspaces list",
|
|
2102
|
+
" groundmail workspaces use <name|id>",
|
|
2103
|
+
" groundmail domains add example.com [--tenant <tenant>]",
|
|
2104
|
+
" groundmail domains apply example.com --provider cloudflare",
|
|
2105
|
+
" groundmail domains apply example.com --provider vercel",
|
|
2106
|
+
" groundmail domains apply example.com --provider godaddy",
|
|
2107
|
+
" groundmail domains list [--tenant <tenant>]",
|
|
2108
|
+
" groundmail domains get example.com",
|
|
2109
|
+
" groundmail domains update example.com --inbound true",
|
|
2110
|
+
" groundmail domains verify example.com",
|
|
2111
|
+
" groundmail domains delete example.com",
|
|
2112
|
+
" groundmail dmarc status example.com [--json] DMARC monitoring: sources, pass rates, readiness",
|
|
2113
|
+
" groundmail dmarc enable example.com [--apply cloudflare|vercel|godaddy|netlify|digitalocean|dnsimple] report address and the record to publish",
|
|
2114
|
+
" groundmail dmarc digest example.com --on|--off weekly digest email",
|
|
2115
|
+
" groundmail dmarc disable example.com",
|
|
2116
|
+
" groundmail received-emails list [--domain inbound.example.com] [--in-reply-to-email msg_...]",
|
|
2117
|
+
" groundmail received-emails get inb_...",
|
|
2118
|
+
" groundmail test-inbox address [--prefix signup] a fresh address on your team's test domain (mail there is caught, never delivered)",
|
|
2119
|
+
" groundmail test-inbox wait --to <address> [--timeout 30] [--since <iso>] [--json] wait for an email; prints its code and links (exit 2 on timeout)",
|
|
2120
|
+
" groundmail test-inbox list [--to <address>] [--since <iso>] [--json]",
|
|
2121
|
+
" groundmail mailboxes create --instant [--owner-email you@example.com] no account or key needed",
|
|
2122
|
+
" groundmail mailboxes owner <mailbox> --owner-email you@example.com send your person a code",
|
|
2123
|
+
" groundmail mailboxes verify <mailbox> --code 482913 unlock sending",
|
|
2124
|
+
" groundmail mailboxes create [--local-part signup-bot] [--domain agents.example.com] [--display-name \u2026] [--external-id \u2026] [--tenant <tenant>] [--retention-days 7] [--spam-threshold 5] [--metadata JSON] [--forward-from <mailbox>] [--json] a real address your agent reads and sends from; no --domain: a hosted address, which can only reply; --forward-from: a hosted mailbox this one replaces",
|
|
2125
|
+
" groundmail mailboxes list [--tenant <tenant>] [--limit 25] [--cursor \u2026] [--json]",
|
|
2126
|
+
" groundmail mailboxes get <mailbox> [--json]",
|
|
2127
|
+
" groundmail mailboxes delete <mailbox> --yes the mailbox and all its mail; a hosted address is never given out again",
|
|
2128
|
+
" groundmail mailboxes forward <mailbox> --to <new mailbox> --yes move a hosted address to a mailbox on your own domain, for 30 days; it can't send meanwhile",
|
|
2129
|
+
" groundmail mailboxes threads <mailbox> [--label \u2026] [--participant \u2026] [--unread] [--archived] [--since <iso>] [--include-held] [--limit 25] [--cursor \u2026] [--json]",
|
|
2130
|
+
" groundmail mailboxes read <mailbox> [mbm_\u2026|thd_\u2026] [--search \u2026] [--from \u2026] [--thread thd_\u2026] [--label \u2026] [--unread] [--since <iso>] [--include-held] [--limit 25] [--cursor \u2026] [--json] one message or thread; with no id, list or search messages",
|
|
2131
|
+
" groundmail mailboxes wait <mailbox> [--from \u2026] [--thread thd_\u2026] [--since <iso>] [--timeout 30] [--json] wait for the next message; prints its code and links (exit 2 on timeout)",
|
|
2132
|
+
" groundmail mailboxes send <mailbox> --to a@example.com,b@example.com [--cc \u2026] --subject \u2026 (--text \u2026 | --html \u2026) [--idempotency-key \u2026] [--json] email someone first, from a mailbox on your own domain",
|
|
2133
|
+
" groundmail mailboxes reply <mailbox> mbm_\u2026 (--text \u2026 | --html \u2026) [--reply-all] [--idempotency-key \u2026] [--json] reply in the thread",
|
|
2134
|
+
` groundmail emails send [--from you@domain.com | --sender billing] --to user@example.com --subject "Hello" --html "<p>Hi</p>" [--category transactional|product|newsletter] [--unsubscribe] [--tenant <tenant>] [--watch [--timeout 60] [--json]] --sender: a saved sender's handle; no --from or --sender: the sandbox sender (to your account email only)`,
|
|
2135
|
+
' groundmail emails send --to you@example.com --subject "Hello" --text "Hi" --watch watch it land: the receiving server, its reply and the time',
|
|
2136
|
+
" groundmail emails batch --file ./emails.json [--tenant <tenant>]",
|
|
2137
|
+
" groundmail emails list [--limit 25] [--cursor \u2026] [--test | --live] [--tenant <tenant>] [--stream transactional|product|newsletter]",
|
|
2138
|
+
" groundmail emails insights [--window 7d|30d]",
|
|
2139
|
+
" groundmail emails get --id msg_\u2026 [--watch [--timeout 60] [--json]] --watch: follow an email you already sent until it lands",
|
|
2140
|
+
" groundmail emails cancel --id msg_\u2026",
|
|
2141
|
+
" groundmail batches get <batch_id> an emails batch's status counts (the batch_id `emails batch` returned)",
|
|
2142
|
+
" groundmail batches cancel <batch_id> cancel the batch's emails that are still scheduled; safe to repeat",
|
|
2143
|
+
" groundmail emails resend --id msg_\u2026",
|
|
2144
|
+
` groundmail events send --name user.created --email user@example.com --data '{"name":"Ada"}'`,
|
|
2145
|
+
" groundmail automations list",
|
|
2146
|
+
" groundmail automations create --file ./welcome.json",
|
|
2147
|
+
` groundmail automations create --name Welcome --trigger user.created --steps '[{"type":"send_email","email":{"from":"you@yourdomain.com","to":"{{event.email}}","template":{"id":"welcome","variables":{"name":"{{event.data.name}}"}}}}]' each email step sends a published template`,
|
|
2148
|
+
" groundmail automations create --file ./otp-sms.json",
|
|
2149
|
+
" groundmail automations get --id <automation-id>",
|
|
2150
|
+
" groundmail automations update --id <automation-id> --file ./welcome.json",
|
|
2151
|
+
' groundmail automations update --id <automation-id> --folder Onboarding (--folder "" takes it out of any folder)',
|
|
2152
|
+
" groundmail automations activate --id <automation-id>",
|
|
2153
|
+
" groundmail automations pause --id <automation-id>",
|
|
2154
|
+
" groundmail automations delete --id <automation-id>",
|
|
2155
|
+
" groundmail webhooks list",
|
|
2156
|
+
" groundmail webhooks create --url https://yourapp.com/hooks [--tenants a,b] [--mailboxes mbx_a,mbx_b] --tenants: only these tenants' email events; --mailboxes: only these mailboxes' events",
|
|
2157
|
+
" groundmail webhooks get --id wh_\u2026",
|
|
2158
|
+
" groundmail webhooks update --id wh_\u2026 [--url \u2026] [--event email.sent] [--enabled true|false] [--tenants a,b|all] [--mailboxes mbx_a,mbx_b|all]",
|
|
2159
|
+
" groundmail webhooks delete --id <webhook-id>",
|
|
2160
|
+
" groundmail webhooks deliveries list --webhook <webhook-id> [--status failed] [--event email.delivered] [--email-id msg_\u2026] [--q \u2026] [--test true|false] [--limit 25] [--cursor \u2026]",
|
|
2161
|
+
" groundmail webhooks deliveries get --webhook <webhook-id> --id <delivery-id>",
|
|
2162
|
+
" groundmail webhooks deliveries resend --webhook <webhook-id> --id <delivery-id>",
|
|
2163
|
+
" groundmail validate user@example.com Groundmail validation: verdict, reasons, typo suggestion (exit 2 when invalid)",
|
|
2164
|
+
" groundmail burrowguard sites the forms BurrowGuard protects",
|
|
2165
|
+
' groundmail burrowguard sites create --name "Sign-up form" --hostname app.example.com [--hostname localhost] [--block-free-webmail] [--allow-data-center-ips] [--email-checks-only] [--watch-only] prints the secret once',
|
|
2166
|
+
" groundmail burrowguard sites update <id> --watch-only | --no-watch-only show what would be blocked without blocking anyone, or start blocking",
|
|
2167
|
+
" groundmail burrowguard sites roll-secret <id> --yes the old secret stops working at once",
|
|
2168
|
+
" groundmail burrowguard sites delete <id> --yes",
|
|
2169
|
+
" groundmail burrowguard checks [--site <id>] [--result passed|blocked|would_block|no_token] [--range 24h|7d|30d] [--limit 25]",
|
|
2170
|
+
" groundmail suppressions list [--tenant <tenant>]",
|
|
2171
|
+
" groundmail suppressions add --email user@example.com [--reason \u2026] [--tenant <tenant>]",
|
|
2172
|
+
" groundmail suppressions remove --id <id> | --email user@example.com [--tenant <tenant>]",
|
|
2173
|
+
" groundmail suppressions list --type category_opt_out [--category product|newsletter] [--tenant <tenant> | --include-tenants] one-click category unsubscribes",
|
|
2174
|
+
" groundmail suppressions remove --type category_opt_out --email user@example.com [--category newsletter] [--tenant <tenant>] re-subscribe to a category",
|
|
2175
|
+
" groundmail templates list [--status draft|published]",
|
|
2176
|
+
" groundmail templates starters",
|
|
2177
|
+
" groundmail templates from-starter password-reset",
|
|
2178
|
+
' groundmail templates create --name Welcome --subject "Hi" --html "<p>Hi</p>" [--category transactional|product] [--unsubscribe-enabled] [--folder Onboarding] [--sender billing]',
|
|
2179
|
+
" groundmail templates get --id tpl_\u2026",
|
|
2180
|
+
" groundmail templates update --id tpl_\u2026 [--name \u2026] [--subject \u2026] [--html \u2026] [--text \u2026] [--set-alias \u2026] [--category transactional|product] [--unsubscribe-enabled|--no-unsubscribe-enabled] [--folder \u2026] [--sender billing|none]",
|
|
2181
|
+
" groundmail templates publish --id tpl_\u2026",
|
|
2182
|
+
` groundmail templates push ./emails/welcome.tsx --alias welcome [--subject "Welcome"] [--props '{"name":"Ada"}'] [--publish]`,
|
|
2183
|
+
" groundmail templates versions --id tpl_\u2026",
|
|
2184
|
+
" groundmail templates restore --id tpl_\u2026 --version 2",
|
|
2185
|
+
" groundmail templates test-send --id tpl_\u2026 --to you@domain.com [--from you@domain.com | --sender billing] neither: the template's sender, else your default sender",
|
|
2186
|
+
' groundmail templates duplicate --id tpl_\u2026 [--name "Copy"]',
|
|
2187
|
+
" groundmail templates delete --id tpl_\u2026 refused while an automation step sends it (the error names them)",
|
|
2188
|
+
" groundmail tenants list [--status active|paused] [--limit 25] [--cursor \u2026] your customers, each with its own domains, suppressions and limits",
|
|
2189
|
+
` groundmail tenants create <external-id> [--name \u2026] [--default-from-domain \u2026] [--stream-policy all|transactional_only] [--daily-limit N] [--metadata '{"plan":"pro"}']`,
|
|
2190
|
+
" groundmail tenants get <tenant>",
|
|
2191
|
+
" groundmail tenants update <tenant> [--name \u2026] [--default-from-domain \u2026|none] [--stream-policy \u2026] [--daily-limit N|none] [--metadata JSON|null]",
|
|
2192
|
+
" groundmail tenants pause <tenant> [--reason \u2026] stops the tenant's new sends",
|
|
2193
|
+
" groundmail tenants resume <tenant>",
|
|
2194
|
+
" groundmail tenants delete <tenant> --yes",
|
|
2195
|
+
" groundmail tenants stats <tenant> [--days 7]",
|
|
2196
|
+
" groundmail tenants domains list <tenant>",
|
|
2197
|
+
" groundmail tenants domains add <tenant> example.com only this tenant can then send from it",
|
|
2198
|
+
" groundmail tenants domains remove <tenant> example.com gives the domain back to the account",
|
|
2199
|
+
" groundmail brand get your logo, colors and footer, which templates follow",
|
|
2200
|
+
' groundmail brand set [--logo-url <https url>|none] [--logo-align left|center] [--logo-width 120] [--color #2f6feb] [--company "Acme Inc"|none] [--address "\u2026"|none] [--website acme.com|none] [--settings-file brand.json] --color: buttons, links and the line across the top',
|
|
2201
|
+
" groundmail senders list the names and addresses your email goes out as",
|
|
2202
|
+
' groundmail senders create --name "Acme Billing" --address billing@mail.acme.com [--reply-to billing@acme.com] [--handle billing] [--default] address on a verified domain',
|
|
2203
|
+
" groundmail senders get <id|handle>",
|
|
2204
|
+
" groundmail senders update <id|handle> [--name \u2026] [--address \u2026] [--reply-to \u2026|none] [--handle \u2026] [--default|--no-default]",
|
|
2205
|
+
" groundmail senders delete <id|handle> --yes refused while an automation or template uses it",
|
|
2206
|
+
" groundmail keys list",
|
|
2207
|
+
" groundmail keys create [--name Default] [--scopes emails:send,domains:read] [--allowed-domains example.com] [--test] [--tenant <tenant>] [--mailboxes mbx_a,mbx_b] --test: a gm_test_ key, sends are simulated; --tenant: a key limited to one tenant; --mailboxes: a key for one agent, limited to its mailboxes",
|
|
2208
|
+
" groundmail keys delete --id <api-key-id>",
|
|
2209
|
+
" groundmail migrate sendgrid [--dry-run] [--only templates,suppressions,domains,webhooks] [--region eu] import from SendGrid (Beta)",
|
|
2210
|
+
" groundmail migrate postmark [--dry-run] [--only \u2026] import from Postmark (Beta)",
|
|
2211
|
+
" groundmail migrate mailgun [--dry-run] [--only \u2026] [--region eu] import from Mailgun (Beta)",
|
|
2212
|
+
" groundmail migrate resend [--dry-run] [--only \u2026] import from Resend (Beta; a Full access key)",
|
|
2213
|
+
" groundmail migrate ses --region us-east-1 [--dry-run] [--only \u2026] import one Amazon SES region (Beta)",
|
|
2214
|
+
" groundmail migrate list your recent imports",
|
|
2215
|
+
" groundmail migrate report <id> [--csv] an import report (JSON, or CSV to stdout)",
|
|
2216
|
+
" groundmail integrations twilio status",
|
|
2217
|
+
" groundmail integrations twilio connect --account-sid AC\u2026 --auth-token \u2026 --from +15551234567",
|
|
2218
|
+
" groundmail integrations twilio disconnect"
|
|
2219
|
+
];
|
|
2220
|
+
var FOOTER_LINES = [
|
|
2221
|
+
"Environment:",
|
|
2222
|
+
" GROUNDMAIL_API_KEY=gm_\u2026 optional (CI): wins over the `groundmail login` sign-in",
|
|
2223
|
+
" GROUNDMAIL_NO_KEYCHAIN=1 keep the refresh token in the config file (0600) instead of the OS keychain",
|
|
2224
|
+
" GROUNDMAIL_NO_UPDATE_NOTIFIER=1 turn off the update notice",
|
|
2225
|
+
" TWILIO_ACCOUNT_SID=AC\u2026 TWILIO_AUTH_TOKEN=\u2026 TWILIO_DEFAULT_FROM=+1\u2026 (integrations twilio connect)",
|
|
2226
|
+
" CLOUDFLARE_API_TOKEN=\u2026 (optional local apply; else uses dashboard Integrations)",
|
|
2227
|
+
" VERCEL_API_TOKEN=\u2026 VERCEL_TEAM_ID=\u2026 (optional local apply; else uses dashboard Integrations)",
|
|
2228
|
+
" GODADDY_PAT=\u2026 or GODADDY_API_KEY=\u2026 GODADDY_API_SECRET=\u2026 (domains apply --provider godaddy)",
|
|
2229
|
+
" SENDGRID_API_KEY / POSTMARK_SERVER_TOKEN (+ POSTMARK_ACCOUNT_TOKEN) / MAILGUN_API_KEY / RESEND_API_KEY (migrate; used once, never stored)",
|
|
2230
|
+
" AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (+ AWS_SESSION_TOKEN, AWS_REGION) (migrate ses; used once, never stored)",
|
|
2231
|
+
"",
|
|
2232
|
+
"Scopes: full, emails:send, emails:read, domains:read, domains:write, webhooks:write, templates:write,",
|
|
2233
|
+
" suppressions:write, account:read, mailboxes:read, mailboxes:write, mailboxes:send",
|
|
2234
|
+
"",
|
|
2235
|
+
"<tenant>: the tenant's external id (your id for that customer) or its Groundmail id.",
|
|
2236
|
+
"<mailbox>: the mailbox's mbx_ id, its address or your external id.",
|
|
2237
|
+
"",
|
|
2238
|
+
"--watch exit codes: 0 delivered, 1 error, 3 bounced or failed, 4 still on its way at --timeout, 5 test send (never delivered).",
|
|
2239
|
+
"",
|
|
2240
|
+
"Add --help to any command for just its usage. --version prints the CLI version."
|
|
2241
|
+
];
|
|
2242
|
+
var CLI_VERSION = version;
|
|
2243
|
+
var ALIASES = {
|
|
2244
|
+
workspace: { "": "workspaces" },
|
|
2245
|
+
domains: { create: "add", remove: "delete" },
|
|
2246
|
+
events: { trigger: "send" },
|
|
2247
|
+
automations: { add: "create", remove: "delete" },
|
|
2248
|
+
suppressions: { create: "add", delete: "remove" },
|
|
2249
|
+
templates: { add: "create", remove: "delete" },
|
|
2250
|
+
tenants: { add: "create", remove: "delete" },
|
|
2251
|
+
mailboxes: { add: "create", remove: "delete" },
|
|
2252
|
+
senders: { add: "create", remove: "delete" },
|
|
2253
|
+
brand: { update: "set" }
|
|
2254
|
+
};
|
|
2255
|
+
function words(line) {
|
|
2256
|
+
return line.trim().split(/\s+/);
|
|
2257
|
+
}
|
|
2258
|
+
function fullUsage() {
|
|
2259
|
+
return ["Usage:", ...USAGE_LINES, "", ...FOOTER_LINES].join("\n");
|
|
2260
|
+
}
|
|
2261
|
+
function unknownCommandMessage(argv) {
|
|
2262
|
+
const typed = argv.filter((arg) => !arg.startsWith("-")).slice(0, 2).join(" ");
|
|
2263
|
+
return `Unknown command: groundmail ${typed}
|
|
2264
|
+
${outdatedHint()}`;
|
|
2265
|
+
}
|
|
2266
|
+
function outdatedHint() {
|
|
2267
|
+
return `You're on groundmail-cli ${CLI_VERSION}. If the docs show this, your version may be out of date: npm install -g groundmail-cli@latest`;
|
|
2268
|
+
}
|
|
2269
|
+
function usageFor(command, subcommand) {
|
|
2270
|
+
if (!command) return fullUsage();
|
|
2271
|
+
const name = ALIASES[command]?.[""] ?? command;
|
|
2272
|
+
const forCommand = USAGE_LINES.filter((line) => words(line)[1] === name);
|
|
2273
|
+
if (!forCommand.length) return null;
|
|
2274
|
+
const sub = subcommand && !subcommand.startsWith("-") ? ALIASES[name]?.[subcommand] ?? subcommand : void 0;
|
|
2275
|
+
const forSub = sub ? forCommand.filter((line) => words(line)[2] === sub) : [];
|
|
2276
|
+
return ["Usage:", ...forSub.length ? forSub : forCommand].join("\n");
|
|
2277
|
+
}
|
|
2278
|
+
function isHelpFlag(arg) {
|
|
2279
|
+
return arg === "--help" || arg === "-h";
|
|
2280
|
+
}
|
|
2281
|
+
function isVersionFlag(arg) {
|
|
2282
|
+
return arg === "--version" || arg === "-v" || arg === "-V";
|
|
2283
|
+
}
|
|
2284
|
+
function resolveMetaRequest(argv) {
|
|
2285
|
+
const [command, subcommand] = argv;
|
|
2286
|
+
if (isVersionFlag(command) || command === "version") return { kind: "version", text: CLI_VERSION };
|
|
2287
|
+
if (!command) return null;
|
|
2288
|
+
if (isHelpFlag(command)) return { kind: "help", text: fullUsage() };
|
|
2289
|
+
if (command === "help") {
|
|
2290
|
+
return { kind: "help", text: usageFor(argv[1], argv[2]) ?? fullUsage() };
|
|
2291
|
+
}
|
|
2292
|
+
if (argv.some(isHelpFlag)) {
|
|
2293
|
+
const sub = isHelpFlag(subcommand) ? void 0 : subcommand;
|
|
2294
|
+
return { kind: "help", text: usageFor(command, sub) ?? fullUsage() };
|
|
2295
|
+
}
|
|
2296
|
+
return null;
|
|
2297
|
+
}
|
|
2298
|
+
|
|
2092
2299
|
// src/session/brand.ts
|
|
2093
2300
|
var GROUNDMAIL = {
|
|
2094
2301
|
bin: "groundmail",
|
|
@@ -2112,9 +2319,6 @@ function trimUrl(value) {
|
|
|
2112
2319
|
return (value ?? "").trim().replace(/\/+$/, "");
|
|
2113
2320
|
}
|
|
2114
2321
|
|
|
2115
|
-
// package.json
|
|
2116
|
-
var version = "0.43.0";
|
|
2117
|
-
|
|
2118
2322
|
// src/user-agent.ts
|
|
2119
2323
|
var AGENT_ENV = [
|
|
2120
2324
|
["claude-code", ["CLAUDECODE", "CLAUDE_CODE"]],
|
|
@@ -2993,7 +3197,23 @@ function readFlag17(args, flag) {
|
|
|
2993
3197
|
if (index === -1) return void 0;
|
|
2994
3198
|
return args[index + 1];
|
|
2995
3199
|
}
|
|
3200
|
+
var KNOWN_FLAGS = /* @__PURE__ */ new Set([
|
|
3201
|
+
"--api-key",
|
|
3202
|
+
"--base-url",
|
|
3203
|
+
"--name",
|
|
3204
|
+
"--scope",
|
|
3205
|
+
"--scopes",
|
|
3206
|
+
"--allowed-domains",
|
|
3207
|
+
"--test",
|
|
3208
|
+
"--tenant",
|
|
3209
|
+
"--mailboxes"
|
|
3210
|
+
]);
|
|
2996
3211
|
async function runCreateKeyCommandWithClient(args, env = process.env) {
|
|
3212
|
+
const unknown = args.find((arg) => arg.startsWith("--") && !KNOWN_FLAGS.has(arg.split("=")[0]));
|
|
3213
|
+
if (unknown) {
|
|
3214
|
+
return { exitCode: 1, error: `Unknown option ${unknown}. No key was created.
|
|
3215
|
+
${outdatedHint()}` };
|
|
3216
|
+
}
|
|
2997
3217
|
const apiKey = readFlag17(args, "--api-key") || env.GROUNDMAIL_API_KEY;
|
|
2998
3218
|
const baseUrl = readFlag17(args, "--base-url") || defaultApiUrl(env);
|
|
2999
3219
|
if (!apiKey) {
|
|
@@ -5056,8 +5276,8 @@ function formatMessage(message) {
|
|
|
5056
5276
|
}
|
|
5057
5277
|
function formatThread(thread) {
|
|
5058
5278
|
const head = [`Thread: ${thread.id}`, `Subject: ${oneLine(thread.subject)}`, `With: ${oneLine(thread.participants.join(", "))}`];
|
|
5059
|
-
const
|
|
5060
|
-
return [UNTRUSTED_LINE, ...head, ...
|
|
5279
|
+
const parts2 = thread.messages.map((message) => [...messageHeader(message), "", plain(message.reply_text ?? message.visible_text) || "(no visible text)"].join("\n"));
|
|
5280
|
+
return [UNTRUSTED_LINE, ...head, ...parts2.flatMap((part) => ["", "---", part])].join("\n");
|
|
5061
5281
|
}
|
|
5062
5282
|
function threadRow(thread) {
|
|
5063
5283
|
const unread = thread.unread_count ? ` ${thread.unread_count} unread` : "";
|
|
@@ -5289,199 +5509,81 @@ async function runMailboxesCommand(subcommand, argv, env = process.env) {
|
|
|
5289
5509
|
}
|
|
5290
5510
|
}
|
|
5291
5511
|
|
|
5292
|
-
// src/
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
" groundmail test-inbox wait --to <address> [--timeout 30] [--since <iso>] [--json] wait for an email; prints its code and links (exit 2 on timeout)",
|
|
5317
|
-
" groundmail test-inbox list [--to <address>] [--since <iso>] [--json]",
|
|
5318
|
-
" groundmail mailboxes create --instant [--owner-email you@example.com] no account or key needed",
|
|
5319
|
-
" groundmail mailboxes owner <mailbox> --owner-email you@example.com send your person a code",
|
|
5320
|
-
" groundmail mailboxes verify <mailbox> --code 482913 unlock sending",
|
|
5321
|
-
" groundmail mailboxes create [--local-part signup-bot] [--domain agents.example.com] [--display-name \u2026] [--external-id \u2026] [--tenant <tenant>] [--retention-days 7] [--spam-threshold 5] [--metadata JSON] [--forward-from <mailbox>] [--json] a real address your agent reads and sends from; no --domain: a hosted address, which can only reply; --forward-from: a hosted mailbox this one replaces",
|
|
5322
|
-
" groundmail mailboxes list [--tenant <tenant>] [--limit 25] [--cursor \u2026] [--json]",
|
|
5323
|
-
" groundmail mailboxes get <mailbox> [--json]",
|
|
5324
|
-
" groundmail mailboxes delete <mailbox> --yes the mailbox and all its mail; a hosted address is never given out again",
|
|
5325
|
-
" groundmail mailboxes forward <mailbox> --to <new mailbox> --yes move a hosted address to a mailbox on your own domain, for 30 days; it can't send meanwhile",
|
|
5326
|
-
" groundmail mailboxes threads <mailbox> [--label \u2026] [--participant \u2026] [--unread] [--archived] [--since <iso>] [--include-held] [--limit 25] [--cursor \u2026] [--json]",
|
|
5327
|
-
" groundmail mailboxes read <mailbox> [mbm_\u2026|thd_\u2026] [--search \u2026] [--from \u2026] [--thread thd_\u2026] [--label \u2026] [--unread] [--since <iso>] [--include-held] [--limit 25] [--cursor \u2026] [--json] one message or thread; with no id, list or search messages",
|
|
5328
|
-
" groundmail mailboxes wait <mailbox> [--from \u2026] [--thread thd_\u2026] [--since <iso>] [--timeout 30] [--json] wait for the next message; prints its code and links (exit 2 on timeout)",
|
|
5329
|
-
" groundmail mailboxes send <mailbox> --to a@example.com,b@example.com [--cc \u2026] --subject \u2026 (--text \u2026 | --html \u2026) [--idempotency-key \u2026] [--json] email someone first, from a mailbox on your own domain",
|
|
5330
|
-
" groundmail mailboxes reply <mailbox> mbm_\u2026 (--text \u2026 | --html \u2026) [--reply-all] [--idempotency-key \u2026] [--json] reply in the thread",
|
|
5331
|
-
` groundmail emails send [--from you@domain.com | --sender billing] --to user@example.com --subject "Hello" --html "<p>Hi</p>" [--category transactional|product|newsletter] [--unsubscribe] [--tenant <tenant>] [--watch [--timeout 60] [--json]] --sender: a saved sender's handle; no --from or --sender: the sandbox sender (to your account email only)`,
|
|
5332
|
-
' groundmail emails send --to you@example.com --subject "Hello" --text "Hi" --watch watch it land: the receiving server, its reply and the time',
|
|
5333
|
-
" groundmail emails batch --file ./emails.json [--tenant <tenant>]",
|
|
5334
|
-
" groundmail emails list [--limit 25] [--cursor \u2026] [--test | --live] [--tenant <tenant>] [--stream transactional|product|newsletter]",
|
|
5335
|
-
" groundmail emails insights [--window 7d|30d]",
|
|
5336
|
-
" groundmail emails get --id msg_\u2026 [--watch [--timeout 60] [--json]] --watch: follow an email you already sent until it lands",
|
|
5337
|
-
" groundmail emails cancel --id msg_\u2026",
|
|
5338
|
-
" groundmail batches get <batch_id> an emails batch's status counts (the batch_id `emails batch` returned)",
|
|
5339
|
-
" groundmail batches cancel <batch_id> cancel the batch's emails that are still scheduled; safe to repeat",
|
|
5340
|
-
" groundmail emails resend --id msg_\u2026",
|
|
5341
|
-
` groundmail events send --name user.created --email user@example.com --data '{"name":"Ada"}'`,
|
|
5342
|
-
" groundmail automations list",
|
|
5343
|
-
" groundmail automations create --file ./welcome.json",
|
|
5344
|
-
` groundmail automations create --name Welcome --trigger user.created --steps '[{"type":"send_email","email":{"from":"you@yourdomain.com","to":"{{event.email}}","template":{"id":"welcome","variables":{"name":"{{event.data.name}}"}}}}]' each email step sends a published template`,
|
|
5345
|
-
" groundmail automations create --file ./otp-sms.json",
|
|
5346
|
-
" groundmail automations get --id <automation-id>",
|
|
5347
|
-
" groundmail automations update --id <automation-id> --file ./welcome.json",
|
|
5348
|
-
' groundmail automations update --id <automation-id> --folder Onboarding (--folder "" takes it out of any folder)',
|
|
5349
|
-
" groundmail automations activate --id <automation-id>",
|
|
5350
|
-
" groundmail automations pause --id <automation-id>",
|
|
5351
|
-
" groundmail automations delete --id <automation-id>",
|
|
5352
|
-
" groundmail webhooks list",
|
|
5353
|
-
" groundmail webhooks create --url https://yourapp.com/hooks [--tenants a,b] [--mailboxes mbx_a,mbx_b] --tenants: only these tenants' email events; --mailboxes: only these mailboxes' events",
|
|
5354
|
-
" groundmail webhooks get --id wh_\u2026",
|
|
5355
|
-
" groundmail webhooks update --id wh_\u2026 [--url \u2026] [--event email.sent] [--enabled true|false] [--tenants a,b|all] [--mailboxes mbx_a,mbx_b|all]",
|
|
5356
|
-
" groundmail webhooks delete --id <webhook-id>",
|
|
5357
|
-
" groundmail webhooks deliveries list --webhook <webhook-id> [--status failed] [--event email.delivered] [--email-id msg_\u2026] [--q \u2026] [--test true|false] [--limit 25] [--cursor \u2026]",
|
|
5358
|
-
" groundmail webhooks deliveries get --webhook <webhook-id> --id <delivery-id>",
|
|
5359
|
-
" groundmail webhooks deliveries resend --webhook <webhook-id> --id <delivery-id>",
|
|
5360
|
-
" groundmail validate user@example.com Groundmail validation: verdict, reasons, typo suggestion (exit 2 when invalid)",
|
|
5361
|
-
" groundmail burrowguard sites the forms BurrowGuard protects",
|
|
5362
|
-
' groundmail burrowguard sites create --name "Sign-up form" --hostname app.example.com [--hostname localhost] [--block-free-webmail] [--allow-data-center-ips] [--email-checks-only] [--watch-only] prints the secret once',
|
|
5363
|
-
" groundmail burrowguard sites update <id> --watch-only | --no-watch-only show what would be blocked without blocking anyone, or start blocking",
|
|
5364
|
-
" groundmail burrowguard sites roll-secret <id> --yes the old secret stops working at once",
|
|
5365
|
-
" groundmail burrowguard sites delete <id> --yes",
|
|
5366
|
-
" groundmail burrowguard checks [--site <id>] [--result passed|blocked|would_block|no_token] [--range 24h|7d|30d] [--limit 25]",
|
|
5367
|
-
" groundmail suppressions list [--tenant <tenant>]",
|
|
5368
|
-
" groundmail suppressions add --email user@example.com [--reason \u2026] [--tenant <tenant>]",
|
|
5369
|
-
" groundmail suppressions remove --id <id> | --email user@example.com [--tenant <tenant>]",
|
|
5370
|
-
" groundmail suppressions list --type category_opt_out [--category product|newsletter] [--tenant <tenant> | --include-tenants] one-click category unsubscribes",
|
|
5371
|
-
" groundmail suppressions remove --type category_opt_out --email user@example.com [--category newsletter] [--tenant <tenant>] re-subscribe to a category",
|
|
5372
|
-
" groundmail templates list [--status draft|published]",
|
|
5373
|
-
" groundmail templates starters",
|
|
5374
|
-
" groundmail templates from-starter password-reset",
|
|
5375
|
-
' groundmail templates create --name Welcome --subject "Hi" --html "<p>Hi</p>" [--category transactional|product] [--unsubscribe-enabled] [--folder Onboarding] [--sender billing]',
|
|
5376
|
-
" groundmail templates get --id tpl_\u2026",
|
|
5377
|
-
" groundmail templates update --id tpl_\u2026 [--name \u2026] [--subject \u2026] [--html \u2026] [--text \u2026] [--set-alias \u2026] [--category transactional|product] [--unsubscribe-enabled|--no-unsubscribe-enabled] [--folder \u2026] [--sender billing|none]",
|
|
5378
|
-
" groundmail templates publish --id tpl_\u2026",
|
|
5379
|
-
` groundmail templates push ./emails/welcome.tsx --alias welcome [--subject "Welcome"] [--props '{"name":"Ada"}'] [--publish]`,
|
|
5380
|
-
" groundmail templates versions --id tpl_\u2026",
|
|
5381
|
-
" groundmail templates restore --id tpl_\u2026 --version 2",
|
|
5382
|
-
" groundmail templates test-send --id tpl_\u2026 --to you@domain.com [--from you@domain.com | --sender billing] neither: the template's sender, else your default sender",
|
|
5383
|
-
' groundmail templates duplicate --id tpl_\u2026 [--name "Copy"]',
|
|
5384
|
-
" groundmail templates delete --id tpl_\u2026 refused while an automation step sends it (the error names them)",
|
|
5385
|
-
" groundmail tenants list [--status active|paused] [--limit 25] [--cursor \u2026] your customers, each with its own domains, suppressions and limits",
|
|
5386
|
-
` groundmail tenants create <external-id> [--name \u2026] [--default-from-domain \u2026] [--stream-policy all|transactional_only] [--daily-limit N] [--metadata '{"plan":"pro"}']`,
|
|
5387
|
-
" groundmail tenants get <tenant>",
|
|
5388
|
-
" groundmail tenants update <tenant> [--name \u2026] [--default-from-domain \u2026|none] [--stream-policy \u2026] [--daily-limit N|none] [--metadata JSON|null]",
|
|
5389
|
-
" groundmail tenants pause <tenant> [--reason \u2026] stops the tenant's new sends",
|
|
5390
|
-
" groundmail tenants resume <tenant>",
|
|
5391
|
-
" groundmail tenants delete <tenant> --yes",
|
|
5392
|
-
" groundmail tenants stats <tenant> [--days 7]",
|
|
5393
|
-
" groundmail tenants domains list <tenant>",
|
|
5394
|
-
" groundmail tenants domains add <tenant> example.com only this tenant can then send from it",
|
|
5395
|
-
" groundmail tenants domains remove <tenant> example.com gives the domain back to the account",
|
|
5396
|
-
" groundmail brand get your logo, colors and footer, which templates follow",
|
|
5397
|
-
' groundmail brand set [--logo-url <https url>|none] [--logo-align left|center] [--logo-width 120] [--color #2f6feb] [--company "Acme Inc"|none] [--address "\u2026"|none] [--website acme.com|none] [--settings-file brand.json] --color: buttons, links and the line across the top',
|
|
5398
|
-
" groundmail senders list the names and addresses your email goes out as",
|
|
5399
|
-
' groundmail senders create --name "Acme Billing" --address billing@mail.acme.com [--reply-to billing@acme.com] [--handle billing] [--default] address on a verified domain',
|
|
5400
|
-
" groundmail senders get <id|handle>",
|
|
5401
|
-
" groundmail senders update <id|handle> [--name \u2026] [--address \u2026] [--reply-to \u2026|none] [--handle \u2026] [--default|--no-default]",
|
|
5402
|
-
" groundmail senders delete <id|handle> --yes refused while an automation or template uses it",
|
|
5403
|
-
" groundmail keys list",
|
|
5404
|
-
" groundmail keys create [--name Default] [--scopes emails:send,domains:read] [--allowed-domains example.com] [--test] [--tenant <tenant>] [--mailboxes mbx_a,mbx_b] --test: a gm_test_ key, sends are simulated; --tenant: a key limited to one tenant; --mailboxes: a key for one agent, limited to its mailboxes",
|
|
5405
|
-
" groundmail keys delete --id <api-key-id>",
|
|
5406
|
-
" groundmail migrate sendgrid [--dry-run] [--only templates,suppressions,domains,webhooks] [--region eu] import from SendGrid (Beta)",
|
|
5407
|
-
" groundmail migrate postmark [--dry-run] [--only \u2026] import from Postmark (Beta)",
|
|
5408
|
-
" groundmail migrate mailgun [--dry-run] [--only \u2026] [--region eu] import from Mailgun (Beta)",
|
|
5409
|
-
" groundmail migrate resend [--dry-run] [--only \u2026] import from Resend (Beta; a Full access key)",
|
|
5410
|
-
" groundmail migrate ses --region us-east-1 [--dry-run] [--only \u2026] import one Amazon SES region (Beta)",
|
|
5411
|
-
" groundmail migrate list your recent imports",
|
|
5412
|
-
" groundmail migrate report <id> [--csv] an import report (JSON, or CSV to stdout)",
|
|
5413
|
-
" groundmail integrations twilio status",
|
|
5414
|
-
" groundmail integrations twilio connect --account-sid AC\u2026 --auth-token \u2026 --from +15551234567",
|
|
5415
|
-
" groundmail integrations twilio disconnect"
|
|
5416
|
-
];
|
|
5417
|
-
var FOOTER_LINES = [
|
|
5418
|
-
"Environment:",
|
|
5419
|
-
" GROUNDMAIL_API_KEY=gm_\u2026 optional (CI): wins over the `groundmail login` sign-in",
|
|
5420
|
-
" GROUNDMAIL_NO_KEYCHAIN=1 keep the refresh token in the config file (0600) instead of the OS keychain",
|
|
5421
|
-
" TWILIO_ACCOUNT_SID=AC\u2026 TWILIO_AUTH_TOKEN=\u2026 TWILIO_DEFAULT_FROM=+1\u2026 (integrations twilio connect)",
|
|
5422
|
-
" CLOUDFLARE_API_TOKEN=\u2026 (optional local apply; else uses dashboard Integrations)",
|
|
5423
|
-
" VERCEL_API_TOKEN=\u2026 VERCEL_TEAM_ID=\u2026 (optional local apply; else uses dashboard Integrations)",
|
|
5424
|
-
" GODADDY_PAT=\u2026 or GODADDY_API_KEY=\u2026 GODADDY_API_SECRET=\u2026 (domains apply --provider godaddy)",
|
|
5425
|
-
" SENDGRID_API_KEY / POSTMARK_SERVER_TOKEN (+ POSTMARK_ACCOUNT_TOKEN) / MAILGUN_API_KEY / RESEND_API_KEY (migrate; used once, never stored)",
|
|
5426
|
-
" AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (+ AWS_SESSION_TOKEN, AWS_REGION) (migrate ses; used once, never stored)",
|
|
5427
|
-
"",
|
|
5428
|
-
"Scopes: full, emails:send, emails:read, domains:read, domains:write, webhooks:write, templates:write,",
|
|
5429
|
-
" suppressions:write, account:read, mailboxes:read, mailboxes:write, mailboxes:send",
|
|
5430
|
-
"",
|
|
5431
|
-
"<tenant>: the tenant's external id (your id for that customer) or its Groundmail id.",
|
|
5432
|
-
"<mailbox>: the mailbox's mbx_ id, its address or your external id.",
|
|
5433
|
-
"",
|
|
5434
|
-
"--watch exit codes: 0 delivered, 1 error, 3 bounced or failed, 4 still on its way at --timeout, 5 test send (never delivered).",
|
|
5435
|
-
"",
|
|
5436
|
-
"Add --help to any command for just its usage. --version prints the CLI version."
|
|
5437
|
-
];
|
|
5438
|
-
var CLI_VERSION = version;
|
|
5439
|
-
var ALIASES = {
|
|
5440
|
-
workspace: { "": "workspaces" },
|
|
5441
|
-
domains: { create: "add", remove: "delete" },
|
|
5442
|
-
events: { trigger: "send" },
|
|
5443
|
-
automations: { add: "create", remove: "delete" },
|
|
5444
|
-
suppressions: { create: "add", delete: "remove" },
|
|
5445
|
-
templates: { add: "create", remove: "delete" },
|
|
5446
|
-
tenants: { add: "create", remove: "delete" },
|
|
5447
|
-
mailboxes: { add: "create", remove: "delete" },
|
|
5448
|
-
senders: { add: "create", remove: "delete" },
|
|
5449
|
-
brand: { update: "set" }
|
|
5450
|
-
};
|
|
5451
|
-
function words(line) {
|
|
5452
|
-
return line.trim().split(/\s+/);
|
|
5453
|
-
}
|
|
5454
|
-
function fullUsage() {
|
|
5455
|
-
return ["Usage:", ...USAGE_LINES, "", ...FOOTER_LINES].join("\n");
|
|
5512
|
+
// src/update-check.ts
|
|
5513
|
+
import { spawn as spawn2 } from "child_process";
|
|
5514
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
5515
|
+
import { join as join2 } from "path";
|
|
5516
|
+
var UPDATE_COMMAND = "npm install -g groundmail-cli@latest";
|
|
5517
|
+
var REFRESH_COMMAND = "__refresh-update-check";
|
|
5518
|
+
var REGISTRY_URL = "https://registry.npmjs.org/groundmail-cli/latest";
|
|
5519
|
+
var CHECK_EVERY_MS = 24 * 60 * 60 * 1e3;
|
|
5520
|
+
var REGISTRY_TIMEOUT_MS = 3e3;
|
|
5521
|
+
function cachePath(env) {
|
|
5522
|
+
return join2(configDir(env), "update-check.json");
|
|
5523
|
+
}
|
|
5524
|
+
function parts(version2) {
|
|
5525
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version2.trim());
|
|
5526
|
+
return match ? match.slice(1).map(Number) : null;
|
|
5527
|
+
}
|
|
5528
|
+
function isNewerVersion(latest, current) {
|
|
5529
|
+
const next = parts(latest);
|
|
5530
|
+
const now = parts(current);
|
|
5531
|
+
if (!next || !now) return false;
|
|
5532
|
+
for (let index = 0; index < 3; index += 1) {
|
|
5533
|
+
if (next[index] !== now[index]) return next[index] > now[index];
|
|
5534
|
+
}
|
|
5535
|
+
return false;
|
|
5456
5536
|
}
|
|
5457
|
-
function
|
|
5458
|
-
|
|
5459
|
-
const name = ALIASES[command]?.[""] ?? command;
|
|
5460
|
-
const forCommand = USAGE_LINES.filter((line) => words(line)[1] === name);
|
|
5461
|
-
if (!forCommand.length) return null;
|
|
5462
|
-
const sub = subcommand && !subcommand.startsWith("-") ? ALIASES[name]?.[subcommand] ?? subcommand : void 0;
|
|
5463
|
-
const forSub = sub ? forCommand.filter((line) => words(line)[2] === sub) : [];
|
|
5464
|
-
return ["Usage:", ...forSub.length ? forSub : forCommand].join("\n");
|
|
5537
|
+
function updateNotice(latest, current = CLI_VERSION) {
|
|
5538
|
+
return `Update available: ${current} \u2192 ${latest}. Run: ${UPDATE_COMMAND}`;
|
|
5465
5539
|
}
|
|
5466
|
-
function
|
|
5467
|
-
|
|
5540
|
+
function updateCheckEnabled(env, stderrIsTty) {
|
|
5541
|
+
if (!stderrIsTty) return false;
|
|
5542
|
+
return !env.CI && !env.GROUNDMAIL_NO_UPDATE_NOTIFIER && !env.NO_UPDATE_NOTIFIER;
|
|
5468
5543
|
}
|
|
5469
|
-
function
|
|
5470
|
-
|
|
5544
|
+
function readUpdateCache(env) {
|
|
5545
|
+
try {
|
|
5546
|
+
const value = JSON.parse(readFileSync2(cachePath(env), "utf8"));
|
|
5547
|
+
if (typeof value.checkedAt !== "number") return null;
|
|
5548
|
+
return { checkedAt: value.checkedAt, latest: typeof value.latest === "string" ? value.latest : null };
|
|
5549
|
+
} catch {
|
|
5550
|
+
return null;
|
|
5551
|
+
}
|
|
5471
5552
|
}
|
|
5472
|
-
function
|
|
5473
|
-
|
|
5474
|
-
|
|
5475
|
-
|
|
5476
|
-
|
|
5477
|
-
|
|
5478
|
-
|
|
5553
|
+
async function refreshUpdateCache(env = process.env, fetchImpl = fetch, now = Date.now()) {
|
|
5554
|
+
let latest = readUpdateCache(env)?.latest ?? null;
|
|
5555
|
+
try {
|
|
5556
|
+
const response = await fetchImpl(REGISTRY_URL, { signal: AbortSignal.timeout(REGISTRY_TIMEOUT_MS) });
|
|
5557
|
+
if (response.ok) {
|
|
5558
|
+
const body = await response.json();
|
|
5559
|
+
if (typeof body.version === "string") latest = body.version;
|
|
5560
|
+
}
|
|
5561
|
+
} catch {
|
|
5479
5562
|
}
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5563
|
+
try {
|
|
5564
|
+
mkdirSync2(configDir(env), { recursive: true });
|
|
5565
|
+
writeFileSync2(cachePath(env), JSON.stringify({ checkedAt: now, latest }));
|
|
5566
|
+
} catch {
|
|
5567
|
+
}
|
|
5568
|
+
}
|
|
5569
|
+
function startUpdateCheck(env = process.env, now = Date.now()) {
|
|
5570
|
+
try {
|
|
5571
|
+
if (!updateCheckEnabled(env, Boolean(process.stderr.isTTY))) return;
|
|
5572
|
+
const cache = readUpdateCache(env);
|
|
5573
|
+
if (!cache || now - cache.checkedAt >= CHECK_EVERY_MS) {
|
|
5574
|
+
const script = process.argv[1];
|
|
5575
|
+
if (script) {
|
|
5576
|
+
spawn2(process.execPath, [script, REFRESH_COMMAND], { detached: true, env, stdio: "ignore" }).unref();
|
|
5577
|
+
}
|
|
5578
|
+
}
|
|
5579
|
+
const latest = cache?.latest;
|
|
5580
|
+
if (latest && isNewerVersion(latest, CLI_VERSION)) {
|
|
5581
|
+
process.once("exit", () => process.stderr.write(`
|
|
5582
|
+
${updateNotice(latest)}
|
|
5583
|
+
`));
|
|
5584
|
+
}
|
|
5585
|
+
} catch {
|
|
5483
5586
|
}
|
|
5484
|
-
return null;
|
|
5485
5587
|
}
|
|
5486
5588
|
|
|
5487
5589
|
// src/cli.ts
|
|
@@ -5490,11 +5592,17 @@ function printUsage() {
|
|
|
5490
5592
|
}
|
|
5491
5593
|
async function finish(result) {
|
|
5492
5594
|
if (result.error) console.error(result.error);
|
|
5595
|
+
if (result.exitCode && result.error?.startsWith("Usage:")) console.error(outdatedHint());
|
|
5493
5596
|
process.exit(result.exitCode);
|
|
5494
5597
|
}
|
|
5495
5598
|
async function main() {
|
|
5496
5599
|
const [, , command, subcommand] = process.argv;
|
|
5497
5600
|
const args = process.argv.slice(3);
|
|
5601
|
+
if (command === REFRESH_COMMAND) {
|
|
5602
|
+
await refreshUpdateCache();
|
|
5603
|
+
process.exit(0);
|
|
5604
|
+
}
|
|
5605
|
+
startUpdateCheck();
|
|
5498
5606
|
const meta = resolveMetaRequest(process.argv.slice(2));
|
|
5499
5607
|
if (meta) {
|
|
5500
5608
|
console.log(meta.text);
|
|
@@ -5826,6 +5934,8 @@ async function main() {
|
|
|
5826
5934
|
}
|
|
5827
5935
|
}
|
|
5828
5936
|
printUsage();
|
|
5937
|
+
if (command) console.error(`
|
|
5938
|
+
${unknownCommandMessage(process.argv.slice(2))}`);
|
|
5829
5939
|
process.exit(1);
|
|
5830
5940
|
}
|
|
5831
5941
|
main().catch((error) => {
|