@agenticmail/cli 0.9.36 → 0.9.38
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/dist/cli.js +264 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4927,6 +4927,254 @@ var NON_INTERACTIVE = false;
|
|
|
4927
4927
|
function nonInteractiveDefault(value) {
|
|
4928
4928
|
return NON_INTERACTIVE ? value : null;
|
|
4929
4929
|
}
|
|
4930
|
+
async function cmdSetupRelay() {
|
|
4931
|
+
const args = process.argv.slice(3);
|
|
4932
|
+
if (args.some((a) => a === "--help" || a === "-h" || a === "help")) {
|
|
4933
|
+
log2("");
|
|
4934
|
+
log2(` ${c2.pinkBg(" \u{1F380} agenticmail setup-relay ")}`);
|
|
4935
|
+
log2("");
|
|
4936
|
+
log2(` ${c2.bold("Usage:")} agenticmail setup-relay`);
|
|
4937
|
+
log2("");
|
|
4938
|
+
log2(` Adds (or updates) the Gmail / Outlook / custom relay AgenticMail uses`);
|
|
4939
|
+
log2(` to send mail to the public internet. Run this YOURSELF \u2014 never paste`);
|
|
4940
|
+
log2(` the Gmail app password into an agent's chat (it would end up in the`);
|
|
4941
|
+
log2(` LLM's context / logs / conversation history).`);
|
|
4942
|
+
log2("");
|
|
4943
|
+
log2(` Password input is hidden (raw-mode stdin). The agent never sees it.`);
|
|
4944
|
+
log2("");
|
|
4945
|
+
log2(` Prereq: AgenticMail already bootstrapped (run \`agenticmail setup\` first).`);
|
|
4946
|
+
log2("");
|
|
4947
|
+
return;
|
|
4948
|
+
}
|
|
4949
|
+
const configPath = join(homedir(), ".agenticmail", "config.json");
|
|
4950
|
+
if (!existsSync2(configPath)) {
|
|
4951
|
+
log2("");
|
|
4952
|
+
fail2(`AgenticMail isn't set up yet \u2014 no config at ${c2.dim(configPath)}`);
|
|
4953
|
+
log2(` Run ${c2.cyan("agenticmail setup")} first, then come back to add the relay.`);
|
|
4954
|
+
log2("");
|
|
4955
|
+
process.exit(1);
|
|
4956
|
+
}
|
|
4957
|
+
let config;
|
|
4958
|
+
try {
|
|
4959
|
+
config = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
4960
|
+
} catch (err) {
|
|
4961
|
+
log2("");
|
|
4962
|
+
fail2(`Could not read ${configPath}: ${err.message}`);
|
|
4963
|
+
log2("");
|
|
4964
|
+
process.exit(1);
|
|
4965
|
+
}
|
|
4966
|
+
log2("");
|
|
4967
|
+
log2(` ${c2.bold("\u{1F380} AgenticMail \u2014 set up Gmail relay")} `);
|
|
4968
|
+
log2("");
|
|
4969
|
+
log2(` ${c2.dim("This command runs entirely in your terminal. The password")}`);
|
|
4970
|
+
log2(` ${c2.dim("input is hidden and never leaves this process \u2014 your agent")}`);
|
|
4971
|
+
log2(` ${c2.dim("doesn't see it.")}`);
|
|
4972
|
+
log2("");
|
|
4973
|
+
try {
|
|
4974
|
+
const result = await setupRelay(config);
|
|
4975
|
+
if (!result.success) {
|
|
4976
|
+
log2("");
|
|
4977
|
+
fail2("Relay setup did not complete \u2014 see messages above.");
|
|
4978
|
+
process.exit(1);
|
|
4979
|
+
}
|
|
4980
|
+
log2("");
|
|
4981
|
+
ok2("Gmail relay configured.");
|
|
4982
|
+
log2("");
|
|
4983
|
+
log2(` ${c2.bold("Next:")} point bridge-escalation alerts at your personal email:`);
|
|
4984
|
+
log2("");
|
|
4985
|
+
log2(` ${c2.cyan("Option A")} \u2014 tell your host agent: "set my operator notification email to <you@gmail.com>"`);
|
|
4986
|
+
log2(` ${c2.cyan("Option B")} \u2014 open the web UI \u2192 click your avatar \u2192 ${c2.bold("Alert email")} \u2192 type, Save`);
|
|
4987
|
+
log2("");
|
|
4988
|
+
log2(` ${c2.dim("Either path writes ~/.agenticmail/operator-prefs.json \u2014 the dispatcher")}`);
|
|
4989
|
+
log2(` ${c2.dim("forwards a digest there when sub-agents mail your bridge and the")}`);
|
|
4990
|
+
log2(` ${c2.dim("dispatcher can't resume your host session.")}`);
|
|
4991
|
+
log2("");
|
|
4992
|
+
} catch (err) {
|
|
4993
|
+
log2("");
|
|
4994
|
+
fail2(`Setup-relay failed: ${err.message}`);
|
|
4995
|
+
log2("");
|
|
4996
|
+
process.exit(1);
|
|
4997
|
+
}
|
|
4998
|
+
}
|
|
4999
|
+
async function cmdSetupEmail() {
|
|
5000
|
+
const args = process.argv.slice(3);
|
|
5001
|
+
if (args.some((a) => a === "--help" || a === "-h" || a === "help")) {
|
|
5002
|
+
log2("");
|
|
5003
|
+
log2(` ${c2.pinkBg(" \u{1F380} agenticmail setup-email ")}`);
|
|
5004
|
+
log2("");
|
|
5005
|
+
log2(` ${c2.bold("Usage:")} agenticmail setup-email`);
|
|
5006
|
+
log2("");
|
|
5007
|
+
log2(` Minimal email-relay setup: enter your address, enter the password,`);
|
|
5008
|
+
log2(` done. Provider (Gmail, Outlook, custom) is detected from the domain.`);
|
|
5009
|
+
log2(` Password is collected via hidden stdin \u2014 your agent never sees it.`);
|
|
5010
|
+
log2("");
|
|
5011
|
+
log2(` Prereq: AgenticMail already bootstrapped (run \`agenticmail setup\` first).`);
|
|
5012
|
+
log2("");
|
|
5013
|
+
return;
|
|
5014
|
+
}
|
|
5015
|
+
const configPath = join(homedir(), ".agenticmail", "config.json");
|
|
5016
|
+
if (!existsSync2(configPath)) {
|
|
5017
|
+
log2("");
|
|
5018
|
+
fail2(`AgenticMail isn't set up yet \u2014 no config at ${c2.dim(configPath)}`);
|
|
5019
|
+
log2(` Run ${c2.cyan("agenticmail setup")} first, then come back to add your email.`);
|
|
5020
|
+
log2("");
|
|
5021
|
+
process.exit(1);
|
|
5022
|
+
}
|
|
5023
|
+
let config;
|
|
5024
|
+
try {
|
|
5025
|
+
config = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
5026
|
+
} catch (err) {
|
|
5027
|
+
log2("");
|
|
5028
|
+
fail2(`Could not read ${configPath}: ${err.message}`);
|
|
5029
|
+
log2("");
|
|
5030
|
+
process.exit(1);
|
|
5031
|
+
}
|
|
5032
|
+
log2("");
|
|
5033
|
+
log2(` ${c2.bold("\u{1F380} AgenticMail \u2014 connect your mailbox")} `);
|
|
5034
|
+
log2("");
|
|
5035
|
+
log2(` ${c2.dim("Two questions: your email, your password. Password input is")}`);
|
|
5036
|
+
log2(` ${c2.dim("hidden and never leaves this process \u2014 your agent doesn't see it.")}`);
|
|
5037
|
+
log2("");
|
|
5038
|
+
const apiBase = `http://${config.api.host}:${config.api.port}`;
|
|
5039
|
+
let serverReady = false;
|
|
5040
|
+
try {
|
|
5041
|
+
const probe = await fetch(`${apiBase}/api/agenticmail/health`, { signal: AbortSignal.timeout(2e3) });
|
|
5042
|
+
serverReady = probe.ok;
|
|
5043
|
+
} catch {
|
|
5044
|
+
}
|
|
5045
|
+
if (!serverReady) {
|
|
5046
|
+
try {
|
|
5047
|
+
serverReady = await startApiServer(config);
|
|
5048
|
+
} catch {
|
|
5049
|
+
}
|
|
5050
|
+
}
|
|
5051
|
+
if (!serverReady) {
|
|
5052
|
+
fail2(`API server not reachable at ${c2.cyan(apiBase)}`);
|
|
5053
|
+
info2(`Start it with ${c2.green("agenticmail start")}, then re-run this command.`);
|
|
5054
|
+
process.exit(1);
|
|
5055
|
+
}
|
|
5056
|
+
let agentName = "secretary";
|
|
5057
|
+
try {
|
|
5058
|
+
const acctResp = await fetch(`${apiBase}/api/agenticmail/accounts`, {
|
|
5059
|
+
headers: { "Authorization": `Bearer ${config.masterKey}` },
|
|
5060
|
+
signal: AbortSignal.timeout(5e3)
|
|
5061
|
+
});
|
|
5062
|
+
if (acctResp.ok) {
|
|
5063
|
+
const data = await acctResp.json();
|
|
5064
|
+
const agents = data?.agents ?? data ?? [];
|
|
5065
|
+
const first = agents.find((a) => a.name && a.name !== "claudecode" && a.name !== "codex") ?? agents[0];
|
|
5066
|
+
if (first?.name) agentName = first.name;
|
|
5067
|
+
}
|
|
5068
|
+
} catch {
|
|
5069
|
+
}
|
|
5070
|
+
const email = (await ask(` ${c2.cyan("Your email address:")} `)).trim();
|
|
5071
|
+
if (!email || !email.includes("@")) {
|
|
5072
|
+
log2("");
|
|
5073
|
+
fail2("That doesn't look like a valid email address.");
|
|
5074
|
+
process.exit(1);
|
|
5075
|
+
}
|
|
5076
|
+
const domain = email.split("@")[1].toLowerCase();
|
|
5077
|
+
const GMAIL_DOMAINS = /* @__PURE__ */ new Set(["gmail.com", "googlemail.com"]);
|
|
5078
|
+
const OUTLOOK_DOMAINS = /* @__PURE__ */ new Set(["outlook.com", "hotmail.com", "live.com", "msn.com"]);
|
|
5079
|
+
let provider;
|
|
5080
|
+
let smtpHost;
|
|
5081
|
+
let smtpPort;
|
|
5082
|
+
let imapHost;
|
|
5083
|
+
let imapPort;
|
|
5084
|
+
let appPasswordHint = "";
|
|
5085
|
+
if (GMAIL_DOMAINS.has(domain)) {
|
|
5086
|
+
provider = "gmail";
|
|
5087
|
+
appPasswordHint = `App password: ${c2.cyan("https://myaccount.google.com/apppasswords")}`;
|
|
5088
|
+
} else if (OUTLOOK_DOMAINS.has(domain)) {
|
|
5089
|
+
provider = "outlook";
|
|
5090
|
+
appPasswordHint = `App password: your Microsoft account \u2192 Security \u2192 Advanced security options \u2192 App passwords`;
|
|
5091
|
+
} else {
|
|
5092
|
+
log2("");
|
|
5093
|
+
log2(` ${c2.dim("We don't recognize the domain ")}${c2.bold(domain)}${c2.dim(". Where does it live?")}`);
|
|
5094
|
+
log2(` ${c2.cyan("1.")} Google Workspace ${c2.dim("(mail hosted by Gmail)")}`);
|
|
5095
|
+
log2(` ${c2.cyan("2.")} Microsoft 365 ${c2.dim("(mail hosted by Outlook)")}`);
|
|
5096
|
+
log2(` ${c2.cyan("3.")} Custom mail server`);
|
|
5097
|
+
const pickProv = await pick(` ${c2.magenta(">")} `, ["1", "2", "3"]);
|
|
5098
|
+
if (pickProv === "1") {
|
|
5099
|
+
provider = "gmail";
|
|
5100
|
+
appPasswordHint = `App password: ${c2.cyan("https://myaccount.google.com/apppasswords")}`;
|
|
5101
|
+
} else if (pickProv === "2") {
|
|
5102
|
+
provider = "outlook";
|
|
5103
|
+
appPasswordHint = `App password: your Microsoft account \u2192 Security \u2192 Advanced security options \u2192 App passwords`;
|
|
5104
|
+
} else {
|
|
5105
|
+
provider = "custom";
|
|
5106
|
+
log2("");
|
|
5107
|
+
log2(` ${c2.dim("Your mail-server hostnames (check your provider's docs):")}`);
|
|
5108
|
+
smtpHost = (await ask(` ${c2.cyan("Outgoing (SMTP) host:")} `)).trim();
|
|
5109
|
+
const smtpPortStr = (await ask(` ${c2.cyan("SMTP port")} ${c2.dim("(587)")}: `)).trim();
|
|
5110
|
+
smtpPort = smtpPortStr ? parseInt(smtpPortStr, 10) : 587;
|
|
5111
|
+
imapHost = (await ask(` ${c2.cyan("Incoming (IMAP) host:")} `)).trim();
|
|
5112
|
+
const imapPortStr = (await ask(` ${c2.cyan("IMAP port")} ${c2.dim("(993)")}: `)).trim();
|
|
5113
|
+
imapPort = imapPortStr ? parseInt(imapPortStr, 10) : 993;
|
|
5114
|
+
}
|
|
5115
|
+
}
|
|
5116
|
+
log2("");
|
|
5117
|
+
if (appPasswordHint) log2(` ${c2.dim(appPasswordHint)}`);
|
|
5118
|
+
log2(` ${c2.dim("Spaces in the password are fine \u2014 we'll strip them.")}`);
|
|
5119
|
+
log2("");
|
|
5120
|
+
const rawPassword = await askSecret(` ${c2.cyan("Password:")} `);
|
|
5121
|
+
const password = rawPassword.replace(/\s+/g, "");
|
|
5122
|
+
if (!password) {
|
|
5123
|
+
log2("");
|
|
5124
|
+
fail2("No password entered.");
|
|
5125
|
+
process.exit(1);
|
|
5126
|
+
}
|
|
5127
|
+
log2("");
|
|
5128
|
+
const spinner = new Spinner("relay");
|
|
5129
|
+
spinner.start();
|
|
5130
|
+
try {
|
|
5131
|
+
const response = await fetch(`${apiBase}/api/agenticmail/gateway/relay`, {
|
|
5132
|
+
method: "POST",
|
|
5133
|
+
headers: {
|
|
5134
|
+
"Authorization": `Bearer ${config.masterKey}`,
|
|
5135
|
+
"Content-Type": "application/json"
|
|
5136
|
+
},
|
|
5137
|
+
body: JSON.stringify({
|
|
5138
|
+
provider,
|
|
5139
|
+
email,
|
|
5140
|
+
password,
|
|
5141
|
+
agentName,
|
|
5142
|
+
smtpHost,
|
|
5143
|
+
smtpPort,
|
|
5144
|
+
imapHost,
|
|
5145
|
+
imapPort
|
|
5146
|
+
}),
|
|
5147
|
+
signal: AbortSignal.timeout(3e4)
|
|
5148
|
+
});
|
|
5149
|
+
if (!response.ok) {
|
|
5150
|
+
const text = await response.text();
|
|
5151
|
+
const friendly = parseFriendlyError(text);
|
|
5152
|
+
spinner.fail(friendly.message);
|
|
5153
|
+
log2("");
|
|
5154
|
+
if (friendly.isAuthError) {
|
|
5155
|
+
info2("Check the email + password and run `agenticmail setup-email` again.");
|
|
5156
|
+
}
|
|
5157
|
+
log2("");
|
|
5158
|
+
process.exit(1);
|
|
5159
|
+
}
|
|
5160
|
+
const data = await response.json();
|
|
5161
|
+
spinner.succeed(`Mailbox connected \u2014 ${c2.bold(email)} ${c2.dim("via " + provider)}`);
|
|
5162
|
+
if (data?.agent?.subAddress) {
|
|
5163
|
+
log2("");
|
|
5164
|
+
ok2(`Agent ${c2.bold('"' + (data.agent.name ?? agentName) + '"')} ready at ${c2.cyan(data.agent.subAddress)}`);
|
|
5165
|
+
}
|
|
5166
|
+
log2("");
|
|
5167
|
+
log2(` ${c2.bold("Next:")} point bridge-escalation alerts at your personal email:`);
|
|
5168
|
+
log2("");
|
|
5169
|
+
log2(` ${c2.cyan("Option A")} \u2014 tell your host agent: "set my operator notification email to <you@example.com>"`);
|
|
5170
|
+
log2(` ${c2.cyan("Option B")} \u2014 open the web UI \u2192 click your avatar \u2192 ${c2.bold("Alert email")} \u2192 type, Save`);
|
|
5171
|
+
log2("");
|
|
5172
|
+
} catch (err) {
|
|
5173
|
+
spinner.fail(`Setup-email failed: ${err.message}`);
|
|
5174
|
+
log2("");
|
|
5175
|
+
process.exit(1);
|
|
5176
|
+
}
|
|
5177
|
+
}
|
|
4930
5178
|
async function cmdSetup() {
|
|
4931
5179
|
const setupArgs = process.argv.slice(3);
|
|
4932
5180
|
if (setupArgs.some((a) => a === "--help" || a === "-h" || a === "help")) {
|
|
@@ -7392,6 +7640,21 @@ switch (command) {
|
|
|
7392
7640
|
process.exit(1);
|
|
7393
7641
|
});
|
|
7394
7642
|
break;
|
|
7643
|
+
case "setup-relay":
|
|
7644
|
+
case "relay":
|
|
7645
|
+
cmdSetupRelay().catch((err) => {
|
|
7646
|
+
console.error(err);
|
|
7647
|
+
process.exit(1);
|
|
7648
|
+
});
|
|
7649
|
+
break;
|
|
7650
|
+
case "setup-email":
|
|
7651
|
+
case "email":
|
|
7652
|
+
case "connect-email":
|
|
7653
|
+
cmdSetupEmail().catch((err) => {
|
|
7654
|
+
console.error(err);
|
|
7655
|
+
process.exit(1);
|
|
7656
|
+
});
|
|
7657
|
+
break;
|
|
7395
7658
|
case "start":
|
|
7396
7659
|
cmdStart().catch((err) => {
|
|
7397
7660
|
console.error(err);
|
|
@@ -7483,6 +7746,7 @@ switch (command) {
|
|
|
7483
7746
|
log2(` ${c2.green("agenticmail")} Get started (setup + start)`);
|
|
7484
7747
|
log2(` ${c2.green("agenticmail bootstrap")} ${c2.dim("Zero-question install \u2014 for AI agents (Claude Code) to run on a user's behalf")}`);
|
|
7485
7748
|
log2(` ${c2.green("agenticmail setup")} Re-run the setup wizard ${c2.dim("(use --yes for non-interactive)")}`);
|
|
7749
|
+
log2(` ${c2.green("agenticmail setup-email")} Connect your mailbox \u2014 just email + password ${c2.dim("(auto-detects Gmail/Outlook/custom)")}`);
|
|
7486
7750
|
log2(` ${c2.green("agenticmail start")} Start the server`);
|
|
7487
7751
|
log2(` ${c2.green("agenticmail stop")} Stop the server`);
|
|
7488
7752
|
log2(` ${c2.green("agenticmail status")} See what's running`);
|
package/package.json
CHANGED