@agenticmail/cli 0.5.57

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.
@@ -0,0 +1 @@
1
+ export { AccountManager, AddressInfo, Agent, AgenticMailClient, AgenticMailClientOptions, AgenticMailConfig, Attachment, CloudflareClient, CreateAgentOptions, DNSConfigurator, DnsRecord, DnsSetupResult, DomainInfo, DomainManager, DomainModeConfig, DomainPurchaseResult, DomainPurchaser, DomainSearchResult, DomainSetupResult, EmailEnvelope, EmailSearchIndex, GatewayConfig, GatewayManager, GatewayManagerOptions, GatewayMode, GatewayStatus, InboundEmail, InboxEvent, InboxExpungeEvent, InboxFlagsEvent, InboxNewEvent, InboxWatcher, InboxWatcherOptions, MailReceiver, MailReceiverOptions, MailSender, MailSenderOptions, MailboxInfo, ParsedAttachment, ParsedEmail, PurchasedDomain, RELAY_PRESETS, RelayConfig, RelayGateway, RelayProvider, SearchCriteria, SearchableEmail, SendMailOptions, SendResult, StalwartAdmin, StalwartAdminOptions, StalwartPrincipal, TunnelConfig, TunnelManager, WatcherOptions, closeDatabase, createTestDatabase, ensureDataDir, getDatabase, parseEmail, resolveConfig, saveConfig } from '@agenticmail/core';
package/dist/index.js ADDED
@@ -0,0 +1,43 @@
1
+ // src/index.ts
2
+ import { AgenticMailClient } from "@agenticmail/core";
3
+ import { resolveConfig, ensureDataDir, saveConfig } from "@agenticmail/core";
4
+ import { StalwartAdmin } from "@agenticmail/core";
5
+ import { AccountManager } from "@agenticmail/core";
6
+ import { MailSender } from "@agenticmail/core";
7
+ import { MailReceiver } from "@agenticmail/core";
8
+ import { parseEmail } from "@agenticmail/core";
9
+ import { InboxWatcher } from "@agenticmail/core";
10
+ import { getDatabase, closeDatabase, createTestDatabase } from "@agenticmail/core";
11
+ import { EmailSearchIndex } from "@agenticmail/core";
12
+ import { DomainManager } from "@agenticmail/core";
13
+ import { GatewayManager } from "@agenticmail/core";
14
+ import { RelayGateway } from "@agenticmail/core";
15
+ import { CloudflareClient } from "@agenticmail/core";
16
+ import { DomainPurchaser } from "@agenticmail/core";
17
+ import { DNSConfigurator } from "@agenticmail/core";
18
+ import { TunnelManager } from "@agenticmail/core";
19
+ import { RELAY_PRESETS } from "@agenticmail/core";
20
+ export {
21
+ AccountManager,
22
+ AgenticMailClient,
23
+ CloudflareClient,
24
+ DNSConfigurator,
25
+ DomainManager,
26
+ DomainPurchaser,
27
+ EmailSearchIndex,
28
+ GatewayManager,
29
+ InboxWatcher,
30
+ MailReceiver,
31
+ MailSender,
32
+ RELAY_PRESETS,
33
+ RelayGateway,
34
+ StalwartAdmin,
35
+ TunnelManager,
36
+ closeDatabase,
37
+ createTestDatabase,
38
+ ensureDataDir,
39
+ getDatabase,
40
+ parseEmail,
41
+ resolveConfig,
42
+ saveConfig
43
+ };
@@ -0,0 +1,242 @@
1
+ // src/ws-chat.ts
2
+ import { createPrivateKey, createPublicKey, sign, createHash, generateKeyPairSync, randomUUID } from "crypto";
3
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
4
+ import { join, dirname } from "path";
5
+ var PROTOCOL_VERSION = 3;
6
+ function base64UrlEncode(buf) {
7
+ return buf.toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
8
+ }
9
+ function derivePublicKeyRaw(publicKeyPem) {
10
+ const key = createPublicKey(publicKeyPem);
11
+ const spki = key.export({ type: "spki", format: "der" });
12
+ return Buffer.from(spki).subarray(-32);
13
+ }
14
+ function fingerprintPublicKey(publicKeyPem) {
15
+ const raw = derivePublicKeyRaw(publicKeyPem);
16
+ return createHash("sha256").update(raw).digest("hex");
17
+ }
18
+ function loadOrCreateIdentity() {
19
+ const homedir = process.env.HOME || process.env.USERPROFILE || "";
20
+ const filePath = join(homedir, ".agenticmail", "device-identity.json");
21
+ try {
22
+ if (existsSync(filePath)) {
23
+ const parsed = JSON.parse(readFileSync(filePath, "utf8"));
24
+ if (parsed?.version === 1 && parsed.privateKeyPem && parsed.publicKeyPem) {
25
+ return {
26
+ deviceId: fingerprintPublicKey(parsed.publicKeyPem),
27
+ publicKeyPem: parsed.publicKeyPem,
28
+ privateKeyPem: parsed.privateKeyPem
29
+ };
30
+ }
31
+ }
32
+ } catch {
33
+ }
34
+ const { publicKey, privateKey } = generateKeyPairSync("ed25519");
35
+ const publicKeyPem = publicKey.export({ type: "spki", format: "pem" });
36
+ const privateKeyPem = privateKey.export({ type: "pkcs8", format: "pem" });
37
+ const deviceId = fingerprintPublicKey(publicKeyPem);
38
+ mkdirSync(dirname(filePath), { recursive: true });
39
+ writeFileSync(filePath, JSON.stringify({ version: 1, deviceId, publicKeyPem, privateKeyPem, createdAtMs: Date.now() }, null, 2) + "\n", { mode: 384 });
40
+ return { deviceId, publicKeyPem, privateKeyPem };
41
+ }
42
+ function signPayload(privateKeyPem, payload) {
43
+ const key = createPrivateKey(privateKeyPem);
44
+ return base64UrlEncode(sign(null, Buffer.from(payload, "utf8"), key));
45
+ }
46
+ var WsChat = class {
47
+ ws = null;
48
+ reqId = 0;
49
+ connected = false;
50
+ pendingRequests = /* @__PURE__ */ new Map();
51
+ chatResolve = null;
52
+ chatBuffer = "";
53
+ onDelta = null;
54
+ opts;
55
+ connectResolve = null;
56
+ connectReject = null;
57
+ identity;
58
+ connectNonce;
59
+ constructor(opts) {
60
+ this.opts = opts;
61
+ this.identity = loadOrCreateIdentity();
62
+ }
63
+ sendConnect() {
64
+ const id = String(++this.reqId);
65
+ this.pendingRequests.set(id, {
66
+ resolve: () => {
67
+ this.connected = true;
68
+ this.connectResolve?.();
69
+ },
70
+ reject: (err) => {
71
+ this.connectReject?.(err);
72
+ }
73
+ });
74
+ const scopes = ["operator.admin", "operator.write", "operator.read"];
75
+ const signedAtMs = Date.now();
76
+ const nonce = this.connectNonce;
77
+ const version = nonce ? "v2" : "v1";
78
+ const payloadParts = [
79
+ version,
80
+ this.identity.deviceId,
81
+ "gateway-client",
82
+ "backend",
83
+ "operator",
84
+ scopes.join(","),
85
+ String(signedAtMs),
86
+ this.opts.token
87
+ ];
88
+ if (version === "v2") payloadParts.push(nonce ?? "");
89
+ const payloadStr = payloadParts.join("|");
90
+ const signature = signPayload(this.identity.privateKeyPem, payloadStr);
91
+ this.ws.send(JSON.stringify({
92
+ type: "req",
93
+ id,
94
+ method: "connect",
95
+ params: {
96
+ minProtocol: PROTOCOL_VERSION,
97
+ maxProtocol: PROTOCOL_VERSION,
98
+ client: {
99
+ id: "gateway-client",
100
+ displayName: "AgenticMail Chat",
101
+ version: "1.0.0",
102
+ platform: process.platform,
103
+ mode: "backend"
104
+ },
105
+ caps: [],
106
+ auth: { token: this.opts.token },
107
+ role: "operator",
108
+ scopes,
109
+ device: {
110
+ id: this.identity.deviceId,
111
+ publicKey: base64UrlEncode(derivePublicKeyRaw(this.identity.publicKeyPem)),
112
+ signature,
113
+ signedAt: signedAtMs,
114
+ nonce
115
+ }
116
+ }
117
+ }));
118
+ }
119
+ async connect() {
120
+ return new Promise((resolve, reject) => {
121
+ this.connectResolve = resolve;
122
+ this.connectReject = reject;
123
+ const url = this.opts.gatewayUrl.replace(/^http/, "ws");
124
+ this.ws = new WebSocket(url);
125
+ const timeout = setTimeout(() => {
126
+ reject(new Error("Connection timeout"));
127
+ this.ws?.close();
128
+ }, 15e3);
129
+ this.ws.onopen = () => {
130
+ };
131
+ this.ws.onmessage = (event) => {
132
+ try {
133
+ const data = JSON.parse(typeof event.data === "string" ? event.data : event.data.toString());
134
+ if (data.type === "event" && data.event === "connect.challenge") {
135
+ this.connectNonce = data.payload?.nonce;
136
+ this.sendConnect();
137
+ return;
138
+ }
139
+ this.handleMessage(data);
140
+ } catch {
141
+ }
142
+ };
143
+ this.ws.onerror = () => {
144
+ clearTimeout(timeout);
145
+ reject(new Error("Connection failed"));
146
+ };
147
+ this.ws.onclose = () => {
148
+ clearTimeout(timeout);
149
+ this.connected = false;
150
+ if (this.chatResolve) {
151
+ this.chatResolve({ text: this.chatBuffer, done: true, error: "connection closed" });
152
+ this.chatResolve = null;
153
+ }
154
+ };
155
+ const origResolve = this.connectResolve;
156
+ this.connectResolve = () => {
157
+ clearTimeout(timeout);
158
+ origResolve?.();
159
+ };
160
+ });
161
+ }
162
+ handleMessage(data) {
163
+ if (data.type === "res" && typeof data.id === "string") {
164
+ const pending = this.pendingRequests.get(data.id);
165
+ if (pending) {
166
+ this.pendingRequests.delete(data.id);
167
+ if (data.ok === false) {
168
+ pending.reject(new Error(data.error?.message || "request failed"));
169
+ } else {
170
+ pending.resolve(data.result ?? data);
171
+ }
172
+ }
173
+ return;
174
+ }
175
+ if (data.type === "event" || data.type === "evt") {
176
+ const event = data.event;
177
+ const payload = data.payload;
178
+ if (event === "chat") {
179
+ const text = payload?.message?.content?.[0]?.text ?? "";
180
+ const state = payload?.state;
181
+ if (state === "delta" && text) {
182
+ this.chatBuffer = text;
183
+ this.onDelta?.(text);
184
+ }
185
+ if (state === "final") {
186
+ if (text) this.chatBuffer = text;
187
+ if (this.chatResolve) {
188
+ this.chatResolve({ text: this.chatBuffer, done: true });
189
+ this.chatResolve = null;
190
+ }
191
+ }
192
+ }
193
+ }
194
+ }
195
+ async send(message, opts) {
196
+ if (!this.connected || !this.ws) throw new Error("Not connected");
197
+ this.chatBuffer = "";
198
+ this.onDelta = opts?.onDelta ?? null;
199
+ const id = String(++this.reqId);
200
+ const timeoutMs = opts?.timeoutMs ?? 12e4;
201
+ return new Promise((resolve, reject) => {
202
+ this.chatResolve = resolve;
203
+ const timer = setTimeout(() => {
204
+ if (this.chatResolve) {
205
+ this.chatResolve({ text: this.chatBuffer, done: true, error: "timeout" });
206
+ this.chatResolve = null;
207
+ }
208
+ }, timeoutMs);
209
+ this.pendingRequests.set(id, {
210
+ resolve: () => {
211
+ },
212
+ reject: (err) => {
213
+ clearTimeout(timer);
214
+ this.chatResolve = null;
215
+ reject(err);
216
+ }
217
+ });
218
+ const idempotencyKey = randomUUID();
219
+ this.ws.send(JSON.stringify({
220
+ type: "req",
221
+ id,
222
+ method: "chat.send",
223
+ params: {
224
+ message,
225
+ sessionKey: opts?.sessionKey ?? this.opts.sessionKey ?? "agenticmail-chat",
226
+ idempotencyKey
227
+ }
228
+ }));
229
+ });
230
+ }
231
+ close() {
232
+ this.ws?.close();
233
+ this.ws = null;
234
+ this.connected = false;
235
+ }
236
+ get isConnected() {
237
+ return this.connected;
238
+ }
239
+ };
240
+ export {
241
+ WsChat
242
+ };
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@agenticmail/cli",
3
+ "version": "0.5.57",
4
+ "description": "Email and SMS infrastructure for AI agents — the first platform to give agents real email addresses and phone numbers",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "bin": {
9
+ "agenticmail": "dist/cli.js"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "import": "./dist/index.js",
14
+ "types": "./dist/index.d.ts"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "scripts",
20
+ "README.md",
21
+ "REFERENCE.md",
22
+ "LICENSE"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup src/index.ts src/cli.ts --format esm --dts --clean",
26
+ "test": "vitest run --passWithNoTests",
27
+ "preuninstall": "node scripts/uninstall.mjs",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "dependencies": {
31
+ "@agenticmail/api": "^0.5.56",
32
+ "@agenticmail/core": "^0.5.0",
33
+ "json5": "^2.2.3"
34
+ },
35
+ "devDependencies": {
36
+ "tsup": "^8.4.0",
37
+ "typescript": "^5.7.0",
38
+ "vitest": "^3.0.0"
39
+ },
40
+ "engines": {
41
+ "node": ">=20"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/agenticmail/agenticmail.git",
46
+ "directory": "agenticmail"
47
+ },
48
+ "homepage": "https://github.com/agenticmail/agenticmail",
49
+ "bugs": "https://github.com/agenticmail/agenticmail/issues",
50
+ "keywords": [
51
+ "email",
52
+ "sms",
53
+ "phone",
54
+ "ai",
55
+ "agent",
56
+ "mail",
57
+ "smtp",
58
+ "imap",
59
+ "mcp",
60
+ "ai-agent",
61
+ "agenticmail",
62
+ "llm",
63
+ "gateway",
64
+ "cloudflare",
65
+ "google-voice",
66
+ "text-message",
67
+ "verification-code",
68
+ "openclaw"
69
+ ],
70
+ "publishConfig": {
71
+ "access": "public"
72
+ },
73
+ "author": "Ope Olatunji",
74
+ "license": "MIT"
75
+ }
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Cleanup script that runs on `npm uninstall agenticmail`.
5
+ *
6
+ * 1. Unloads & removes the launchd / systemd auto-start service
7
+ * 2. Stops & removes the agenticmail-stalwart Docker container
8
+ * 3. Cleans agenticmail entries from OpenClaw config
9
+ * 4. Removes ~/.agenticmail data directory
10
+ */
11
+
12
+ import { execSync, execFileSync } from 'node:child_process';
13
+ import { readFileSync, writeFileSync, existsSync, rmSync, unlinkSync, lstatSync, realpathSync } from 'node:fs';
14
+ import { join } from 'node:path';
15
+ import { homedir, platform } from 'node:os';
16
+
17
+ const home = homedir();
18
+ const os = platform();
19
+
20
+ function log(msg) { console.log(`[agenticmail] ${msg}`); }
21
+ function tryExec(cmd, opts = {}) { try { execSync(cmd, { timeout: 15_000, stdio: 'ignore', ...opts }); } catch { /* ignore */ } }
22
+
23
+ // ── 1. Unload auto-start service ─────────────────────────────────
24
+
25
+ if (os === 'darwin') {
26
+ const plist = join(home, 'Library', 'LaunchAgents', 'com.agenticmail.server.plist');
27
+ if (existsSync(plist)) {
28
+ log('Unloading launchd service...');
29
+ tryExec(`launchctl bootout gui/$(id -u) "${plist}"`);
30
+ try { unlinkSync(plist); } catch { /* ignore */ }
31
+ }
32
+ } else if (os === 'linux') {
33
+ const unit = 'agenticmail.service';
34
+ tryExec(`systemctl --user stop ${unit}`);
35
+ tryExec(`systemctl --user disable ${unit}`);
36
+ const unitPath = join(home, '.config', 'systemd', 'user', unit);
37
+ if (existsSync(unitPath)) {
38
+ try { unlinkSync(unitPath); } catch { /* ignore */ }
39
+ tryExec('systemctl --user daemon-reload');
40
+ }
41
+ }
42
+
43
+ // ── 2. Stop Docker container ──────────────────────────────────────
44
+
45
+ try {
46
+ const ps = execFileSync('docker', ['ps', '-a', '--filter', 'name=agenticmail-stalwart', '--format', '{{.Names}}'],
47
+ { timeout: 10_000, stdio: ['ignore', 'pipe', 'ignore'] }).toString().trim();
48
+ if (ps.includes('agenticmail-stalwart')) {
49
+ log('Stopping mail server container...');
50
+ tryExec('docker rm -f agenticmail-stalwart');
51
+ }
52
+ } catch { /* docker not available — skip */ }
53
+
54
+ // ── 3. Clean OpenClaw config ──────────────────────────────────────
55
+
56
+ const openclawConfig = join(home, '.openclaw', 'openclaw.json');
57
+ if (existsSync(openclawConfig)) {
58
+ try {
59
+ const raw = readFileSync(openclawConfig, 'utf-8');
60
+ const config = JSON.parse(raw);
61
+ let changed = false;
62
+
63
+ // Remove plugins.entries.agenticmail
64
+ if (config.plugins?.entries?.agenticmail) {
65
+ delete config.plugins.entries.agenticmail;
66
+ changed = true;
67
+ if (Object.keys(config.plugins.entries).length === 0) delete config.plugins.entries;
68
+ }
69
+
70
+ // Remove our path from plugins.load.paths
71
+ if (Array.isArray(config.plugins?.load?.paths)) {
72
+ const before = config.plugins.load.paths.length;
73
+ config.plugins.load.paths = config.plugins.load.paths.filter(
74
+ (p) => !p.includes('@agenticmail')
75
+ );
76
+ if (config.plugins.load.paths.length !== before) changed = true;
77
+ if (config.plugins.load.paths.length === 0) {
78
+ delete config.plugins.load.paths;
79
+ if (config.plugins.load && Object.keys(config.plugins.load).length === 0) delete config.plugins.load;
80
+ }
81
+ }
82
+
83
+ if (changed) {
84
+ writeFileSync(openclawConfig, JSON.stringify(config, null, 2) + '\n');
85
+ log('Cleaned OpenClaw config');
86
+ }
87
+ } catch { /* don't fail uninstall */ }
88
+ }
89
+
90
+ // ── 4. Remove ~/.agenticmail ──────────────────────────────────────
91
+
92
+ const dataDir = join(home, '.agenticmail');
93
+ if (existsSync(dataDir)) {
94
+ // Safety: verify it's not a symlink pointing outside home (symlink attack prevention)
95
+ try {
96
+ const stat = lstatSync(dataDir);
97
+ if (stat.isSymbolicLink()) {
98
+ const realPath = realpathSync(dataDir);
99
+ if (!realPath.startsWith(home)) {
100
+ log(`WARNING: ~/.agenticmail is a symlink to ${realPath} (outside home dir) — skipping removal`);
101
+ } else {
102
+ log('Removing ~/.agenticmail/ (symlink) ...');
103
+ try { rmSync(dataDir, { recursive: true, force: true }); } catch { /* ignore */ }
104
+ }
105
+ } else {
106
+ log('Removing ~/.agenticmail/ ...');
107
+ try { rmSync(dataDir, { recursive: true, force: true }); } catch { /* ignore */ }
108
+ }
109
+ } catch { /* stat failed — skip */ }
110
+ }
111
+
112
+ log('Uninstall complete.');