agenticmail 0.3.25 → 0.5.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 +16 -3
- package/dist/cli.js +863 -21
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The main package for [AgenticMail](https://github.com/agenticmail/agenticmail) — email infrastructure for AI agents. This is the package you install to get started.
|
|
4
4
|
|
|
5
|
-
It bundles a setup wizard, API server launcher, and a full interactive shell with
|
|
5
|
+
It bundles a setup wizard, API server launcher, and a full interactive shell with 44 commands for managing agents, sending and receiving email, configuring gateways, and more. It also re-exports everything from `@agenticmail/core` so you can use it as an SDK.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -42,7 +42,11 @@ Running `agenticmail setup` walks you through everything needed to get email wor
|
|
|
42
42
|
|
|
43
43
|
3. **Service startup** — starts Docker if needed, ensures Stalwart is running and healthy.
|
|
44
44
|
|
|
45
|
-
4. **Email connection** — this is where you choose how your agents connect to the outside world
|
|
45
|
+
4. **Email connection** — this is where you choose how your agents connect to the outside world.
|
|
46
|
+
|
|
47
|
+
5. **Phone number access (optional)** — set up Google Voice for SMS. Agents can receive verification codes and send texts. The wizard validates Gmail/Google Voice email matching, warns about mismatches, and collects separate credentials when needed. SMS reading prioritizes direct Google Voice web access (instant) with email forwarding as fallback.
|
|
48
|
+
|
|
49
|
+
6. **OpenClaw integration** — if OpenClaw is detected, automatically registers the plugin.
|
|
46
50
|
|
|
47
51
|
### Relay Mode (Recommended for Getting Started)
|
|
48
52
|
|
|
@@ -99,7 +103,7 @@ If the server crashes, you get clear error output showing what went wrong.
|
|
|
99
103
|
|
|
100
104
|
## The Interactive Shell
|
|
101
105
|
|
|
102
|
-
The shell is the main way to interact with AgenticMail. It provides
|
|
106
|
+
The shell is the main way to interact with AgenticMail. It provides 44 commands organized by category, with arrow-key navigation, color-coded output, and keyboard shortcuts.
|
|
103
107
|
|
|
104
108
|
### Getting Around
|
|
105
109
|
|
|
@@ -182,8 +186,17 @@ The shell is the main way to interact with AgenticMail. It provides 36 commands
|
|
|
182
186
|
|---------|-------------|
|
|
183
187
|
| `/help` | Show all available commands with descriptions. |
|
|
184
188
|
| `/clear` | Clear the screen. |
|
|
189
|
+
| `/update` | Check for and install the latest AgenticMail version. Auto-detects OpenClaw and updates both. |
|
|
185
190
|
| `/exit` | Exit the shell (also `/quit`). Stops the server and cleans up. |
|
|
186
191
|
|
|
192
|
+
### CLI Update Command
|
|
193
|
+
|
|
194
|
+
```bash
|
|
195
|
+
agenticmail update
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
Checks npm for the latest version, compares with your current install, and updates in-place. If OpenClaw is detected, it also updates `@agenticmail/openclaw` and restarts the gateway automatically. Works with npm, pnpm, and bun.
|
|
199
|
+
|
|
187
200
|
---
|
|
188
201
|
|
|
189
202
|
## Inbox Navigation
|
package/dist/cli.js
CHANGED
|
@@ -235,6 +235,21 @@ async function interactiveShell(options) {
|
|
|
235
235
|
log(` ${c.dim("Server:")} ${c.cyan(`http://${config.api.host}:${config.api.port}`)}`);
|
|
236
236
|
if (agentLine) log(` ${c.dim("Agents:")} ${agentLine}`);
|
|
237
237
|
if (emailLine) log(` ${c.dim("Email:")} ${emailLine}`);
|
|
238
|
+
try {
|
|
239
|
+
const agentsResp = await apiFetch("/api/agenticmail/accounts");
|
|
240
|
+
if (agentsResp.ok) {
|
|
241
|
+
const agentsData = await agentsResp.json();
|
|
242
|
+
const agents = agentsData.agents || agentsData.accounts || [];
|
|
243
|
+
for (const a of agents) {
|
|
244
|
+
const smsConf = a.metadata?.sms;
|
|
245
|
+
if (smsConf?.enabled && smsConf.phoneNumber) {
|
|
246
|
+
log(` ${c.dim("Phone:")} ${c.green(smsConf.phoneNumber)} ${c.dim("via Google Voice")} ${c.dim("(" + a.name + ")")}`);
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
} catch {
|
|
252
|
+
}
|
|
238
253
|
log("");
|
|
239
254
|
log("");
|
|
240
255
|
log(` ${c.dim("Type")} ${c.bold("/help")} ${c.dim("for commands, or")} ${c.bold("/exit")} ${c.dim("to stop.")}`);
|
|
@@ -1665,6 +1680,14 @@ ${orig.text || ""}`;
|
|
|
1665
1680
|
log(` ${c.cyan("\u{1F4C1}")} ${f.path}${special}`);
|
|
1666
1681
|
}
|
|
1667
1682
|
}
|
|
1683
|
+
try {
|
|
1684
|
+
const smsResp = await agentFetch(agent.apiKey, "/api/agenticmail/sms/config");
|
|
1685
|
+
const smsData = await smsResp.json();
|
|
1686
|
+
if (smsData.sms?.enabled) {
|
|
1687
|
+
log(` ${c.green("\u{1F4F1}")} SMS ${c.dim(`(${smsData.sms.phoneNumber})`)}`);
|
|
1688
|
+
}
|
|
1689
|
+
} catch {
|
|
1690
|
+
}
|
|
1668
1691
|
} else if (choice.trim() === "2" || choice.trim().toLowerCase() === "create") {
|
|
1669
1692
|
const name = await question(` ${c.dim("Folder name:")} `);
|
|
1670
1693
|
if (isBack(name) || !name.trim()) {
|
|
@@ -1694,10 +1717,25 @@ ${orig.text || ""}`;
|
|
|
1694
1717
|
log("");
|
|
1695
1718
|
return;
|
|
1696
1719
|
}
|
|
1720
|
+
let hasSms = false;
|
|
1721
|
+
try {
|
|
1722
|
+
const smsResp = await agentFetch(agent.apiKey, "/api/agenticmail/sms/config");
|
|
1723
|
+
const smsData = await smsResp.json();
|
|
1724
|
+
if (smsData.sms?.enabled) hasSms = true;
|
|
1725
|
+
} catch {
|
|
1726
|
+
}
|
|
1697
1727
|
for (let i = 0; i < folders.length; i++) {
|
|
1698
1728
|
log(` ${c.dim(`[${i + 1}]`)} ${c.cyan(folders[i].path)}`);
|
|
1699
1729
|
}
|
|
1700
|
-
|
|
1730
|
+
if (hasSms) {
|
|
1731
|
+
log(` ${c.dim(`[${folders.length + 1}]`)} ${c.green("SMS")} ${c.dim("(text messages)")}`);
|
|
1732
|
+
}
|
|
1733
|
+
const totalChoices = hasSms ? folders.length + 1 : folders.length;
|
|
1734
|
+
const idx = await askChoice(` ${c.dim("Folder #:")} `, totalChoices);
|
|
1735
|
+
if (idx !== null && hasSms && idx === folders.length) {
|
|
1736
|
+
await commands.sms.run();
|
|
1737
|
+
return;
|
|
1738
|
+
}
|
|
1701
1739
|
if (idx === null) {
|
|
1702
1740
|
log("");
|
|
1703
1741
|
return;
|
|
@@ -3617,6 +3655,308 @@ ${c.dim(boxChar.bl + boxChar.h.repeat(bWidth) + boxChar.br)}`);
|
|
|
3617
3655
|
}
|
|
3618
3656
|
}
|
|
3619
3657
|
},
|
|
3658
|
+
sms: {
|
|
3659
|
+
desc: "Manage SMS / phone number (view, setup, change, disable)",
|
|
3660
|
+
run: async () => {
|
|
3661
|
+
const agent = getActiveAgent();
|
|
3662
|
+
if (!agent) return;
|
|
3663
|
+
log("");
|
|
3664
|
+
log(hr());
|
|
3665
|
+
heading("SMS / Phone Number");
|
|
3666
|
+
log("");
|
|
3667
|
+
let smsConfig = null;
|
|
3668
|
+
try {
|
|
3669
|
+
const resp = await fetch(`${apiBase}/api/agenticmail/sms/config`, {
|
|
3670
|
+
headers: { "Authorization": `Bearer ${agent.apiKey}` }
|
|
3671
|
+
});
|
|
3672
|
+
const data = await resp.json();
|
|
3673
|
+
smsConfig = data.sms;
|
|
3674
|
+
} catch {
|
|
3675
|
+
}
|
|
3676
|
+
if (smsConfig?.enabled) {
|
|
3677
|
+
log(` ${c.green("\u25CF")} SMS is ${c.green("enabled")}`);
|
|
3678
|
+
log(` ${c.dim("Phone number:")} ${c.bold(smsConfig.phoneNumber)}`);
|
|
3679
|
+
log(` ${c.dim("Forwarding to:")} ${smsConfig.forwardingEmail || c.dim("(agent email)")}`);
|
|
3680
|
+
log(` ${c.dim("Provider:")} ${smsConfig.provider}`);
|
|
3681
|
+
log(` ${c.dim("Configured:")} ${smsConfig.configuredAt ? new Date(smsConfig.configuredAt).toLocaleDateString() : "unknown"}`);
|
|
3682
|
+
} else if (smsConfig && !smsConfig.enabled) {
|
|
3683
|
+
log(` ${c.yellow("\u25CF")} SMS is ${c.yellow("disabled")}`);
|
|
3684
|
+
log(` ${c.dim("Phone number:")} ${smsConfig.phoneNumber}`);
|
|
3685
|
+
} else {
|
|
3686
|
+
log(` ${c.dim("\u25CF")} SMS is ${c.dim("not configured")}`);
|
|
3687
|
+
}
|
|
3688
|
+
log("");
|
|
3689
|
+
log(` ${c.bold("Options:")}`);
|
|
3690
|
+
if (!smsConfig?.enabled) {
|
|
3691
|
+
log(` ${c.cyan("1")} Set up a phone number`);
|
|
3692
|
+
} else {
|
|
3693
|
+
log(` ${c.cyan("1")} View SMS messages`);
|
|
3694
|
+
log(` ${c.cyan("2")} Change phone number`);
|
|
3695
|
+
log(` ${c.cyan("3")} Check for verification codes`);
|
|
3696
|
+
log(` ${c.cyan("4")} Disable SMS`);
|
|
3697
|
+
}
|
|
3698
|
+
log(` ${c.dim("Enter")} Go back`);
|
|
3699
|
+
log("");
|
|
3700
|
+
const choice = await new Promise((resolve) => {
|
|
3701
|
+
rl.question(` ${c.bold("Choose:")} `, resolve);
|
|
3702
|
+
});
|
|
3703
|
+
if (!smsConfig?.enabled) {
|
|
3704
|
+
if (choice.trim() === "1") {
|
|
3705
|
+
log("");
|
|
3706
|
+
log(` ${c.bold("Google Voice Setup (takes ~2 minutes):")}`);
|
|
3707
|
+
log("");
|
|
3708
|
+
log(` 1. Go to ${c.cyan("https://voice.google.com")}`);
|
|
3709
|
+
log(` 2. Sign in with your Google account`);
|
|
3710
|
+
log(` 3. Click "Choose a phone number"`);
|
|
3711
|
+
log(` 4. Search by city or area code, pick a number`);
|
|
3712
|
+
log(` 5. Verify with your existing phone`);
|
|
3713
|
+
log(` 6. Go to Settings > Messages > Enable "Forward messages to email"`);
|
|
3714
|
+
log("");
|
|
3715
|
+
const phone = await new Promise((resolve) => {
|
|
3716
|
+
rl.question(` ${c.bold("Google Voice number")} ${c.dim("(e.g. +12125551234):")} `, resolve);
|
|
3717
|
+
});
|
|
3718
|
+
if (!phone.trim()) {
|
|
3719
|
+
info("Cancelled.");
|
|
3720
|
+
return;
|
|
3721
|
+
}
|
|
3722
|
+
const fwdEmail = await new Promise((resolve) => {
|
|
3723
|
+
rl.question(` ${c.bold("Forwarding email")} ${c.dim("(Enter for agent email):")} `, resolve);
|
|
3724
|
+
});
|
|
3725
|
+
try {
|
|
3726
|
+
const resp = await fetch(`${apiBase}/api/agenticmail/sms/setup`, {
|
|
3727
|
+
method: "POST",
|
|
3728
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${agent.apiKey}` },
|
|
3729
|
+
body: JSON.stringify({ phoneNumber: phone.trim(), forwardingEmail: fwdEmail.trim() || void 0 })
|
|
3730
|
+
});
|
|
3731
|
+
const data = await resp.json();
|
|
3732
|
+
if (data.success) {
|
|
3733
|
+
ok(`Phone number saved: ${data.sms?.phoneNumber || phone.trim()}`);
|
|
3734
|
+
info("Make sure SMS forwarding is enabled in Google Voice settings.");
|
|
3735
|
+
} else {
|
|
3736
|
+
fail(data.error || "Setup failed");
|
|
3737
|
+
}
|
|
3738
|
+
} catch (err) {
|
|
3739
|
+
fail(err.message);
|
|
3740
|
+
}
|
|
3741
|
+
}
|
|
3742
|
+
} else {
|
|
3743
|
+
if (choice.trim() === "1") {
|
|
3744
|
+
try {
|
|
3745
|
+
const resp = await fetch(`${apiBase}/api/agenticmail/sms/messages?limit=20`, {
|
|
3746
|
+
headers: { "Authorization": `Bearer ${agent.apiKey}` }
|
|
3747
|
+
});
|
|
3748
|
+
const data = await resp.json();
|
|
3749
|
+
if (!data.messages?.length) {
|
|
3750
|
+
info("No SMS messages yet.");
|
|
3751
|
+
} else {
|
|
3752
|
+
log("");
|
|
3753
|
+
for (const msg of data.messages) {
|
|
3754
|
+
const dir = msg.direction === "inbound" ? c.green("\u2190 IN ") : c.blue("\u2192 OUT");
|
|
3755
|
+
const status = msg.status === "received" ? "" : ` ${c.dim(`[${msg.status}]`)}`;
|
|
3756
|
+
const time = new Date(msg.createdAt).toLocaleString();
|
|
3757
|
+
log(` ${dir} ${c.bold(msg.phoneNumber)}${status} ${c.dim(time)}`);
|
|
3758
|
+
log(` ${msg.body.length > 80 ? msg.body.slice(0, 80) + "..." : msg.body}`);
|
|
3759
|
+
log("");
|
|
3760
|
+
}
|
|
3761
|
+
info(`${data.messages.length} message(s)`);
|
|
3762
|
+
}
|
|
3763
|
+
} catch (err) {
|
|
3764
|
+
fail(err.message);
|
|
3765
|
+
}
|
|
3766
|
+
} else if (choice.trim() === "2") {
|
|
3767
|
+
const newPhone = await new Promise((resolve) => {
|
|
3768
|
+
rl.question(` ${c.bold("New Google Voice number")} ${c.dim("(e.g. +12125551234):")} `, resolve);
|
|
3769
|
+
});
|
|
3770
|
+
if (!newPhone.trim()) {
|
|
3771
|
+
info("Cancelled.");
|
|
3772
|
+
return;
|
|
3773
|
+
}
|
|
3774
|
+
const newFwd = await new Promise((resolve) => {
|
|
3775
|
+
rl.question(` ${c.bold("Forwarding email")} ${c.dim("(Enter to keep current):")} `, resolve);
|
|
3776
|
+
});
|
|
3777
|
+
try {
|
|
3778
|
+
const resp = await fetch(`${apiBase}/api/agenticmail/sms/setup`, {
|
|
3779
|
+
method: "POST",
|
|
3780
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${agent.apiKey}` },
|
|
3781
|
+
body: JSON.stringify({ phoneNumber: newPhone.trim(), forwardingEmail: newFwd.trim() || void 0 })
|
|
3782
|
+
});
|
|
3783
|
+
const data = await resp.json();
|
|
3784
|
+
if (data.success) {
|
|
3785
|
+
ok(`Phone number updated to: ${data.sms?.phoneNumber || newPhone.trim()}`);
|
|
3786
|
+
} else {
|
|
3787
|
+
fail(data.error || "Update failed");
|
|
3788
|
+
}
|
|
3789
|
+
} catch (err) {
|
|
3790
|
+
fail(err.message);
|
|
3791
|
+
}
|
|
3792
|
+
} else if (choice.trim() === "3") {
|
|
3793
|
+
try {
|
|
3794
|
+
const resp = await fetch(`${apiBase}/api/agenticmail/sms/verification-code?minutes=30`, {
|
|
3795
|
+
headers: { "Authorization": `Bearer ${agent.apiKey}` }
|
|
3796
|
+
});
|
|
3797
|
+
const data = await resp.json();
|
|
3798
|
+
if (data.found) {
|
|
3799
|
+
log("");
|
|
3800
|
+
ok(`Verification code found: ${c.bold(c.green(data.code))}`);
|
|
3801
|
+
log(` ${c.dim("From:")} ${data.from}`);
|
|
3802
|
+
log(` ${c.dim("Message:")} ${data.body}`);
|
|
3803
|
+
log(` ${c.dim("Received:")} ${new Date(data.receivedAt).toLocaleString()}`);
|
|
3804
|
+
} else {
|
|
3805
|
+
info("No verification codes found in the last 30 minutes.");
|
|
3806
|
+
info("Make sure Google Voice SMS forwarding is enabled and use /inbox to check for forwarded SMS emails.");
|
|
3807
|
+
}
|
|
3808
|
+
} catch (err) {
|
|
3809
|
+
fail(err.message);
|
|
3810
|
+
}
|
|
3811
|
+
} else if (choice.trim() === "4") {
|
|
3812
|
+
const confirm = await new Promise((resolve) => {
|
|
3813
|
+
rl.question(` ${c.bold("Disable SMS?")} ${c.dim("This keeps your number saved but stops SMS features. (y/N)")} `, resolve);
|
|
3814
|
+
});
|
|
3815
|
+
if (confirm.toLowerCase().startsWith("y")) {
|
|
3816
|
+
try {
|
|
3817
|
+
const resp = await fetch(`${apiBase}/api/agenticmail/sms/disable`, {
|
|
3818
|
+
method: "POST",
|
|
3819
|
+
headers: { "Authorization": `Bearer ${agent.apiKey}` }
|
|
3820
|
+
});
|
|
3821
|
+
const data = await resp.json();
|
|
3822
|
+
if (data.success) {
|
|
3823
|
+
ok("SMS disabled. Your number is still saved. Use /sms to re-enable anytime.");
|
|
3824
|
+
} else {
|
|
3825
|
+
fail(data.error || "Failed to disable");
|
|
3826
|
+
}
|
|
3827
|
+
} catch (err) {
|
|
3828
|
+
fail(err.message);
|
|
3829
|
+
}
|
|
3830
|
+
} else {
|
|
3831
|
+
info("Cancelled.");
|
|
3832
|
+
}
|
|
3833
|
+
}
|
|
3834
|
+
}
|
|
3835
|
+
log("");
|
|
3836
|
+
}
|
|
3837
|
+
},
|
|
3838
|
+
update: {
|
|
3839
|
+
desc: "Check for and install the latest AgenticMail version",
|
|
3840
|
+
run: async () => {
|
|
3841
|
+
log("");
|
|
3842
|
+
log(hr());
|
|
3843
|
+
heading("Update AgenticMail");
|
|
3844
|
+
log("");
|
|
3845
|
+
const { execSync } = await import("child_process");
|
|
3846
|
+
let currentVersion = "unknown";
|
|
3847
|
+
try {
|
|
3848
|
+
const pkg = await import("agenticmail/package.json");
|
|
3849
|
+
currentVersion = pkg.default?.version ?? "unknown";
|
|
3850
|
+
} catch {
|
|
3851
|
+
try {
|
|
3852
|
+
const { readFileSync: readFileSync3 } = await import("fs");
|
|
3853
|
+
const { join: join2, dirname: dirname2 } = await import("path");
|
|
3854
|
+
const { fileURLToPath: fileURLToPath2 } = await import("url");
|
|
3855
|
+
const thisDir = dirname2(fileURLToPath2(import.meta.url));
|
|
3856
|
+
const pkgPath = join2(thisDir, "..", "package.json");
|
|
3857
|
+
const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
|
|
3858
|
+
currentVersion = pkg.version ?? "unknown";
|
|
3859
|
+
} catch {
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
info(`Current version: ${c.bold(currentVersion)}`);
|
|
3863
|
+
let latestVersion = "unknown";
|
|
3864
|
+
try {
|
|
3865
|
+
latestVersion = execSync("npm view agenticmail version", { encoding: "utf-8", timeout: 15e3 }).trim();
|
|
3866
|
+
} catch {
|
|
3867
|
+
fail("Could not check npm for latest version. Check your internet connection.");
|
|
3868
|
+
return;
|
|
3869
|
+
}
|
|
3870
|
+
info(`Latest version: ${c.bold(latestVersion)}`);
|
|
3871
|
+
if (currentVersion === latestVersion) {
|
|
3872
|
+
ok("You are already on the latest version!");
|
|
3873
|
+
log("");
|
|
3874
|
+
return;
|
|
3875
|
+
}
|
|
3876
|
+
log("");
|
|
3877
|
+
info(`New version available: ${c.yellow(currentVersion)} \u2192 ${c.green(latestVersion)}`);
|
|
3878
|
+
let hasOpenClaw = false;
|
|
3879
|
+
let openClawVersion = "";
|
|
3880
|
+
try {
|
|
3881
|
+
openClawVersion = execSync('openclaw --version 2>/dev/null || echo ""', { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
3882
|
+
if (openClawVersion) hasOpenClaw = true;
|
|
3883
|
+
} catch {
|
|
3884
|
+
}
|
|
3885
|
+
if (hasOpenClaw) {
|
|
3886
|
+
info(`OpenClaw detected: ${c.bold(openClawVersion)}`);
|
|
3887
|
+
try {
|
|
3888
|
+
const peerDeps = execSync(`npm view agenticmail@${latestVersion} peerDependencies --json 2>/dev/null`, { encoding: "utf-8", timeout: 15e3 }).trim();
|
|
3889
|
+
if (peerDeps) {
|
|
3890
|
+
const deps = JSON.parse(peerDeps);
|
|
3891
|
+
if (deps.openclaw) {
|
|
3892
|
+
info(`Required OpenClaw version: ${c.bold(deps.openclaw)}`);
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
} catch {
|
|
3896
|
+
}
|
|
3897
|
+
info("OpenClaw plugin will also be updated.");
|
|
3898
|
+
}
|
|
3899
|
+
log("");
|
|
3900
|
+
const confirm = await new Promise((resolve) => {
|
|
3901
|
+
rl.question(` ${c.bold("Update now?")} ${c.dim("(Y/n)")} `, resolve);
|
|
3902
|
+
});
|
|
3903
|
+
if (confirm.toLowerCase().startsWith("n")) {
|
|
3904
|
+
info("Update cancelled.");
|
|
3905
|
+
log("");
|
|
3906
|
+
return;
|
|
3907
|
+
}
|
|
3908
|
+
log("");
|
|
3909
|
+
info("Updating...");
|
|
3910
|
+
try {
|
|
3911
|
+
let pm = "npm";
|
|
3912
|
+
try {
|
|
3913
|
+
execSync("pnpm --version", { stdio: "ignore", timeout: 5e3 });
|
|
3914
|
+
pm = "pnpm";
|
|
3915
|
+
} catch {
|
|
3916
|
+
try {
|
|
3917
|
+
execSync("bun --version", { stdio: "ignore", timeout: 5e3 });
|
|
3918
|
+
pm = "bun";
|
|
3919
|
+
} catch {
|
|
3920
|
+
}
|
|
3921
|
+
}
|
|
3922
|
+
let isGlobal = false;
|
|
3923
|
+
try {
|
|
3924
|
+
const globalList = execSync(`${pm === "npm" ? "npm" : pm} list -g agenticmail 2>/dev/null`, { encoding: "utf-8", timeout: 1e4 });
|
|
3925
|
+
if (globalList.includes("agenticmail")) isGlobal = true;
|
|
3926
|
+
} catch {
|
|
3927
|
+
}
|
|
3928
|
+
const scope = isGlobal ? "-g" : "";
|
|
3929
|
+
const installCmd = pm === "bun" ? `bun add ${scope} agenticmail@latest` : `${pm} install ${scope} agenticmail@latest`;
|
|
3930
|
+
info(`Running: ${c.dim(installCmd)}`);
|
|
3931
|
+
execSync(installCmd, { stdio: "inherit", timeout: 12e4 });
|
|
3932
|
+
if (hasOpenClaw) {
|
|
3933
|
+
const pluginCmd = pm === "bun" ? `bun add ${scope} @agenticmail/openclaw@latest` : `${pm} install ${scope} @agenticmail/openclaw@latest`;
|
|
3934
|
+
info(`Updating OpenClaw plugin: ${c.dim(pluginCmd)}`);
|
|
3935
|
+
try {
|
|
3936
|
+
execSync(pluginCmd, { stdio: "inherit", timeout: 12e4 });
|
|
3937
|
+
ok("OpenClaw plugin updated.");
|
|
3938
|
+
try {
|
|
3939
|
+
execSync("openclaw gateway restart", { stdio: "pipe", timeout: 3e4 });
|
|
3940
|
+
ok("OpenClaw gateway restarted.");
|
|
3941
|
+
} catch {
|
|
3942
|
+
info(`Restart OpenClaw manually: ${c.green("openclaw gateway restart")}`);
|
|
3943
|
+
}
|
|
3944
|
+
} catch (err) {
|
|
3945
|
+
log(` ${c.yellow("!")} Plugin update failed: ${err.message}`);
|
|
3946
|
+
info(`Update manually: ${c.green(pluginCmd)}`);
|
|
3947
|
+
}
|
|
3948
|
+
}
|
|
3949
|
+
log("");
|
|
3950
|
+
ok(`Updated to agenticmail@${latestVersion}`);
|
|
3951
|
+
info("Restart the shell to use the new version.");
|
|
3952
|
+
log("");
|
|
3953
|
+
} catch (err) {
|
|
3954
|
+
fail(`Update failed: ${err.message}`);
|
|
3955
|
+
info(`Try manually: ${c.green("npm install -g agenticmail@latest")}`);
|
|
3956
|
+
log("");
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
},
|
|
3620
3960
|
exit: {
|
|
3621
3961
|
desc: "Stop the server and exit",
|
|
3622
3962
|
run: async () => {
|
|
@@ -3875,6 +4215,8 @@ var c2 = {
|
|
|
3875
4215
|
yellow: (s) => `\x1B[33m${s}\x1B[0m`,
|
|
3876
4216
|
cyan: (s) => `\x1B[36m${s}\x1B[0m`,
|
|
3877
4217
|
magenta: (s) => `\x1B[35m${s}\x1B[0m`,
|
|
4218
|
+
pink: (s) => `\x1B[38;5;205m${s}\x1B[0m`,
|
|
4219
|
+
pinkBg: (s) => `\x1B[48;5;205m\x1B[97m${s}\x1B[0m`,
|
|
3878
4220
|
dim: (s) => `\x1B[90m${s}\x1B[0m`,
|
|
3879
4221
|
bold: (s) => `\x1B[1m${s}\x1B[0m`,
|
|
3880
4222
|
bgGreen: (s) => `\x1B[42m\x1B[30m${s}\x1B[0m`,
|
|
@@ -3959,6 +4301,8 @@ var Spinner = class {
|
|
|
3959
4301
|
msgChangeCounter = 0;
|
|
3960
4302
|
category;
|
|
3961
4303
|
currentMsg;
|
|
4304
|
+
progressPct = -1;
|
|
4305
|
+
// -1 = no progress bar
|
|
3962
4306
|
constructor(category, initialMsg) {
|
|
3963
4307
|
this.category = category;
|
|
3964
4308
|
const msgs = LOADING_MESSAGES[category] ?? LOADING_MESSAGES.general;
|
|
@@ -3968,13 +4312,23 @@ var Spinner = class {
|
|
|
3968
4312
|
this.frameIdx = 0;
|
|
3969
4313
|
this.msgIdx = 0;
|
|
3970
4314
|
this.msgChangeCounter = 0;
|
|
4315
|
+
this.progressPct = -1;
|
|
3971
4316
|
const msgs = LOADING_MESSAGES[this.category] ?? LOADING_MESSAGES.general;
|
|
3972
4317
|
this.interval = setInterval(() => {
|
|
3973
4318
|
const frame = SPINNER_FRAMES[this.frameIdx % SPINNER_FRAMES.length];
|
|
3974
|
-
|
|
4319
|
+
if (this.progressPct >= 0) {
|
|
4320
|
+
const barWidth = 20;
|
|
4321
|
+
const filled = Math.round(this.progressPct / 100 * barWidth);
|
|
4322
|
+
const empty = barWidth - filled;
|
|
4323
|
+
const bar = c2.pink("\u2588".repeat(filled)) + c2.dim("\u2591".repeat(empty));
|
|
4324
|
+
const pctStr = c2.pink(`${this.progressPct}%`);
|
|
4325
|
+
process.stdout.write(`\r ${c2.pink(frame)} ${bar} ${pctStr} ${c2.dim(this.currentMsg)}\x1B[K`);
|
|
4326
|
+
} else {
|
|
4327
|
+
process.stdout.write(`\r ${c2.cyan(frame)} ${c2.yellow(this.currentMsg)}\x1B[K`);
|
|
4328
|
+
}
|
|
3975
4329
|
this.frameIdx++;
|
|
3976
4330
|
this.msgChangeCounter++;
|
|
3977
|
-
if (this.msgChangeCounter >= 30) {
|
|
4331
|
+
if (this.msgChangeCounter >= 30 && this.progressPct < 0) {
|
|
3978
4332
|
this.msgChangeCounter = 0;
|
|
3979
4333
|
this.msgIdx = (this.msgIdx + 1) % msgs.length;
|
|
3980
4334
|
this.currentMsg = msgs[this.msgIdx];
|
|
@@ -3982,7 +4336,13 @@ var Spinner = class {
|
|
|
3982
4336
|
}, 100);
|
|
3983
4337
|
}
|
|
3984
4338
|
update(msg) {
|
|
3985
|
-
|
|
4339
|
+
const match = msg.match(/^__progress__:(\d+):(.*)$/);
|
|
4340
|
+
if (match) {
|
|
4341
|
+
this.progressPct = Math.min(100, parseInt(match[1], 10));
|
|
4342
|
+
this.currentMsg = match[2];
|
|
4343
|
+
} else {
|
|
4344
|
+
this.currentMsg = msg;
|
|
4345
|
+
}
|
|
3986
4346
|
this.msgChangeCounter = 0;
|
|
3987
4347
|
}
|
|
3988
4348
|
succeed(msg) {
|
|
@@ -4109,7 +4469,7 @@ async function cmdSetup() {
|
|
|
4109
4469
|
log2(` needs to send and receive real email.`);
|
|
4110
4470
|
log2("");
|
|
4111
4471
|
const hasOpenClaw = existsSync2(join(homedir(), ".openclaw", "openclaw.json"));
|
|
4112
|
-
const totalSteps = hasOpenClaw ?
|
|
4472
|
+
const totalSteps = hasOpenClaw ? 6 : 5;
|
|
4113
4473
|
log2(` Here's what we'll do:`);
|
|
4114
4474
|
log2(` ${c2.dim("1.")} Check your system for required tools`);
|
|
4115
4475
|
log2(` ${c2.dim("2.")} Create your private account and keys`);
|
|
@@ -4202,14 +4562,41 @@ async function cmdSetup() {
|
|
|
4202
4562
|
{
|
|
4203
4563
|
const spinner = new Spinner("docker");
|
|
4204
4564
|
spinner.start();
|
|
4565
|
+
const dockerSetup = new SetupManager((msg) => spinner.update(msg));
|
|
4205
4566
|
try {
|
|
4206
|
-
await
|
|
4567
|
+
await dockerSetup.ensureDocker();
|
|
4207
4568
|
spinner.succeed(`${c2.bold("Docker")} \u2014 engine running`);
|
|
4208
4569
|
} catch (err) {
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4570
|
+
const msg = err.message;
|
|
4571
|
+
if (msg === "DOCKER_MANUAL_START") {
|
|
4572
|
+
spinner.fail(`Docker installed but couldn't start automatically`);
|
|
4573
|
+
log2("");
|
|
4574
|
+
log2(` ${c2.pink(c2.bold("Don't worry! Here's how to fix this:"))}`);
|
|
4575
|
+
log2("");
|
|
4576
|
+
log2(` ${c2.pink("Step 1:")} Open a ${c2.bold("new terminal window")}`);
|
|
4577
|
+
log2(` ${c2.dim("(Command + T on Mac, or open Terminal from your dock)")}`);
|
|
4578
|
+
log2("");
|
|
4579
|
+
log2(` ${c2.pink("Step 2:")} Run this command:`);
|
|
4580
|
+
log2(` ${c2.cyan("open -a Docker")}`);
|
|
4581
|
+
log2("");
|
|
4582
|
+
log2(` ${c2.pink("Step 3:")} Wait for Docker Desktop to fully load`);
|
|
4583
|
+
log2(` ${c2.dim("You'll see a whale icon in your menu bar (top of screen).")}`);
|
|
4584
|
+
log2(` ${c2.dim("Wait until it stops animating \u2014 that means it's ready.")}`);
|
|
4585
|
+
log2("");
|
|
4586
|
+
log2(` ${c2.pink("Step 4:")} Close that terminal window`);
|
|
4587
|
+
log2(` ${c2.dim("Just close the window normally (Command + W). Don't press Ctrl+C.")}`);
|
|
4588
|
+
log2("");
|
|
4589
|
+
log2(` ${c2.pink("Step 5:")} Come back here and run:`);
|
|
4590
|
+
log2(` ${c2.green("npx agenticmail@latest")}`);
|
|
4591
|
+
log2("");
|
|
4592
|
+
log2(` ${c2.dim("That's it! Docker only needs this manual start the first time.")}`);
|
|
4593
|
+
log2(` ${c2.dim("After that, it starts automatically.")}`);
|
|
4594
|
+
} else {
|
|
4595
|
+
spinner.fail(`Couldn't start Docker: ${msg}`);
|
|
4596
|
+
log2("");
|
|
4597
|
+
log2(` ${c2.yellow("Tip:")} Install Docker manually from ${c2.cyan("https://docker.com/get-docker")}`);
|
|
4598
|
+
log2(` ${c2.dim("Then run")} ${c2.green("agenticmail setup")} ${c2.dim("again.")}`);
|
|
4599
|
+
}
|
|
4213
4600
|
process.exit(1);
|
|
4214
4601
|
}
|
|
4215
4602
|
await new Promise((r) => setTimeout(r, 300));
|
|
@@ -4218,8 +4605,9 @@ async function cmdSetup() {
|
|
|
4218
4605
|
if (!stalwart?.installed) {
|
|
4219
4606
|
const spinner = new Spinner("stalwart");
|
|
4220
4607
|
spinner.start();
|
|
4608
|
+
const stalwartSetup = new SetupManager((msg) => spinner.update(msg));
|
|
4221
4609
|
try {
|
|
4222
|
-
await
|
|
4610
|
+
await stalwartSetup.ensureStalwart();
|
|
4223
4611
|
spinner.succeed(`${c2.bold("Mail Server")} \u2014 up and running!`);
|
|
4224
4612
|
} catch (err) {
|
|
4225
4613
|
spinner.fail(`Couldn't start the mail server: ${err.message}`);
|
|
@@ -4377,9 +4765,117 @@ async function cmdSetup() {
|
|
|
4377
4765
|
} else if (!existingEmail) {
|
|
4378
4766
|
info2("No problem! You can set up email anytime by running this again.");
|
|
4379
4767
|
}
|
|
4768
|
+
if (serverReady) {
|
|
4769
|
+
log2("");
|
|
4770
|
+
log2(` ${c2.bold(`Step 5 of ${totalSteps}`)} ${c2.dim("\u2014")} ${c2.bold("Phone number access (optional)")}`);
|
|
4771
|
+
log2("");
|
|
4772
|
+
log2(` ${c2.dim("Give your AI agent a phone number via Google Voice.")}`);
|
|
4773
|
+
log2(` ${c2.dim("This lets agents receive verification codes and send texts.")}`);
|
|
4774
|
+
log2("");
|
|
4775
|
+
const wantSms = await ask(` ${c2.bold("Set up phone number access?")} ${c2.dim("(y/N)")} `);
|
|
4776
|
+
if (wantSms.toLowerCase().startsWith("y")) {
|
|
4777
|
+
log2("");
|
|
4778
|
+
log2(` ${c2.bold("What this does:")}`);
|
|
4779
|
+
log2(` Your AI agent gets a real phone number it can use to:`);
|
|
4780
|
+
log2(` ${c2.dim("*")} Receive verification codes when signing up for services`);
|
|
4781
|
+
log2(` ${c2.dim("*")} Send and receive text messages`);
|
|
4782
|
+
log2(` ${c2.dim("*")} Verify accounts on platforms that require phone numbers`);
|
|
4783
|
+
log2("");
|
|
4784
|
+
log2(` ${c2.bold("How it works:")}`);
|
|
4785
|
+
log2(` Google Voice gives you a free US phone number. When someone`);
|
|
4786
|
+
log2(` texts that number, Google forwards it to your email. Your`);
|
|
4787
|
+
log2(` agent reads the email and extracts the message or code.`);
|
|
4788
|
+
log2("");
|
|
4789
|
+
const hasVoice = await ask(` ${c2.bold("Do you already have a Google Voice number?")} ${c2.dim("(y/N)")} `);
|
|
4790
|
+
if (!hasVoice.toLowerCase().startsWith("y")) {
|
|
4791
|
+
log2("");
|
|
4792
|
+
log2(` ${c2.bold("No problem! Setting up Google Voice takes about 2 minutes:")}`);
|
|
4793
|
+
log2("");
|
|
4794
|
+
log2(` ${c2.cyan("Step 1:")} Open ${c2.bold(c2.cyan("https://voice.google.com"))} in your browser`);
|
|
4795
|
+
log2(` ${c2.cyan("Step 2:")} Sign in with your Google account`);
|
|
4796
|
+
log2(` ${c2.cyan("Step 3:")} Click ${c2.bold('"Choose a phone number"')}`);
|
|
4797
|
+
log2(` ${c2.cyan("Step 4:")} Search for a number by city or area code`);
|
|
4798
|
+
log2(` ${c2.cyan("Step 5:")} Pick a number and click ${c2.bold('"Verify"')}`);
|
|
4799
|
+
log2(` ${c2.dim("(Google will verify via your existing phone number)")}`);
|
|
4800
|
+
log2(` ${c2.cyan("Step 6:")} Once verified, go to ${c2.bold("Settings")} (gear icon)`);
|
|
4801
|
+
log2(` ${c2.cyan("Step 7:")} Under Messages, enable ${c2.bold('"Forward messages to email"')}`);
|
|
4802
|
+
log2("");
|
|
4803
|
+
log2(` ${c2.dim("That's it! Come back here when you have your number.")}`);
|
|
4804
|
+
log2("");
|
|
4805
|
+
const ready = await ask(` ${c2.bold("Press Enter when you have your Google Voice number ready...")} `);
|
|
4806
|
+
}
|
|
4807
|
+
log2("");
|
|
4808
|
+
const phoneNumber = await ask(` ${c2.bold("Your Google Voice phone number")} ${c2.dim("(e.g. +12125551234):")} `);
|
|
4809
|
+
if (phoneNumber.trim()) {
|
|
4810
|
+
const digits = phoneNumber.replace(/[^+\d]/g, "").replace(/\D/g, "");
|
|
4811
|
+
if (digits.length < 10) {
|
|
4812
|
+
log2(` ${c2.yellow("!")} That doesn't look like a valid phone number (need at least 10 digits).`);
|
|
4813
|
+
info2("You can set this up later in the shell with /sms or via the agenticmail_sms_setup tool.");
|
|
4814
|
+
} else {
|
|
4815
|
+
const forwardEmail = await ask(` ${c2.bold("Email Google Voice forwards SMS to")} ${c2.dim("(Enter to use agent email):")} `);
|
|
4816
|
+
try {
|
|
4817
|
+
const apiBase = `http://${result.config.api.host}:${result.config.api.port}`;
|
|
4818
|
+
const resp = await fetch(`${apiBase}/api/agenticmail/sms/setup`, {
|
|
4819
|
+
method: "POST",
|
|
4820
|
+
headers: {
|
|
4821
|
+
"Content-Type": "application/json",
|
|
4822
|
+
"Authorization": `Bearer ${result.config.masterKey}`
|
|
4823
|
+
},
|
|
4824
|
+
body: JSON.stringify({
|
|
4825
|
+
phoneNumber: phoneNumber.trim(),
|
|
4826
|
+
forwardingEmail: forwardEmail.trim() || void 0
|
|
4827
|
+
})
|
|
4828
|
+
});
|
|
4829
|
+
const data = await resp.json();
|
|
4830
|
+
if (data.success) {
|
|
4831
|
+
log2("");
|
|
4832
|
+
log2(` ${c2.green("\u2714")} Phone number saved: ${c2.bold(data.sms?.phoneNumber || phoneNumber.trim())}`);
|
|
4833
|
+
log2("");
|
|
4834
|
+
log2(` ${c2.bold("Important:")} Make sure you enabled SMS forwarding in Google Voice:`);
|
|
4835
|
+
log2(` ${c2.dim("voice.google.com > Settings > Messages > Forward messages to email")}`);
|
|
4836
|
+
log2("");
|
|
4837
|
+
log2(` ${c2.dim("Your agent can now receive verification codes and text messages.")}`);
|
|
4838
|
+
log2(` ${c2.dim("Manage SMS anytime in the shell with /sms")}`);
|
|
4839
|
+
} else {
|
|
4840
|
+
throw new Error(data.error || "API call failed");
|
|
4841
|
+
}
|
|
4842
|
+
} catch {
|
|
4843
|
+
try {
|
|
4844
|
+
const { readFileSync: readFileSync3, writeFileSync: writeFileSync3 } = await import("fs");
|
|
4845
|
+
const { join: join2 } = await import("path");
|
|
4846
|
+
const os = await import("os");
|
|
4847
|
+
const configPath = join2(result.config.dataDir || os.homedir() + "/.agenticmail", "config.json");
|
|
4848
|
+
let fileConfig = {};
|
|
4849
|
+
try {
|
|
4850
|
+
fileConfig = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
4851
|
+
} catch {
|
|
4852
|
+
}
|
|
4853
|
+
fileConfig.sms = {
|
|
4854
|
+
enabled: true,
|
|
4855
|
+
phoneNumber: phoneNumber.trim(),
|
|
4856
|
+
forwardingEmail: forwardEmail.trim() || "",
|
|
4857
|
+
provider: "google_voice",
|
|
4858
|
+
configuredAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
4859
|
+
};
|
|
4860
|
+
writeFileSync3(configPath, JSON.stringify(fileConfig, null, 2), { encoding: "utf-8", mode: 384 });
|
|
4861
|
+
log2(` ${c2.green("\u2714")} Phone number saved: ${c2.bold(phoneNumber.trim())}`);
|
|
4862
|
+
log2(` ${c2.dim("Make sure SMS forwarding is enabled in Google Voice settings.")}`);
|
|
4863
|
+
} catch (err) {
|
|
4864
|
+
log2(` ${c2.yellow("!")} Could not save: ${err.message}`);
|
|
4865
|
+
log2(` ${c2.dim("Set up later in the shell with /sms")}`);
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4868
|
+
}
|
|
4869
|
+
} else {
|
|
4870
|
+
info2("Skipped. Set up anytime in the shell with /sms");
|
|
4871
|
+
}
|
|
4872
|
+
} else {
|
|
4873
|
+
info2("Skipped. Add a phone number anytime with /sms in the shell.");
|
|
4874
|
+
}
|
|
4875
|
+
}
|
|
4380
4876
|
if (hasOpenClaw && serverReady) {
|
|
4381
4877
|
log2("");
|
|
4382
|
-
log2(` ${c2.bold(`Step
|
|
4878
|
+
log2(` ${c2.bold(`Step 6 of ${totalSteps}`)} ${c2.dim("\u2014")} ${c2.bold("Configure OpenClaw integration")}`);
|
|
4383
4879
|
log2("");
|
|
4384
4880
|
await registerWithOpenClaw(result.config);
|
|
4385
4881
|
}
|
|
@@ -4968,11 +5464,12 @@ async function cmdOpenClaw() {
|
|
|
4968
5464
|
log2(` This will:`);
|
|
4969
5465
|
log2(` ${c2.dim("1.")} Set up the mail server infrastructure`);
|
|
4970
5466
|
log2(` ${c2.dim("2.")} Create an agent email account`);
|
|
4971
|
-
log2(` ${c2.dim("3.")}
|
|
4972
|
-
log2(` ${c2.dim("4.")}
|
|
5467
|
+
log2(` ${c2.dim("3.")} Set up phone number access ${c2.green("NEW")}`);
|
|
5468
|
+
log2(` ${c2.dim("4.")} Configure the OpenClaw plugin`);
|
|
5469
|
+
log2(` ${c2.dim("5.")} Restart the OpenClaw gateway`);
|
|
4973
5470
|
log2("");
|
|
4974
5471
|
const setup = new SetupManager();
|
|
4975
|
-
log2(` ${c2.bold("Step 1 of
|
|
5472
|
+
log2(` ${c2.bold("Step 1 of 6")} ${c2.dim("\u2014")} ${c2.bold("Checking infrastructure")}`);
|
|
4976
5473
|
log2("");
|
|
4977
5474
|
let config;
|
|
4978
5475
|
let configPath;
|
|
@@ -5031,7 +5528,7 @@ async function cmdOpenClaw() {
|
|
|
5031
5528
|
}
|
|
5032
5529
|
}
|
|
5033
5530
|
log2("");
|
|
5034
|
-
log2(` ${c2.bold("Step 2 of
|
|
5531
|
+
log2(` ${c2.bold("Step 2 of 6")} ${c2.dim("\u2014")} ${c2.bold("Starting server")}`);
|
|
5035
5532
|
log2("");
|
|
5036
5533
|
const apiHost = config.api.host;
|
|
5037
5534
|
const apiPort = config.api.port;
|
|
@@ -5062,7 +5559,7 @@ async function cmdOpenClaw() {
|
|
|
5062
5559
|
}
|
|
5063
5560
|
}
|
|
5064
5561
|
log2("");
|
|
5065
|
-
log2(` ${c2.bold("Step 3 of
|
|
5562
|
+
log2(` ${c2.bold("Step 3 of 6")} ${c2.dim("\u2014")} ${c2.bold("Agent account")}`);
|
|
5066
5563
|
log2("");
|
|
5067
5564
|
let agentApiKey;
|
|
5068
5565
|
let agentEmail = "";
|
|
@@ -5234,7 +5731,212 @@ async function cmdOpenClaw() {
|
|
|
5234
5731
|
}
|
|
5235
5732
|
}
|
|
5236
5733
|
log2("");
|
|
5237
|
-
log2(` ${c2.bold("Step 4 of
|
|
5734
|
+
log2(` ${c2.bold("Step 4 of 6")} ${c2.dim("\u2014")} ${c2.bold("Phone number access")} ${c2.green("NEW")}`);
|
|
5735
|
+
log2("");
|
|
5736
|
+
log2(` ${c2.dim("Give your AI agent a phone number via Google Voice.")}`);
|
|
5737
|
+
log2(` ${c2.dim("Agents can receive verification codes and send texts.")}`);
|
|
5738
|
+
log2("");
|
|
5739
|
+
let smsAlreadyConfigured = false;
|
|
5740
|
+
if (agentApiKey) {
|
|
5741
|
+
try {
|
|
5742
|
+
const smsResp = await fetch(`${apiBase}/api/agenticmail/sms/config`, {
|
|
5743
|
+
headers: { "Authorization": `Bearer ${agentApiKey}` },
|
|
5744
|
+
signal: AbortSignal.timeout(3e3)
|
|
5745
|
+
});
|
|
5746
|
+
const smsData = await smsResp.json();
|
|
5747
|
+
if (smsData.sms?.enabled) {
|
|
5748
|
+
smsAlreadyConfigured = true;
|
|
5749
|
+
ok2(`SMS already configured: ${c2.bold(smsData.sms.phoneNumber)}`);
|
|
5750
|
+
}
|
|
5751
|
+
} catch {
|
|
5752
|
+
}
|
|
5753
|
+
}
|
|
5754
|
+
if (!smsAlreadyConfigured) {
|
|
5755
|
+
const wantSms = await ask(` ${c2.bold("Set up phone number access?")} ${c2.dim("(y/N)")} `);
|
|
5756
|
+
if (wantSms.toLowerCase().startsWith("y")) {
|
|
5757
|
+
log2("");
|
|
5758
|
+
const hasVoice = await ask(` ${c2.bold("Do you already have a Google Voice number?")} ${c2.dim("(y/N)")} `);
|
|
5759
|
+
if (!hasVoice.toLowerCase().startsWith("y")) {
|
|
5760
|
+
log2("");
|
|
5761
|
+
log2(` ${c2.bold("Google Voice Setup (takes about 2 minutes):")}`);
|
|
5762
|
+
log2("");
|
|
5763
|
+
log2(` ${c2.cyan("Step 1:")} Open ${c2.bold(c2.cyan("https://voice.google.com"))} in your browser`);
|
|
5764
|
+
log2(` ${c2.cyan("Step 2:")} Sign in with your Google account`);
|
|
5765
|
+
log2(` ${c2.cyan("Step 3:")} Click ${c2.bold('"Choose a phone number"')}`);
|
|
5766
|
+
log2(` ${c2.cyan("Step 4:")} Search for a number by city or area code`);
|
|
5767
|
+
log2(` ${c2.cyan("Step 5:")} Pick a number and click ${c2.bold('"Verify"')}`);
|
|
5768
|
+
log2(` ${c2.dim("(Google will send a code to your existing phone to verify)")}`);
|
|
5769
|
+
log2(` ${c2.cyan("Step 6:")} Once verified, go to ${c2.bold("Settings")} (gear icon)`);
|
|
5770
|
+
log2(` ${c2.cyan("Step 7:")} Under Messages, enable ${c2.bold('"Forward messages to email"')}`);
|
|
5771
|
+
log2("");
|
|
5772
|
+
log2(` ${c2.dim("Come back here when you have your number.")}`);
|
|
5773
|
+
log2("");
|
|
5774
|
+
await ask(` ${c2.bold("Press Enter when ready...")} `);
|
|
5775
|
+
}
|
|
5776
|
+
log2("");
|
|
5777
|
+
const phoneNumber = await ask(` ${c2.bold("Your Google Voice number")} ${c2.dim("(e.g. +12125551234):")} `);
|
|
5778
|
+
if (phoneNumber.trim()) {
|
|
5779
|
+
const digits = phoneNumber.replace(/[^+\d]/g, "").replace(/\D/g, "");
|
|
5780
|
+
if (digits.length < 10) {
|
|
5781
|
+
log2(` ${c2.yellow("!")} That doesn't look like a valid phone number.`);
|
|
5782
|
+
info2("You can set this up later in the shell with /sms");
|
|
5783
|
+
} else {
|
|
5784
|
+
let forwardingEmail;
|
|
5785
|
+
let forwardingPassword;
|
|
5786
|
+
let relayEmail = "";
|
|
5787
|
+
let relayProvider = "";
|
|
5788
|
+
try {
|
|
5789
|
+
const gwResp = await fetch(`${apiBase}/api/agenticmail/gateway/status`, {
|
|
5790
|
+
headers: { "Authorization": `Bearer ${config.masterKey}` },
|
|
5791
|
+
signal: AbortSignal.timeout(5e3)
|
|
5792
|
+
});
|
|
5793
|
+
const gwData = await gwResp.json();
|
|
5794
|
+
if (gwData.mode === "relay" && gwData.relay?.email) {
|
|
5795
|
+
relayEmail = gwData.relay.email;
|
|
5796
|
+
relayProvider = gwData.relay.provider || "";
|
|
5797
|
+
} else if (gwData.mode === "domain") {
|
|
5798
|
+
relayProvider = "domain";
|
|
5799
|
+
}
|
|
5800
|
+
} catch {
|
|
5801
|
+
}
|
|
5802
|
+
log2("");
|
|
5803
|
+
log2(` \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`);
|
|
5804
|
+
log2(` \u2502 ${c2.red(c2.bold("READ THIS"))} \u2014 Google Voice email matching is ${c2.red(c2.bold("critical"))} \u2502`);
|
|
5805
|
+
log2(` \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518`);
|
|
5806
|
+
log2("");
|
|
5807
|
+
log2(` Google Voice forwards SMS ${c2.bold("ONLY")} to the ${c2.green(c2.bold("Gmail account"))}`);
|
|
5808
|
+
log2(` you used to ${c2.green(c2.bold("sign up"))} for Google Voice.`);
|
|
5809
|
+
log2("");
|
|
5810
|
+
log2(` ${c2.yellow("If your agent can't read that Gmail, it will")} ${c2.red(c2.bold("NEVER"))}`);
|
|
5811
|
+
log2(` ${c2.yellow("receive any SMS messages.")}`);
|
|
5812
|
+
log2("");
|
|
5813
|
+
if (relayEmail) {
|
|
5814
|
+
const isGmail = relayEmail.toLowerCase().endsWith("@gmail.com");
|
|
5815
|
+
log2(` Your email relay: ${c2.bold(c2.cyan(relayEmail))}`);
|
|
5816
|
+
if (!isGmail) {
|
|
5817
|
+
log2("");
|
|
5818
|
+
log2(` ${c2.red(c2.bold("!!"))} Your relay is ${c2.bold("not Gmail")}. Google Voice won't forward here.`);
|
|
5819
|
+
log2(` ${c2.red(c2.bold("!!"))} You ${c2.bold("must")} provide the Gmail you used for Google Voice.`);
|
|
5820
|
+
log2("");
|
|
5821
|
+
const gvEmail = await ask(` ${c2.green(c2.bold("Gmail used for Google Voice:"))} `);
|
|
5822
|
+
if (gvEmail.trim() && gvEmail.toLowerCase().includes("@gmail.com")) {
|
|
5823
|
+
log2("");
|
|
5824
|
+
log2(` ${c2.dim("Get an app password at:")} ${c2.cyan("https://myaccount.google.com/apppasswords")}`);
|
|
5825
|
+
const gvPass = await ask(` ${c2.green(c2.bold("App password for"))} ${c2.bold(gvEmail.trim())}: `);
|
|
5826
|
+
if (gvPass.trim()) {
|
|
5827
|
+
forwardingEmail = gvEmail.trim();
|
|
5828
|
+
forwardingPassword = gvPass.trim();
|
|
5829
|
+
} else {
|
|
5830
|
+
log2(` ${c2.red("!")} No password. ${c2.yellow("SMS will not work without this.")}`);
|
|
5831
|
+
log2(` ${c2.dim("Fix later with /sms in the shell.")}`);
|
|
5832
|
+
}
|
|
5833
|
+
} else if (gvEmail.trim()) {
|
|
5834
|
+
log2(` ${c2.red("!")} Google Voice requires a ${c2.bold("Gmail")} address (ends in @gmail.com).`);
|
|
5835
|
+
log2(` ${c2.dim("Fix later with /sms in the shell.")}`);
|
|
5836
|
+
} else {
|
|
5837
|
+
log2(` ${c2.yellow("!")} Skipped. ${c2.dim("SMS will not work until you provide this.")}`);
|
|
5838
|
+
log2(` ${c2.dim("Fix later with /sms in the shell.")}`);
|
|
5839
|
+
}
|
|
5840
|
+
} else {
|
|
5841
|
+
log2("");
|
|
5842
|
+
log2(` ${c2.yellow("?")} Did you sign up for Google Voice with ${c2.bold("this same Gmail")}?`);
|
|
5843
|
+
log2(` ${c2.dim("If you used a different Gmail for Google Voice, say no.")}`);
|
|
5844
|
+
log2("");
|
|
5845
|
+
const sameEmail = await ask(` ${c2.bold("Same Gmail as Google Voice?")} ${c2.dim("(Y/n)")} `);
|
|
5846
|
+
if (sameEmail.toLowerCase().startsWith("n")) {
|
|
5847
|
+
log2("");
|
|
5848
|
+
log2(` ${c2.yellow(c2.bold("Different Gmail detected."))} Your agent needs access to the`);
|
|
5849
|
+
log2(` Google Voice Gmail to receive SMS.`);
|
|
5850
|
+
log2("");
|
|
5851
|
+
const gvEmail = await ask(` ${c2.green(c2.bold("Gmail used for Google Voice:"))} `);
|
|
5852
|
+
if (gvEmail.trim() && gvEmail.toLowerCase().includes("@gmail.com")) {
|
|
5853
|
+
if (gvEmail.trim().toLowerCase() === relayEmail.toLowerCase()) {
|
|
5854
|
+
log2(` ${c2.green("!")} That's the same email as your relay \u2014 you're all set!`);
|
|
5855
|
+
} else {
|
|
5856
|
+
log2("");
|
|
5857
|
+
log2(` ${c2.dim("Get an app password at:")} ${c2.cyan("https://myaccount.google.com/apppasswords")}`);
|
|
5858
|
+
const gvPass = await ask(` ${c2.green(c2.bold("App password for"))} ${c2.bold(gvEmail.trim())}: `);
|
|
5859
|
+
if (gvPass.trim()) {
|
|
5860
|
+
forwardingEmail = gvEmail.trim();
|
|
5861
|
+
forwardingPassword = gvPass.trim();
|
|
5862
|
+
} else {
|
|
5863
|
+
log2(` ${c2.red("!")} No password. ${c2.yellow("SMS will not work without this.")}`);
|
|
5864
|
+
log2(` ${c2.dim("Fix later with /sms in the shell.")}`);
|
|
5865
|
+
}
|
|
5866
|
+
}
|
|
5867
|
+
} else if (gvEmail.trim()) {
|
|
5868
|
+
log2(` ${c2.red("!")} Google Voice requires a ${c2.bold("Gmail")} address.`);
|
|
5869
|
+
log2(` ${c2.dim("Fix later with /sms in the shell.")}`);
|
|
5870
|
+
} else {
|
|
5871
|
+
log2(` ${c2.yellow("!")} Skipped. ${c2.dim("SMS may not work if emails don't match.")}`);
|
|
5872
|
+
log2(` ${c2.dim("Fix later with /sms in the shell.")}`);
|
|
5873
|
+
}
|
|
5874
|
+
}
|
|
5875
|
+
}
|
|
5876
|
+
} else if (relayProvider === "domain") {
|
|
5877
|
+
log2(` ${c2.yellow("!")} You're using ${c2.bold("domain mode")} (no Gmail relay).`);
|
|
5878
|
+
log2(` ${c2.yellow("!")} Google Voice needs a Gmail. Provide the one you signed up with.`);
|
|
5879
|
+
log2("");
|
|
5880
|
+
const gvEmail = await ask(` ${c2.green(c2.bold("Gmail used for Google Voice:"))} `);
|
|
5881
|
+
if (gvEmail.trim() && gvEmail.toLowerCase().includes("@gmail.com")) {
|
|
5882
|
+
log2("");
|
|
5883
|
+
log2(` ${c2.dim("Get an app password at:")} ${c2.cyan("https://myaccount.google.com/apppasswords")}`);
|
|
5884
|
+
const gvPass = await ask(` ${c2.green(c2.bold("App password for"))} ${c2.bold(gvEmail.trim())}: `);
|
|
5885
|
+
if (gvPass.trim()) {
|
|
5886
|
+
forwardingEmail = gvEmail.trim();
|
|
5887
|
+
forwardingPassword = gvPass.trim();
|
|
5888
|
+
} else {
|
|
5889
|
+
log2(` ${c2.red("!")} No password. ${c2.yellow("SMS will not work without this.")}`);
|
|
5890
|
+
log2(` ${c2.dim("Fix later with /sms in the shell.")}`);
|
|
5891
|
+
}
|
|
5892
|
+
} else {
|
|
5893
|
+
log2(` ${c2.yellow("!")} Skipped. ${c2.dim("SMS will not work until you provide this.")}`);
|
|
5894
|
+
log2(` ${c2.dim("Fix later with /sms in the shell.")}`);
|
|
5895
|
+
}
|
|
5896
|
+
} else {
|
|
5897
|
+
log2(` ${c2.dim("No email relay detected yet.")}`);
|
|
5898
|
+
log2("");
|
|
5899
|
+
const gvEmail = await ask(` ${c2.green(c2.bold("Gmail used for Google Voice:"))} `);
|
|
5900
|
+
if (gvEmail.trim() && gvEmail.includes("@")) {
|
|
5901
|
+
forwardingEmail = gvEmail.trim();
|
|
5902
|
+
}
|
|
5903
|
+
}
|
|
5904
|
+
if (agentApiKey) {
|
|
5905
|
+
try {
|
|
5906
|
+
const body = { phoneNumber: phoneNumber.trim() };
|
|
5907
|
+
if (forwardingEmail) body.forwardingEmail = forwardingEmail;
|
|
5908
|
+
if (forwardingPassword) body.forwardingPassword = forwardingPassword;
|
|
5909
|
+
const resp = await fetch(`${apiBase}/api/agenticmail/sms/setup`, {
|
|
5910
|
+
method: "POST",
|
|
5911
|
+
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${agentApiKey}` },
|
|
5912
|
+
body: JSON.stringify(body)
|
|
5913
|
+
});
|
|
5914
|
+
const data = await resp.json();
|
|
5915
|
+
if (data.success) {
|
|
5916
|
+
log2("");
|
|
5917
|
+
ok2(`Phone number saved: ${c2.bold(data.sms?.phoneNumber || phoneNumber.trim())}`);
|
|
5918
|
+
if (forwardingEmail) {
|
|
5919
|
+
ok2(`SMS forwarding via: ${c2.bold(forwardingEmail)}`);
|
|
5920
|
+
}
|
|
5921
|
+
log2(` ${c2.dim('Remember: enable "Forward messages to email" in Google Voice settings')}`);
|
|
5922
|
+
log2(` ${c2.dim("Manage SMS anytime in the shell with /sms")}`);
|
|
5923
|
+
} else {
|
|
5924
|
+
fail2(data.error || "Setup failed");
|
|
5925
|
+
}
|
|
5926
|
+
} catch (err) {
|
|
5927
|
+
fail2(err.message);
|
|
5928
|
+
}
|
|
5929
|
+
}
|
|
5930
|
+
}
|
|
5931
|
+
} else {
|
|
5932
|
+
info2("Skipped. Use /sms in the shell anytime.");
|
|
5933
|
+
}
|
|
5934
|
+
} else {
|
|
5935
|
+
info2("Skipped. Add a phone number anytime with /sms in the shell.");
|
|
5936
|
+
}
|
|
5937
|
+
}
|
|
5938
|
+
log2("");
|
|
5939
|
+
log2(` ${c2.bold("Step 5 of 6")} ${c2.dim("\u2014")} ${c2.bold("Installing plugin + configuring OpenClaw")}`);
|
|
5238
5940
|
log2("");
|
|
5239
5941
|
const pluginDir = resolveOpenClawPluginDir();
|
|
5240
5942
|
if (pluginDir) {
|
|
@@ -5308,7 +6010,7 @@ async function cmdOpenClaw() {
|
|
|
5308
6010
|
}
|
|
5309
6011
|
}
|
|
5310
6012
|
log2("");
|
|
5311
|
-
log2(` ${c2.bold("Step
|
|
6013
|
+
log2(` ${c2.bold("Step 6 of 6")} ${c2.dim("\u2014")} ${c2.bold("Restarting OpenClaw gateway")}`);
|
|
5312
6014
|
log2("");
|
|
5313
6015
|
let gatewayRestarted = false;
|
|
5314
6016
|
let hasOpenClawCli = false;
|
|
@@ -5356,19 +6058,64 @@ async function cmdOpenClaw() {
|
|
|
5356
6058
|
}
|
|
5357
6059
|
log2(` ${c2.dim("Master Key:")} ${c2.yellow(config.masterKey)}`);
|
|
5358
6060
|
log2(` ${c2.dim("Server:")} ${c2.cyan(apiBase)}`);
|
|
6061
|
+
let smsPhone = "";
|
|
6062
|
+
if (agentApiKey) {
|
|
6063
|
+
try {
|
|
6064
|
+
const smsResp = await fetch(`${apiBase}/api/agenticmail/sms/config`, {
|
|
6065
|
+
headers: { "Authorization": `Bearer ${agentApiKey}` },
|
|
6066
|
+
signal: AbortSignal.timeout(3e3)
|
|
6067
|
+
});
|
|
6068
|
+
const smsData = await smsResp.json();
|
|
6069
|
+
if (smsData.sms?.enabled && smsData.sms?.phoneNumber) {
|
|
6070
|
+
smsPhone = smsData.sms.phoneNumber;
|
|
6071
|
+
log2(` ${c2.dim("Phone:")} ${c2.green(smsPhone)} ${c2.dim("via Google Voice")}`);
|
|
6072
|
+
}
|
|
6073
|
+
} catch {
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
6076
|
+
if (!smsPhone) {
|
|
6077
|
+
try {
|
|
6078
|
+
const acctResp = await fetch(`${apiBase}/api/agenticmail/accounts`, {
|
|
6079
|
+
headers: { "Authorization": `Bearer ${config.masterKey}` },
|
|
6080
|
+
signal: AbortSignal.timeout(3e3)
|
|
6081
|
+
});
|
|
6082
|
+
const acctData = await acctResp.json();
|
|
6083
|
+
for (const a of acctData.agents || []) {
|
|
6084
|
+
const smsConf = a.metadata?.sms;
|
|
6085
|
+
if (smsConf?.enabled && smsConf.phoneNumber) {
|
|
6086
|
+
smsPhone = smsConf.phoneNumber;
|
|
6087
|
+
log2(` ${c2.dim("Phone:")} ${c2.green(smsPhone)} ${c2.dim("via Google Voice")} ${c2.dim("(" + a.name + ")")}`);
|
|
6088
|
+
break;
|
|
6089
|
+
}
|
|
6090
|
+
}
|
|
6091
|
+
} catch {
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
5359
6094
|
log2("");
|
|
5360
6095
|
if (gatewayRestarted) {
|
|
5361
|
-
log2(` Your agent now has ${c2.bold("
|
|
6096
|
+
log2(` Your agent now has ${c2.bold("63 email + SMS tools")} available!`);
|
|
5362
6097
|
log2(` Try: ${c2.dim('"Send an email to test@example.com"')}`);
|
|
5363
6098
|
log2("");
|
|
5364
6099
|
log2(` ${c2.bold("\u{1F380} AgenticMail Coordination")} ${c2.dim("(auto-configured)")}`);
|
|
5365
6100
|
log2(` Your agent can now use ${c2.cyan("agenticmail_call_agent")} to call other agents`);
|
|
5366
6101
|
log2(` with structured task queues, push notifications, and auto-spawned sessions.`);
|
|
5367
6102
|
log2(` This replaces sessions_spawn for coordinated multi-agent work.`);
|
|
6103
|
+
log2("");
|
|
6104
|
+
if (smsPhone) {
|
|
6105
|
+
log2(` ${c2.bold("\u{1F4F1} SMS & Phone Access")} ${c2.green("ACTIVE")}`);
|
|
6106
|
+
log2(` Phone: ${c2.bold(smsPhone)} via Google Voice`);
|
|
6107
|
+
log2(` Your agent can receive verification codes and send texts.`);
|
|
6108
|
+
log2(` Manage with ${c2.cyan("/sms")} in the shell.`);
|
|
6109
|
+
} else {
|
|
6110
|
+
log2(` ${c2.bold("\u{1F4F1} SMS & Phone Access")} ${c2.dim("(Google Voice)")}`);
|
|
6111
|
+
log2(` Your agent can receive verification codes and send texts.`);
|
|
6112
|
+
log2(` SMS messages are auto-detected from Google Voice email forwarding.`);
|
|
6113
|
+
log2(` Set up with ${c2.cyan("/sms")} in the shell or during setup wizard.`);
|
|
6114
|
+
}
|
|
5368
6115
|
} else {
|
|
5369
6116
|
log2(` ${c2.bold("Next step:")}`);
|
|
5370
6117
|
log2(` Restart your OpenClaw gateway, then your agent will`);
|
|
5371
|
-
log2(` have ${c2.bold("
|
|
6118
|
+
log2(` have ${c2.bold("63 email + SMS tools")} available!`);
|
|
5372
6119
|
}
|
|
5373
6120
|
log2("");
|
|
5374
6121
|
if (process.stdin.isTTY) {
|
|
@@ -5539,6 +6286,94 @@ async function cmdStop() {
|
|
|
5539
6286
|
}
|
|
5540
6287
|
log2("");
|
|
5541
6288
|
}
|
|
6289
|
+
async function cmdUpdate() {
|
|
6290
|
+
const { execSync } = await import("child_process");
|
|
6291
|
+
log2("");
|
|
6292
|
+
log2(` ${c2.dim("\u2500".repeat(50))}`);
|
|
6293
|
+
log2(` ${c2.bold("Update AgenticMail")}`);
|
|
6294
|
+
log2("");
|
|
6295
|
+
let currentVersion = "unknown";
|
|
6296
|
+
try {
|
|
6297
|
+
const { readFileSync: readFileSync3 } = await import("fs");
|
|
6298
|
+
const { join: join2, dirname: dirname2 } = await import("path");
|
|
6299
|
+
const { fileURLToPath: fileURLToPath2 } = await import("url");
|
|
6300
|
+
const thisDir = dirname2(fileURLToPath2(import.meta.url));
|
|
6301
|
+
const pkg = JSON.parse(readFileSync3(join2(thisDir, "..", "package.json"), "utf-8"));
|
|
6302
|
+
currentVersion = pkg.version ?? "unknown";
|
|
6303
|
+
} catch {
|
|
6304
|
+
}
|
|
6305
|
+
info2(`Current version: ${c2.bold(currentVersion)}`);
|
|
6306
|
+
let latestVersion = "unknown";
|
|
6307
|
+
try {
|
|
6308
|
+
latestVersion = execSync("npm view agenticmail version", { encoding: "utf-8", timeout: 15e3 }).trim();
|
|
6309
|
+
} catch {
|
|
6310
|
+
fail2("Could not check npm. Check your internet connection.");
|
|
6311
|
+
process.exit(1);
|
|
6312
|
+
}
|
|
6313
|
+
info2(`Latest version: ${c2.bold(latestVersion)}`);
|
|
6314
|
+
if (currentVersion === latestVersion) {
|
|
6315
|
+
ok2("Already on the latest version!");
|
|
6316
|
+
log2("");
|
|
6317
|
+
process.exit(0);
|
|
6318
|
+
}
|
|
6319
|
+
info2(`New version available: ${c2.yellow(currentVersion)} \u2192 ${c2.green(latestVersion)}`);
|
|
6320
|
+
log2("");
|
|
6321
|
+
let hasOpenClaw = false;
|
|
6322
|
+
try {
|
|
6323
|
+
execSync("which openclaw", { stdio: "ignore", timeout: 5e3 });
|
|
6324
|
+
hasOpenClaw = true;
|
|
6325
|
+
const ocVersion = execSync('openclaw --version 2>/dev/null || echo "?"', { encoding: "utf-8", timeout: 1e4 }).trim();
|
|
6326
|
+
info2(`OpenClaw detected: ${c2.bold(ocVersion)}`);
|
|
6327
|
+
} catch {
|
|
6328
|
+
}
|
|
6329
|
+
let pm = "npm";
|
|
6330
|
+
try {
|
|
6331
|
+
execSync("pnpm --version", { stdio: "ignore", timeout: 5e3 });
|
|
6332
|
+
pm = "pnpm";
|
|
6333
|
+
} catch {
|
|
6334
|
+
try {
|
|
6335
|
+
execSync("bun --version", { stdio: "ignore", timeout: 5e3 });
|
|
6336
|
+
pm = "bun";
|
|
6337
|
+
} catch {
|
|
6338
|
+
}
|
|
6339
|
+
}
|
|
6340
|
+
let isGlobal = false;
|
|
6341
|
+
try {
|
|
6342
|
+
const list = execSync(`npm list -g agenticmail 2>/dev/null`, { encoding: "utf-8", timeout: 1e4 });
|
|
6343
|
+
if (list.includes("agenticmail@")) isGlobal = true;
|
|
6344
|
+
} catch {
|
|
6345
|
+
}
|
|
6346
|
+
const scope = isGlobal ? "-g" : "";
|
|
6347
|
+
const installCmd = pm === "bun" ? `bun add ${scope} agenticmail@latest`.trim() : `${pm} install ${scope} agenticmail@latest`.trim();
|
|
6348
|
+
info2(`Running: ${c2.dim(installCmd)}`);
|
|
6349
|
+
try {
|
|
6350
|
+
execSync(installCmd, { stdio: "inherit", timeout: 12e4 });
|
|
6351
|
+
ok2(`Updated to agenticmail@${latestVersion}`);
|
|
6352
|
+
} catch (err) {
|
|
6353
|
+
fail2(`Update failed: ${err.message}`);
|
|
6354
|
+
info2(`Try: ${c2.green("npm install -g agenticmail@latest")}`);
|
|
6355
|
+
process.exit(1);
|
|
6356
|
+
}
|
|
6357
|
+
if (hasOpenClaw) {
|
|
6358
|
+
const pluginCmd = pm === "bun" ? `bun add ${scope} @agenticmail/openclaw@latest`.trim() : `${pm} install ${scope} @agenticmail/openclaw@latest`.trim();
|
|
6359
|
+
info2(`Updating OpenClaw plugin: ${c2.dim(pluginCmd)}`);
|
|
6360
|
+
try {
|
|
6361
|
+
execSync(pluginCmd, { stdio: "inherit", timeout: 12e4 });
|
|
6362
|
+
ok2("OpenClaw plugin updated.");
|
|
6363
|
+
try {
|
|
6364
|
+
execSync("openclaw gateway restart", { stdio: "pipe", timeout: 3e4 });
|
|
6365
|
+
ok2("OpenClaw gateway restarted.");
|
|
6366
|
+
} catch {
|
|
6367
|
+
info2(`Restart OpenClaw: ${c2.green("openclaw gateway restart")}`);
|
|
6368
|
+
}
|
|
6369
|
+
} catch {
|
|
6370
|
+
info2(`Update plugin manually: ${c2.green(pluginCmd)}`);
|
|
6371
|
+
}
|
|
6372
|
+
}
|
|
6373
|
+
log2("");
|
|
6374
|
+
ok2("Update complete!");
|
|
6375
|
+
log2("");
|
|
6376
|
+
}
|
|
5542
6377
|
var command = process.argv[2];
|
|
5543
6378
|
switch (command) {
|
|
5544
6379
|
case "setup":
|
|
@@ -5575,6 +6410,12 @@ switch (command) {
|
|
|
5575
6410
|
process.exit(1);
|
|
5576
6411
|
});
|
|
5577
6412
|
break;
|
|
6413
|
+
case "update":
|
|
6414
|
+
cmdUpdate().catch((err) => {
|
|
6415
|
+
console.error(err);
|
|
6416
|
+
process.exit(1);
|
|
6417
|
+
});
|
|
6418
|
+
break;
|
|
5578
6419
|
case "help":
|
|
5579
6420
|
case "--help":
|
|
5580
6421
|
case "-h":
|
|
@@ -5588,6 +6429,7 @@ switch (command) {
|
|
|
5588
6429
|
log2(` ${c2.green("agenticmail stop")} Stop the server`);
|
|
5589
6430
|
log2(` ${c2.green("agenticmail status")} See what's running`);
|
|
5590
6431
|
log2(` ${c2.green("agenticmail openclaw")} Set up AgenticMail for OpenClaw`);
|
|
6432
|
+
log2(` ${c2.green("agenticmail update")} Update to the latest version`);
|
|
5591
6433
|
log2("");
|
|
5592
6434
|
process.exit(0);
|
|
5593
6435
|
default:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agenticmail",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Email infrastructure for AI agents — send and receive real email programmatically",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"prepublishOnly": "npm run build"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@agenticmail/api": "^0.
|
|
30
|
-
"@agenticmail/core": "^0.
|
|
29
|
+
"@agenticmail/api": "^0.5.0",
|
|
30
|
+
"@agenticmail/core": "^0.5.0",
|
|
31
31
|
"json5": "^2.2.3"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|