agenticmail 0.3.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.
@@ -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,67 @@
1
+ {
2
+ "name": "agenticmail",
3
+ "version": "0.3.0",
4
+ "description": "Email infrastructure for AI agents — send and receive real email programmatically",
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
+ "README.md",
20
+ "REFERENCE.md",
21
+ "LICENSE"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup src/index.ts src/cli.ts --format esm --dts --clean",
25
+ "test": "vitest run",
26
+ "prepublishOnly": "npm run build"
27
+ },
28
+ "dependencies": {
29
+ "@agenticmail/api": "^0.3.0",
30
+ "@agenticmail/core": "^0.3.0",
31
+ "json5": "^2.2.3"
32
+ },
33
+ "devDependencies": {
34
+ "tsup": "^8.4.0",
35
+ "typescript": "^5.7.0",
36
+ "vitest": "^3.0.0"
37
+ },
38
+ "engines": {
39
+ "node": ">=20"
40
+ },
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/agenticmail/agenticmail.git",
44
+ "directory": "agenticmail"
45
+ },
46
+ "homepage": "https://github.com/agenticmail/agenticmail",
47
+ "bugs": "https://github.com/agenticmail/agenticmail/issues",
48
+ "keywords": [
49
+ "email",
50
+ "ai",
51
+ "agent",
52
+ "mail",
53
+ "smtp",
54
+ "imap",
55
+ "mcp",
56
+ "ai-agent",
57
+ "agenticmail",
58
+ "llm",
59
+ "gateway",
60
+ "cloudflare"
61
+ ],
62
+ "publishConfig": {
63
+ "access": "public"
64
+ },
65
+ "author": "Ope Olatunji",
66
+ "license": "MIT"
67
+ }