groundmail-cli 0.46.0 → 0.47.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 -1
- package/dist/cli.js +212 -20
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# groundmail-cli
|
|
2
2
|
|
|
3
|
-
Command-line client for [Groundmail](https://groundmail.dev) — send
|
|
3
|
+
Command-line client for [Groundmail](https://groundmail.dev) — send email and manage domains, webhooks, templates, and more from the terminal.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install -g groundmail-cli
|
package/dist/cli.js
CHANGED
|
@@ -2090,10 +2090,11 @@ async function runListKeysCommandWithClient(args, env = process.env) {
|
|
|
2090
2090
|
}
|
|
2091
2091
|
|
|
2092
2092
|
// package.json
|
|
2093
|
-
var version = "0.
|
|
2093
|
+
var version = "0.47.0";
|
|
2094
2094
|
|
|
2095
2095
|
// src/help.ts
|
|
2096
2096
|
var USAGE_LINES = [
|
|
2097
|
+
' groundmail cli search "<what you want to do>" [--limit 5] [--json] find the right command by describing the task; works offline, no sign-in',
|
|
2097
2098
|
" groundmail login [--scope <scopes>] [--workspace <name>] sign in through the browser (OAuth; no key to copy)",
|
|
2098
2099
|
" groundmail logout [--workspace <name>|--all] revokes the sign-in server-side",
|
|
2099
2100
|
" groundmail whoami",
|
|
@@ -2124,7 +2125,7 @@ var USAGE_LINES = [
|
|
|
2124
2125
|
" 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
2126
|
" groundmail mailboxes list [--tenant <tenant>] [--limit 25] [--cursor \u2026] [--json]",
|
|
2126
2127
|
" groundmail mailboxes get <mailbox> [--json]",
|
|
2127
|
-
" groundmail mailboxes update <mailbox> [--send-as support@example.com] [--allowed-senders you@example.com,example.com] [--blocked-senders spam.example] none clears
|
|
2128
|
+
" groundmail mailboxes update <mailbox> [--send-as support@example.com] [--allowed-senders you@example.com,example.com] [--blocked-senders spam.example] choose who can reach the mailbox and what it sends as; none clears a setting",
|
|
2128
2129
|
" groundmail mailboxes delete <mailbox> --yes the mailbox and all its mail; a hosted address is never given out again",
|
|
2129
2130
|
" 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",
|
|
2130
2131
|
" groundmail mailboxes threads <mailbox> [--label \u2026] [--participant \u2026] [--unread] [--archived] [--since <iso>] [--include-held] [--limit 25] [--cursor \u2026] [--json]",
|
|
@@ -2256,8 +2257,9 @@ var ALIASES = {
|
|
|
2256
2257
|
function words(line) {
|
|
2257
2258
|
return line.trim().split(/\s+/);
|
|
2258
2259
|
}
|
|
2260
|
+
var SEARCH_TIP = 'Not sure which command? Run: groundmail cli search "what you want to do"';
|
|
2259
2261
|
function fullUsage() {
|
|
2260
|
-
return ["Usage:", ...USAGE_LINES, "", ...FOOTER_LINES].join("\n");
|
|
2262
|
+
return [SEARCH_TIP, "", "Usage:", ...USAGE_LINES, "", ...FOOTER_LINES].join("\n");
|
|
2261
2263
|
}
|
|
2262
2264
|
function unknownCommandMessage(argv) {
|
|
2263
2265
|
const typed = argv.filter((arg) => !arg.startsWith("-")).slice(0, 2).join(" ");
|
|
@@ -2653,19 +2655,19 @@ async function accessTokenFor(workspace, apiUrl, options = {}) {
|
|
|
2653
2655
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2654
2656
|
const response = await refresh(apiUrl, refreshToken, narrowed ? options.scope.trim() : null, env);
|
|
2655
2657
|
if (response.ok && isTokenSet(response.body)) {
|
|
2656
|
-
const
|
|
2657
|
-
const stored = storeRefreshToken({ apiUrl, workspaceId: workspace.id, refreshToken:
|
|
2658
|
+
const tokens2 = response.body;
|
|
2659
|
+
const stored = storeRefreshToken({ apiUrl, workspaceId: workspace.id, refreshToken: tokens2.refresh_token, scope: oauth.scope }, env);
|
|
2658
2660
|
updateConfig((config) => {
|
|
2659
2661
|
const current = config.workspaces?.[workspace.id];
|
|
2660
2662
|
if (!current) return config;
|
|
2661
2663
|
const next = {
|
|
2662
2664
|
...stored,
|
|
2663
2665
|
scope: oauth.scope,
|
|
2664
|
-
...narrowed ? { accessToken: current.oauth?.accessToken, accessTokenExpiresAt: current.oauth?.accessTokenExpiresAt } : { accessToken:
|
|
2666
|
+
...narrowed ? { accessToken: current.oauth?.accessToken, accessTokenExpiresAt: current.oauth?.accessTokenExpiresAt } : { accessToken: tokens2.access_token, accessTokenExpiresAt: now() + tokens2.expires_in * 1e3 }
|
|
2665
2667
|
};
|
|
2666
2668
|
return { ...config, workspaces: { ...config.workspaces, [workspace.id]: { ...current, oauth: next } } };
|
|
2667
2669
|
}, env);
|
|
2668
|
-
return { ok: true, token:
|
|
2670
|
+
return { ok: true, token: tokens2.access_token, scope: tokens2.scope };
|
|
2669
2671
|
}
|
|
2670
2672
|
const code = oauthErrorCode(response);
|
|
2671
2673
|
if (code === "invalid_grant" && attempt === 0) {
|
|
@@ -2816,14 +2818,14 @@ var CLI_SCOPES = [
|
|
|
2816
2818
|
"mailboxes:send"
|
|
2817
2819
|
];
|
|
2818
2820
|
function parseScopeFlag(value) {
|
|
2819
|
-
const
|
|
2820
|
-
if (!
|
|
2821
|
-
const unknown =
|
|
2821
|
+
const tokens2 = String(value ?? "").split(/[\s,]+/).map((token) => token.trim()).filter(Boolean);
|
|
2822
|
+
if (!tokens2.length) return { ok: true, scope: "full" };
|
|
2823
|
+
const unknown = tokens2.filter((token) => !CLI_SCOPES.includes(token));
|
|
2822
2824
|
if (unknown.length) {
|
|
2823
2825
|
return { ok: false, error: `Unknown scope: ${unknown.join(", ")}. Scopes: ${CLI_SCOPES.join(", ")}.` };
|
|
2824
2826
|
}
|
|
2825
|
-
if (
|
|
2826
|
-
return { ok: true, scope: CLI_SCOPES.filter((scope) =>
|
|
2827
|
+
if (tokens2.includes("full")) return { ok: true, scope: "full" };
|
|
2828
|
+
return { ok: true, scope: CLI_SCOPES.filter((scope) => tokens2.includes(scope)).join(" ") };
|
|
2827
2829
|
}
|
|
2828
2830
|
function openInBrowser(url) {
|
|
2829
2831
|
const [cmd, args] = process.platform === "darwin" ? ["open", [url]] : process.platform === "win32" ? ["rundll32", ["url.dll,FileProtocolHandler", url]] : ["xdg-open", [url]];
|
|
@@ -2987,12 +2989,12 @@ ${next}`);
|
|
|
2987
2989
|
throw error;
|
|
2988
2990
|
}
|
|
2989
2991
|
}
|
|
2990
|
-
async function finishLogin(
|
|
2992
|
+
async function finishLogin(tokens2, login, { io, env, json, now }) {
|
|
2991
2993
|
const brand = cliBrand(env);
|
|
2992
|
-
const workspace =
|
|
2993
|
-
const user =
|
|
2994
|
+
const workspace = tokens2.workspace ?? {};
|
|
2995
|
+
const user = tokens2.user ?? {};
|
|
2994
2996
|
const id = typeof workspace.id === "string" && workspace.id ? workspace.id : "default";
|
|
2995
|
-
const scope = typeof
|
|
2997
|
+
const scope = typeof tokens2.scope === "string" && tokens2.scope ? tokens2.scope : login.scope ?? "full";
|
|
2996
2998
|
const previous = loadConfig(env).workspaces?.[id];
|
|
2997
2999
|
let replacedKey = false;
|
|
2998
3000
|
if (previous?.apiKey) {
|
|
@@ -3003,11 +3005,11 @@ async function finishLogin(tokens, login, { io, env, json, now }) {
|
|
|
3003
3005
|
const oldRefresh = readRefreshToken(previous, login.apiUrl, env);
|
|
3004
3006
|
if (oldRefresh) await revokeRefreshToken(login.apiUrl, oldRefresh, env).catch(() => null);
|
|
3005
3007
|
}
|
|
3006
|
-
const stored = storeRefreshToken({ apiUrl: login.apiUrl, workspaceId: id, refreshToken:
|
|
3008
|
+
const stored = storeRefreshToken({ apiUrl: login.apiUrl, workspaceId: id, refreshToken: tokens2.refresh_token, scope }, env);
|
|
3007
3009
|
const saved = {
|
|
3008
3010
|
id,
|
|
3009
3011
|
name: typeof workspace.name === "string" && workspace.name ? workspace.name : "Workspace",
|
|
3010
|
-
oauth: { ...stored, accessToken:
|
|
3012
|
+
oauth: { ...stored, accessToken: tokens2.access_token, accessTokenExpiresAt: now() + tokens2.expires_in * 1e3 },
|
|
3011
3013
|
...login.apiUrl !== defaultApiUrl(env) ? { apiUrl: login.apiUrl } : {},
|
|
3012
3014
|
email: typeof user.email === "string" ? user.email : null,
|
|
3013
3015
|
savedAt: new Date(now()).toISOString()
|
|
@@ -3021,12 +3023,12 @@ async function finishLogin(tokens, login, { io, env, json, now }) {
|
|
|
3021
3023
|
const envKey = envApiKey(env);
|
|
3022
3024
|
const envNote = envKey ? `${sourceLabel("env", env)} is set in this environment and is used instead of this sign-in until you unset it.` : null;
|
|
3023
3025
|
const where = storeLabel(saved.oauth, configPath(env), env);
|
|
3024
|
-
const next = await nextStep(login.apiUrl,
|
|
3026
|
+
const next = await nextStep(login.apiUrl, tokens2.access_token, scope, env);
|
|
3025
3027
|
if (json) {
|
|
3026
3028
|
printJson(io, {
|
|
3027
3029
|
status: "signed_in",
|
|
3028
3030
|
next,
|
|
3029
|
-
user:
|
|
3031
|
+
user: tokens2.user ?? null,
|
|
3030
3032
|
workspace: { id: saved.id, name: saved.name },
|
|
3031
3033
|
scope,
|
|
3032
3034
|
refresh_token_store: stored.store,
|
|
@@ -5532,6 +5534,195 @@ async function runMailboxesCommand(subcommand, argv, env = process.env) {
|
|
|
5532
5534
|
}
|
|
5533
5535
|
}
|
|
5534
5536
|
|
|
5537
|
+
// src/commands/cli/search.ts
|
|
5538
|
+
var USAGE4 = 'Usage: groundmail cli search "<what you want to do>" [--limit 5] [--json]';
|
|
5539
|
+
var DEFAULT_LIMIT = 5;
|
|
5540
|
+
var MAX_LIMIT = 20;
|
|
5541
|
+
var USAGE_WIDTH = 80;
|
|
5542
|
+
var WEIGHT = { name: 5, keywords: 4, about: 2, flags: 1 };
|
|
5543
|
+
var SYNONYM_SHARE = 0.6;
|
|
5544
|
+
var STOP_WORDS = new Set(
|
|
5545
|
+
"a about an and are as at be by can do for from get give how i in into is it its me my need of on or our out please show so that the them then this to up us want we what when where which who why will with you your".split(
|
|
5546
|
+
" "
|
|
5547
|
+
)
|
|
5548
|
+
);
|
|
5549
|
+
var SYNONYMS = {
|
|
5550
|
+
inbox: ["mailbox"],
|
|
5551
|
+
address: ["mailbox"],
|
|
5552
|
+
bot: ["agent"],
|
|
5553
|
+
agent: ["mailbox"],
|
|
5554
|
+
mail: ["email"],
|
|
5555
|
+
message: ["email"],
|
|
5556
|
+
sender: ["senders"],
|
|
5557
|
+
customer: ["tenant"],
|
|
5558
|
+
client: ["tenant"],
|
|
5559
|
+
company: ["tenant"],
|
|
5560
|
+
workspace: ["workspaces"],
|
|
5561
|
+
token: ["key"],
|
|
5562
|
+
secret: ["key"],
|
|
5563
|
+
make: ["create"],
|
|
5564
|
+
add: ["create"],
|
|
5565
|
+
change: ["update", "set"],
|
|
5566
|
+
edit: ["update"],
|
|
5567
|
+
new: ["create"],
|
|
5568
|
+
setup: ["create", "apply"],
|
|
5569
|
+
remove: ["delete"],
|
|
5570
|
+
stop: ["pause", "cancel", "block"],
|
|
5571
|
+
block: ["suppression"],
|
|
5572
|
+
ban: ["block"],
|
|
5573
|
+
unsubscribe: ["suppression"],
|
|
5574
|
+
dns: ["apply", "domain"],
|
|
5575
|
+
otp: ["code"],
|
|
5576
|
+
magic: ["code"],
|
|
5577
|
+
bounce: ["suppression", "get"],
|
|
5578
|
+
newsletter: ["broadcast"],
|
|
5579
|
+
logo: ["brand"],
|
|
5580
|
+
color: ["brand"],
|
|
5581
|
+
workflow: ["automation"],
|
|
5582
|
+
sequence: ["automation"],
|
|
5583
|
+
drip: ["automation"],
|
|
5584
|
+
callback: ["webhook"],
|
|
5585
|
+
trigger: ["event"],
|
|
5586
|
+
check: ["validate", "verify"],
|
|
5587
|
+
switch: ["migrate"],
|
|
5588
|
+
move: ["migrate", "forward"],
|
|
5589
|
+
import: ["migrate"]
|
|
5590
|
+
};
|
|
5591
|
+
var KEYWORDS = {
|
|
5592
|
+
"mailboxes create": "inbox address agent own email",
|
|
5593
|
+
"mailboxes update": "block allow sender stop reach who write send-as agent someone",
|
|
5594
|
+
"mailboxes read": "inbox message check mail",
|
|
5595
|
+
"mailboxes wait": "code verification sign-up magic link next",
|
|
5596
|
+
"mailboxes reply": "answer respond thread message email",
|
|
5597
|
+
"mailboxes send": "agent email first",
|
|
5598
|
+
"emails send": "transactional test send",
|
|
5599
|
+
"emails get": "status delivered bounce why happened",
|
|
5600
|
+
"emails cancel": "scheduled unsend",
|
|
5601
|
+
"test-inbox wait": "code verification sign-up test catch",
|
|
5602
|
+
"test-inbox address": "test sign-up throwaway disposable",
|
|
5603
|
+
"domains add": "sending own domain",
|
|
5604
|
+
"domains apply": "dns records cloudflare vercel godaddy automatically",
|
|
5605
|
+
"domains verify": "dns check records",
|
|
5606
|
+
"suppressions add": "unsubscribe do-not-email block stop emailing someone address",
|
|
5607
|
+
validate: "email address deliverable real check",
|
|
5608
|
+
"webhooks create": "notify events wake receive",
|
|
5609
|
+
"webhooks deliveries": "failed retry debug",
|
|
5610
|
+
"keys create": "api token secret",
|
|
5611
|
+
login: "sign-in authenticate account browser"
|
|
5612
|
+
};
|
|
5613
|
+
var NO_ACCOUNT_NEEDED = /* @__PURE__ */ new Set(["login", "logout", "whoami", "cli search", "validate"]);
|
|
5614
|
+
function stem(word) {
|
|
5615
|
+
if (word.length > 5 && word.endsWith("ing")) return word.slice(0, -3);
|
|
5616
|
+
if (word.length > 4 && word.endsWith("ies")) return `${word.slice(0, -3)}y`;
|
|
5617
|
+
if (word.length > 4 && word.endsWith("ed")) return word.slice(0, -2);
|
|
5618
|
+
if (word.length > 3 && word.endsWith("s") && !word.endsWith("ss")) return word.slice(0, -1);
|
|
5619
|
+
return word;
|
|
5620
|
+
}
|
|
5621
|
+
function tokens(text) {
|
|
5622
|
+
return (text.toLowerCase().match(/[a-z0-9]+/g) ?? []).filter((word) => !STOP_WORDS.has(word)).map(stem);
|
|
5623
|
+
}
|
|
5624
|
+
function queryTokens(task) {
|
|
5625
|
+
return tokens(
|
|
5626
|
+
task.toLowerCase().replace(/\b(log|sign)[ -]?(in|on)\b/g, "login").replace(/\bset[ -]up\b/g, "setup")
|
|
5627
|
+
);
|
|
5628
|
+
}
|
|
5629
|
+
function parseLine(line) {
|
|
5630
|
+
const [usagePart = "", ...rest] = line.trim().split(/\s{3,}/);
|
|
5631
|
+
const parts2 = usagePart.split(/\s+/).slice(1);
|
|
5632
|
+
const nameParts = [];
|
|
5633
|
+
for (const part of parts2) {
|
|
5634
|
+
if (!/^[a-z][a-z-]*$/.test(part) || nameParts.length === 3) break;
|
|
5635
|
+
nameParts.push(part);
|
|
5636
|
+
}
|
|
5637
|
+
const name = nameParts.join(" ");
|
|
5638
|
+
const summary = rest.join(" ").trim();
|
|
5639
|
+
const needsAccount = !(name.startsWith("mailboxes ") || NO_ACCOUNT_NEEDED.has(name));
|
|
5640
|
+
return {
|
|
5641
|
+
command: `groundmail ${name}`,
|
|
5642
|
+
summary,
|
|
5643
|
+
usage: usagePart,
|
|
5644
|
+
needs_account: needsAccount,
|
|
5645
|
+
name: tokens(name.replace(/-/g, " ")),
|
|
5646
|
+
keywords: tokens(KEYWORDS[name] ?? ""),
|
|
5647
|
+
about: tokens(summary),
|
|
5648
|
+
flags: tokens(parts2.slice(nameParts.length).join(" "))
|
|
5649
|
+
};
|
|
5650
|
+
}
|
|
5651
|
+
function matches(term, word) {
|
|
5652
|
+
if (term === word) return true;
|
|
5653
|
+
return term.length >= 5 && word.length >= 5 && (word.startsWith(term) || term.startsWith(word));
|
|
5654
|
+
}
|
|
5655
|
+
function fieldScore(entry, term) {
|
|
5656
|
+
let best = 0;
|
|
5657
|
+
for (const field of ["name", "keywords", "about", "flags"]) {
|
|
5658
|
+
if (entry[field].some((word) => matches(term, word))) best = Math.max(best, WEIGHT[field]);
|
|
5659
|
+
}
|
|
5660
|
+
return best;
|
|
5661
|
+
}
|
|
5662
|
+
function searchCommands(task, limit = DEFAULT_LIMIT) {
|
|
5663
|
+
const terms = [...new Set(queryTokens(task))];
|
|
5664
|
+
if (!terms.length) return [];
|
|
5665
|
+
const entries = USAGE_LINES.map(parseLine);
|
|
5666
|
+
const rarity = (term) => {
|
|
5667
|
+
const hits = entries.filter((entry) => fieldScore(entry, term) > 0).length;
|
|
5668
|
+
return hits ? 1 + Math.log(entries.length / hits) : 0;
|
|
5669
|
+
};
|
|
5670
|
+
const scored = entries.map((entry) => {
|
|
5671
|
+
let score = 0;
|
|
5672
|
+
let matched = 0;
|
|
5673
|
+
for (const term of terms) {
|
|
5674
|
+
const direct = fieldScore(entry, term) * rarity(term);
|
|
5675
|
+
const viaSynonym = Math.max(
|
|
5676
|
+
0,
|
|
5677
|
+
...(SYNONYMS[term] ?? []).map((word) => fieldScore(entry, stem(word)) * rarity(stem(word)) * SYNONYM_SHARE)
|
|
5678
|
+
);
|
|
5679
|
+
const best = Math.max(direct, viaSynonym);
|
|
5680
|
+
if (best > 0) matched += 1;
|
|
5681
|
+
score += best;
|
|
5682
|
+
}
|
|
5683
|
+
return { entry, score: score * (matched / terms.length) };
|
|
5684
|
+
});
|
|
5685
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5686
|
+
const results = [];
|
|
5687
|
+
for (const { entry, score } of scored.sort((a, b) => b.score - a.score || a.entry.command.length - b.entry.command.length)) {
|
|
5688
|
+
if (score <= 0 || results.length >= limit) break;
|
|
5689
|
+
if (seen.has(entry.command)) continue;
|
|
5690
|
+
seen.add(entry.command);
|
|
5691
|
+
const { command, summary, usage, needs_account } = entry;
|
|
5692
|
+
results.push({ command, summary, usage, needs_account });
|
|
5693
|
+
}
|
|
5694
|
+
return results;
|
|
5695
|
+
}
|
|
5696
|
+
function textOutput(found) {
|
|
5697
|
+
const lines = found.map((match) => {
|
|
5698
|
+
const usage = match.usage.length > USAGE_WIDTH ? `${match.usage.slice(0, USAGE_WIDTH)}\u2026` : match.usage;
|
|
5699
|
+
const note = match.needs_account ? " (needs an account key)" : "";
|
|
5700
|
+
return match.summary ? `${usage}${note}
|
|
5701
|
+
${match.summary}` : `${usage}${note}`;
|
|
5702
|
+
});
|
|
5703
|
+
return [...lines, "", "Add --help to any of these for its flags."].join("\n");
|
|
5704
|
+
}
|
|
5705
|
+
async function runCliCommand(subcommand, argv) {
|
|
5706
|
+
if (subcommand !== "search") return { exitCode: 1, error: USAGE4 };
|
|
5707
|
+
const parsed = parseArgs(argv, { values: ["--limit"], booleans: ["--json"] });
|
|
5708
|
+
if (!parsed.ok) return { exitCode: 1, error: `${parsed.error}
|
|
5709
|
+
${USAGE4}` };
|
|
5710
|
+
const task = parsed.args.positionals.join(" ").trim();
|
|
5711
|
+
if (!task) return { exitCode: 1, error: USAGE4 };
|
|
5712
|
+
const rawLimit = parsed.args.values["--limit"];
|
|
5713
|
+
const limit = rawLimit === void 0 ? DEFAULT_LIMIT : Number(rawLimit);
|
|
5714
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > MAX_LIMIT) {
|
|
5715
|
+
return { exitCode: 1, error: `--limit must be a whole number from 1 to ${MAX_LIMIT}.` };
|
|
5716
|
+
}
|
|
5717
|
+
const found = searchCommands(task, limit);
|
|
5718
|
+
if (parsed.args.booleans["--json"]) {
|
|
5719
|
+
console.log(JSON.stringify({ query: task, matches: found }, null, 2));
|
|
5720
|
+
return { exitCode: 0 };
|
|
5721
|
+
}
|
|
5722
|
+
console.log(found.length ? textOutput(found) : "No matching command. Try other words, or run groundmail --help.");
|
|
5723
|
+
return { exitCode: 0 };
|
|
5724
|
+
}
|
|
5725
|
+
|
|
5535
5726
|
// src/update-check.ts
|
|
5536
5727
|
import { spawn as spawn2 } from "child_process";
|
|
5537
5728
|
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
@@ -5631,6 +5822,7 @@ async function main() {
|
|
|
5631
5822
|
console.log(meta.text);
|
|
5632
5823
|
process.exit(0);
|
|
5633
5824
|
}
|
|
5825
|
+
if (command === "cli") await finish(await runCliCommand(subcommand, args.slice(1)));
|
|
5634
5826
|
if (command === "login") await finish(await runLoginCommand(args));
|
|
5635
5827
|
if (command === "logout") await finish(await runLogoutCommand(args));
|
|
5636
5828
|
if (command === "whoami") await finish(await runWhoamiCommand(args));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "groundmail-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Groundmail CLI
|
|
3
|
+
"version": "0.47.0",
|
|
4
|
+
"description": "Groundmail CLI: send email and manage mailboxes from the terminal",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"prepublishOnly": "npm run build"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"groundmail": "0.48.
|
|
41
|
+
"groundmail": "0.48.1"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"tsup": "^8.5.0",
|