@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 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 20+, `@agenticmail/core`, Stalwart Mail Server running (via Docker)
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 | `3100` | Port for the API server |
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 | `3100` | Server port |
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 Router2 } from "express";
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 = Router2();
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 Router5 } from "express";
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 Router4 } from "express";
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 Router3 } from "express";
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 = Router3();
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 = Router4();
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 = Router5();
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 Router6 } from "express";
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 = Router6();
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 Router7 } from "express";
2623
+ import { Router as Router8 } from "express";
2544
2624
  function createDomainRoutes(domainManager) {
2545
- const router = Router7();
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 Router8 } from "express";
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 = Router8();
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 Router9 } from "express";
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 = Router9();
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 Router10 } from "express";
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 = Router10();
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 Router11 } from "express";
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 = Router11();
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/api",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "REST API server for AgenticMail — email and SMS endpoints for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",