@agenticmail/api 0.7.1 → 0.7.3
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 +17 -2
- package/REFERENCE.md +1 -1
- package/dist/index.js +105 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,7 +10,9 @@ This package runs a web server that handles everything: sending email and SMS, r
|
|
|
10
10
|
npm install @agenticmail/api
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
**Requirements:** Node.js
|
|
13
|
+
**Requirements:** Node.js 22+ (uses Node's built-in `node:sqlite`, no native compilation), `@agenticmail/core@^0.7.0`, Stalwart Mail Server running (via Docker / Colima).
|
|
14
|
+
|
|
15
|
+
**Default listen address:** `http://127.0.0.1:3829`. The port changed from `3100` to `3829` in `0.7.x` to avoid clashes with common dev-tool defaults (Grafana Loki, Express scaffolds, etc.). Override via `AGENTICMAIL_API_PORT` env var or `api.port` in `~/.agenticmail/config.json`.
|
|
14
16
|
|
|
15
17
|
---
|
|
16
18
|
|
|
@@ -24,8 +26,21 @@ The API server is the central hub. It sits between agents (or any client) and th
|
|
|
24
26
|
- **The interactive shell** (`agenticmail start`) — powers every command in the CLI
|
|
25
27
|
- **The MCP server** (`@agenticmail/mcp`) — translates AI tool calls into API requests
|
|
26
28
|
- **OpenClaw sub-agents** (`@agenticmail/openclaw`) — same thing but for the OpenClaw framework
|
|
29
|
+
- **Claude Code sessions** (`@agenticmail/claudecode`) — surfaces every AgenticMail agent as a callable subagent and bridges inbound mail/tasks to Claude-powered workers via a dispatcher daemon. The API auto-mounts integration routes under `/api/agenticmail/integrations/claudecode/*` when `@agenticmail/claudecode` is installed as an optional dependency.
|
|
27
30
|
- **Your own code** — any HTTP client can use the API
|
|
28
31
|
|
|
32
|
+
### Claude Code self-install endpoint
|
|
33
|
+
|
|
34
|
+
When `@agenticmail/claudecode` is installed alongside the API server, three additional routes light up:
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
GET /api/agenticmail/integrations/claudecode/status
|
|
38
|
+
POST /api/agenticmail/integrations/claudecode/install
|
|
39
|
+
POST /api/agenticmail/integrations/claudecode/uninstall
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
These are mounted **before** the master-key auth middleware on purpose — a fresh Claude Code session that doesn't yet have AgenticMail wired up has no way to know the master key, so requiring it would defeat the "agent installs itself" use case. The API binds to `127.0.0.1` by default, so anything that can reach these endpoints can already read `~/.agenticmail/config.json` — the unauthenticated install endpoint doesn't widen the attack surface beyond that. **If you bind the API to a non-loopback interface, put auth or a firewall in front of it** (same caveat as every other unauthenticated route on the server, e.g. `/health`).
|
|
43
|
+
|
|
29
44
|
---
|
|
30
45
|
|
|
31
46
|
## How Authentication Works
|
|
@@ -354,7 +369,7 @@ JSON parse errors (malformed request bodies) return a clear 400 error rather tha
|
|
|
354
369
|
| Variable | Required | Default | Description |
|
|
355
370
|
|----------|----------|---------|-------------|
|
|
356
371
|
| `AGENTICMAIL_MASTER_KEY` | Yes | — | Admin API key (the human owner's key) |
|
|
357
|
-
| `AGENTICMAIL_API_PORT` | No | `
|
|
372
|
+
| `AGENTICMAIL_API_PORT` | No | `3829` | Port for the API server |
|
|
358
373
|
| `STALWART_URL` | No | `http://localhost:8080` | Stalwart mail server admin URL |
|
|
359
374
|
| `STALWART_ADMIN_USER` | No | `admin` | Stalwart admin username |
|
|
360
375
|
| `STALWART_ADMIN_PASSWORD` | No | `changeme` | Stalwart admin password |
|
package/REFERENCE.md
CHANGED
|
@@ -1297,7 +1297,7 @@ Signals: SIGTERM and SIGINT both trigger shutdown.
|
|
|
1297
1297
|
| Variable | Required | Default | Description |
|
|
1298
1298
|
|----------|----------|---------|-------------|
|
|
1299
1299
|
| `AGENTICMAIL_MASTER_KEY` | Yes | — | Master API key |
|
|
1300
|
-
| `AGENTICMAIL_API_PORT` | No | `
|
|
1300
|
+
| `AGENTICMAIL_API_PORT` | No | `3829` | Server port |
|
|
1301
1301
|
| `STALWART_URL` | No | `http://localhost:8080` | Stalwart admin URL |
|
|
1302
1302
|
| `STALWART_ADMIN_USER` | No | `admin` | Stalwart admin user |
|
|
1303
1303
|
| `STALWART_ADMIN_PASSWORD` | No | `changeme` | Stalwart admin password |
|
package/dist/index.js
CHANGED
|
@@ -246,8 +246,72 @@ function createHealthRoutes(stalwart) {
|
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
// src/routes/accounts.ts
|
|
249
|
-
import { Router as
|
|
249
|
+
import { Router as Router3 } from "express";
|
|
250
250
|
import { AGENT_ROLES, AgentDeletionService } from "@agenticmail/core";
|
|
251
|
+
|
|
252
|
+
// src/routes/system-events.ts
|
|
253
|
+
import { Router as Router2 } from "express";
|
|
254
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
255
|
+
function pushSystemEvent(event) {
|
|
256
|
+
if (listeners.size === 0) return;
|
|
257
|
+
const data = `data: ${JSON.stringify(event)}
|
|
258
|
+
|
|
259
|
+
`;
|
|
260
|
+
for (const entry of listeners) {
|
|
261
|
+
try {
|
|
262
|
+
entry.res.write(data);
|
|
263
|
+
} catch {
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function closeAllSystemEventListeners() {
|
|
268
|
+
for (const entry of listeners) {
|
|
269
|
+
try {
|
|
270
|
+
entry.cleanup();
|
|
271
|
+
} catch {
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
entry.res.end();
|
|
275
|
+
} catch {
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
listeners.clear();
|
|
279
|
+
}
|
|
280
|
+
function createSystemEventRoutes() {
|
|
281
|
+
const router = Router2();
|
|
282
|
+
router.get("/system/events", requireMaster, (req, res) => {
|
|
283
|
+
res.setHeader("Content-Type", "text/event-stream");
|
|
284
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
285
|
+
res.setHeader("Connection", "keep-alive");
|
|
286
|
+
res.setHeader("X-Accel-Buffering", "no");
|
|
287
|
+
res.flushHeaders();
|
|
288
|
+
res.write(`data: ${JSON.stringify({ type: "connected" })}
|
|
289
|
+
|
|
290
|
+
`);
|
|
291
|
+
const pingTimer = setInterval(() => {
|
|
292
|
+
try {
|
|
293
|
+
res.write(`: ping
|
|
294
|
+
|
|
295
|
+
`);
|
|
296
|
+
} catch {
|
|
297
|
+
}
|
|
298
|
+
}, 3e4);
|
|
299
|
+
const entry = {
|
|
300
|
+
res,
|
|
301
|
+
cleanup: () => {
|
|
302
|
+
clearInterval(pingTimer);
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
listeners.add(entry);
|
|
306
|
+
req.on("close", () => {
|
|
307
|
+
entry.cleanup();
|
|
308
|
+
listeners.delete(entry);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
return router;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// src/routes/accounts.ts
|
|
251
315
|
function sanitizeAgent(agent) {
|
|
252
316
|
if (!agent) return agent;
|
|
253
317
|
const { metadata, ...rest } = agent;
|
|
@@ -261,7 +325,7 @@ function sanitizeAgent(agent) {
|
|
|
261
325
|
return agent;
|
|
262
326
|
}
|
|
263
327
|
function createAccountRoutes(accountManager, db, config) {
|
|
264
|
-
const router =
|
|
328
|
+
const router = Router3();
|
|
265
329
|
const deletionService = new AgentDeletionService(db, accountManager, config);
|
|
266
330
|
router.post("/accounts", requireMaster, async (req, res, next) => {
|
|
267
331
|
if (!req.body || typeof req.body !== "object") {
|
|
@@ -306,6 +370,13 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
306
370
|
} catch {
|
|
307
371
|
}
|
|
308
372
|
}
|
|
373
|
+
try {
|
|
374
|
+
pushSystemEvent({
|
|
375
|
+
type: "account_created",
|
|
376
|
+
account: sanitizeAgent(agent)
|
|
377
|
+
});
|
|
378
|
+
} catch {
|
|
379
|
+
}
|
|
309
380
|
res.status(201).json(sanitizeAgent(agent));
|
|
310
381
|
} catch (err) {
|
|
311
382
|
const msg = err.message ?? "";
|
|
@@ -463,9 +534,14 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
463
534
|
const archive = req.query.archive !== "false";
|
|
464
535
|
const reason = req.query.reason || void 0;
|
|
465
536
|
const deletedBy = req.query.deletedBy || "api";
|
|
537
|
+
const deletingAgent = allAgents.find((a) => a.id === req.params.id);
|
|
466
538
|
if (archive) {
|
|
467
539
|
const report = await deletionService.archiveAndDelete(req.params.id, { deletedBy, reason });
|
|
468
540
|
const { emails: _emails, ...summary } = report;
|
|
541
|
+
try {
|
|
542
|
+
pushSystemEvent({ type: "account_deleted", accountId: req.params.id, name: deletingAgent?.name });
|
|
543
|
+
} catch {
|
|
544
|
+
}
|
|
469
545
|
res.json(summary);
|
|
470
546
|
} else {
|
|
471
547
|
const deleted = await accountManager.delete(req.params.id);
|
|
@@ -473,6 +549,10 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
473
549
|
res.status(404).json({ error: "Agent not found" });
|
|
474
550
|
return;
|
|
475
551
|
}
|
|
552
|
+
try {
|
|
553
|
+
pushSystemEvent({ type: "account_deleted", accountId: req.params.id, name: deletingAgent?.name });
|
|
554
|
+
} catch {
|
|
555
|
+
}
|
|
476
556
|
res.status(204).send();
|
|
477
557
|
}
|
|
478
558
|
} catch (err) {
|
|
@@ -488,7 +568,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
488
568
|
}
|
|
489
569
|
|
|
490
570
|
// src/routes/mail.ts
|
|
491
|
-
import { Router as
|
|
571
|
+
import { Router as Router6 } from "express";
|
|
492
572
|
import crypto from "crypto";
|
|
493
573
|
import {
|
|
494
574
|
MailSender as MailSender2,
|
|
@@ -501,7 +581,7 @@ import {
|
|
|
501
581
|
} from "@agenticmail/core";
|
|
502
582
|
|
|
503
583
|
// src/routes/events.ts
|
|
504
|
-
import { Router as
|
|
584
|
+
import { Router as Router5 } from "express";
|
|
505
585
|
import {
|
|
506
586
|
InboxWatcher,
|
|
507
587
|
MailReceiver,
|
|
@@ -513,7 +593,7 @@ import {
|
|
|
513
593
|
import { v4 as uuidv42 } from "uuid";
|
|
514
594
|
|
|
515
595
|
// src/routes/features.ts
|
|
516
|
-
import { Router as
|
|
596
|
+
import { Router as Router4 } from "express";
|
|
517
597
|
import { v4 as uuidv4 } from "uuid";
|
|
518
598
|
import {
|
|
519
599
|
MailSender
|
|
@@ -633,7 +713,7 @@ function parseScheduleTime(input) {
|
|
|
633
713
|
return isNaN(fallback.getTime()) ? null : fallback;
|
|
634
714
|
}
|
|
635
715
|
function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
636
|
-
const router =
|
|
716
|
+
const router = Router4();
|
|
637
717
|
router.get("/contacts", requireAgent, async (req, res, next) => {
|
|
638
718
|
try {
|
|
639
719
|
const rows = db.prepare("SELECT * FROM contacts WHERE agent_id = ? ORDER BY name, email").all(req.agent.id);
|
|
@@ -1210,7 +1290,7 @@ async function closeAllWatchers() {
|
|
|
1210
1290
|
activeWatchers.clear();
|
|
1211
1291
|
}
|
|
1212
1292
|
function createEventRoutes(accountManager, config, db) {
|
|
1213
|
-
const router =
|
|
1293
|
+
const router = Router5();
|
|
1214
1294
|
router.get("/events", requireAgent, async (req, res, next) => {
|
|
1215
1295
|
try {
|
|
1216
1296
|
const agent = req.agent;
|
|
@@ -1673,7 +1753,7 @@ function saveSentCopy(authUser, password, config, raw) {
|
|
|
1673
1753
|
})();
|
|
1674
1754
|
}
|
|
1675
1755
|
function createMailRoutes(accountManager, config, db, gatewayManager) {
|
|
1676
|
-
const router =
|
|
1756
|
+
const router = Router6();
|
|
1677
1757
|
router.post("/mail/send", requireAgent, async (req, res, next) => {
|
|
1678
1758
|
try {
|
|
1679
1759
|
if (!req.body || typeof req.body !== "object") {
|
|
@@ -2449,7 +2529,7 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
|
|
|
2449
2529
|
}
|
|
2450
2530
|
|
|
2451
2531
|
// src/routes/inbound.ts
|
|
2452
|
-
import { Router as
|
|
2532
|
+
import { Router as Router7 } from "express";
|
|
2453
2533
|
import { randomUUID } from "crypto";
|
|
2454
2534
|
import {
|
|
2455
2535
|
parseEmail as parseEmail3,
|
|
@@ -2463,7 +2543,7 @@ var INBOUND_SECRET = process.env.AGENTICMAIL_INBOUND_SECRET || (() => {
|
|
|
2463
2543
|
})();
|
|
2464
2544
|
var DEBUG = () => !!process.env.AGENTICMAIL_DEBUG;
|
|
2465
2545
|
function createInboundRoutes(accountManager, config, gatewayManager) {
|
|
2466
|
-
const router =
|
|
2546
|
+
const router = Router7();
|
|
2467
2547
|
router.post("/mail/inbound", async (req, res, next) => {
|
|
2468
2548
|
try {
|
|
2469
2549
|
const secret = req.headers["x-inbound-secret"];
|
|
@@ -2540,9 +2620,9 @@ function createInboundRoutes(accountManager, config, gatewayManager) {
|
|
|
2540
2620
|
}
|
|
2541
2621
|
|
|
2542
2622
|
// src/routes/domains.ts
|
|
2543
|
-
import { Router as
|
|
2623
|
+
import { Router as Router8 } from "express";
|
|
2544
2624
|
function createDomainRoutes(domainManager) {
|
|
2545
|
-
const router =
|
|
2625
|
+
const router = Router8();
|
|
2546
2626
|
router.post("/domains", requireMaster, async (req, res, next) => {
|
|
2547
2627
|
try {
|
|
2548
2628
|
const { domain } = req.body;
|
|
@@ -2596,13 +2676,13 @@ function createDomainRoutes(domainManager) {
|
|
|
2596
2676
|
}
|
|
2597
2677
|
|
|
2598
2678
|
// src/routes/gateway.ts
|
|
2599
|
-
import { Router as
|
|
2679
|
+
import { Router as Router9 } from "express";
|
|
2600
2680
|
import {
|
|
2601
2681
|
RELAY_PRESETS,
|
|
2602
2682
|
AGENT_ROLES as AGENT_ROLES2
|
|
2603
2683
|
} from "@agenticmail/core";
|
|
2604
2684
|
function createGatewayRoutes(gatewayManager) {
|
|
2605
|
-
const router =
|
|
2685
|
+
const router = Router9();
|
|
2606
2686
|
router.get("/gateway/setup-guide", requireMaster, async (_req, res) => {
|
|
2607
2687
|
res.json({
|
|
2608
2688
|
modes: [
|
|
@@ -2958,12 +3038,12 @@ function createGatewayRoutes(gatewayManager) {
|
|
|
2958
3038
|
}
|
|
2959
3039
|
|
|
2960
3040
|
// src/routes/tasks.ts
|
|
2961
|
-
import { Router as
|
|
3041
|
+
import { Router as Router10 } from "express";
|
|
2962
3042
|
import { v4 as uuidv43 } from "uuid";
|
|
2963
3043
|
import { MailSender as MailSender4 } from "@agenticmail/core";
|
|
2964
3044
|
var rpcResolvers = /* @__PURE__ */ new Map();
|
|
2965
3045
|
function createTaskRoutes(db, accountManager, config) {
|
|
2966
|
-
const router =
|
|
3046
|
+
const router = Router10();
|
|
2967
3047
|
router.post("/tasks/assign", requireAuth, async (req, res, next) => {
|
|
2968
3048
|
try {
|
|
2969
3049
|
const { assignee, taskType, payload, expiresInSeconds } = req.body || {};
|
|
@@ -3284,7 +3364,7 @@ function parseTask(row) {
|
|
|
3284
3364
|
}
|
|
3285
3365
|
|
|
3286
3366
|
// src/routes/sms.ts
|
|
3287
|
-
import { Router as
|
|
3367
|
+
import { Router as Router11 } from "express";
|
|
3288
3368
|
import {
|
|
3289
3369
|
SmsManager,
|
|
3290
3370
|
parseGoogleVoiceSms,
|
|
@@ -3293,7 +3373,7 @@ import {
|
|
|
3293
3373
|
isValidPhoneNumber
|
|
3294
3374
|
} from "@agenticmail/core";
|
|
3295
3375
|
function createSmsRoutes(db, accountManager, config, gatewayManager) {
|
|
3296
|
-
const router =
|
|
3376
|
+
const router = Router11();
|
|
3297
3377
|
const smsManager = new SmsManager(db);
|
|
3298
3378
|
function getAgent(req, res) {
|
|
3299
3379
|
const agent = req.agent;
|
|
@@ -3535,7 +3615,7 @@ function createSmsRoutes(db, accountManager, config, gatewayManager) {
|
|
|
3535
3615
|
}
|
|
3536
3616
|
|
|
3537
3617
|
// src/routes/storage.ts
|
|
3538
|
-
import { Router as
|
|
3618
|
+
import { Router as Router12 } from "express";
|
|
3539
3619
|
function mapColumnType(col, dialect) {
|
|
3540
3620
|
const typeMap = {
|
|
3541
3621
|
sqlite: { text: "TEXT", integer: "INTEGER", real: "REAL", boolean: "INTEGER", json: "JSON", blob: "BLOB", timestamp: "TEXT" },
|
|
@@ -3682,7 +3762,7 @@ function adaptBetterSqlite(raw) {
|
|
|
3682
3762
|
}
|
|
3683
3763
|
function createStorageRoutes(rawDb, accountManager, config, dialect = "sqlite") {
|
|
3684
3764
|
const db = adaptBetterSqlite(rawDb);
|
|
3685
|
-
const router =
|
|
3765
|
+
const router = Router12();
|
|
3686
3766
|
function getAgent(req, res) {
|
|
3687
3767
|
const agent = req.agent;
|
|
3688
3768
|
if (!agent) {
|
|
@@ -4595,6 +4675,7 @@ function createApp(configOverrides) {
|
|
|
4595
4675
|
app2.use("/api/agenticmail", createTaskRoutes(db, accountManager, config));
|
|
4596
4676
|
app2.use("/api/agenticmail", createSmsRoutes(db, accountManager, config, gatewayManager));
|
|
4597
4677
|
app2.use("/api/agenticmail", createStorageRoutes(db, accountManager, config));
|
|
4678
|
+
app2.use("/api/agenticmail", createSystemEventRoutes());
|
|
4598
4679
|
app2.use("/api/agenticmail", (_req, res) => {
|
|
4599
4680
|
res.status(404).json({ error: "Not found" });
|
|
4600
4681
|
});
|
|
@@ -4700,6 +4781,10 @@ async function shutdown() {
|
|
|
4700
4781
|
await closeAllWatchers();
|
|
4701
4782
|
} catch {
|
|
4702
4783
|
}
|
|
4784
|
+
try {
|
|
4785
|
+
closeAllSystemEventListeners();
|
|
4786
|
+
} catch {
|
|
4787
|
+
}
|
|
4703
4788
|
try {
|
|
4704
4789
|
await closeCaches();
|
|
4705
4790
|
} catch {
|