@agenticmail/api 0.7.3 → 0.7.6
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 +5 -1
- package/dist/index.js +240 -52
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/agenticmail/agenticmail/main/docs/images/logo-200.png" alt="AgenticMail logo (pink bow)" width="180" />
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">@agenticmail/api</h1>
|
|
2
6
|
|
|
3
7
|
The API server for [AgenticMail](https://github.com/agenticmail/agenticmail) — the part that lets AI agents (and humans) talk to the email and SMS system over the network.
|
|
4
8
|
|
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ function safeEqual(a, b) {
|
|
|
35
35
|
const hb = createHash("sha256").update(b).digest();
|
|
36
36
|
return timingSafeEqual(ha, hb);
|
|
37
37
|
}
|
|
38
|
-
function createAuthMiddleware(masterKey,
|
|
38
|
+
function createAuthMiddleware(masterKey, accountManager2, db) {
|
|
39
39
|
return async (req, res, next) => {
|
|
40
40
|
const authHeader = req.headers.authorization;
|
|
41
41
|
if (!authHeader?.startsWith("Bearer ")) {
|
|
@@ -53,7 +53,7 @@ function createAuthMiddleware(masterKey, accountManager, db) {
|
|
|
53
53
|
return;
|
|
54
54
|
}
|
|
55
55
|
try {
|
|
56
|
-
const agent = await
|
|
56
|
+
const agent = await accountManager2.getByApiKey(token);
|
|
57
57
|
if (agent) {
|
|
58
58
|
req.agent = agent;
|
|
59
59
|
if (db) {
|
|
@@ -324,9 +324,9 @@ function sanitizeAgent(agent) {
|
|
|
324
324
|
}
|
|
325
325
|
return agent;
|
|
326
326
|
}
|
|
327
|
-
function createAccountRoutes(
|
|
327
|
+
function createAccountRoutes(accountManager2, db, config) {
|
|
328
328
|
const router = Router3();
|
|
329
|
-
const deletionService = new AgentDeletionService(db,
|
|
329
|
+
const deletionService = new AgentDeletionService(db, accountManager2, config);
|
|
330
330
|
router.post("/accounts", requireMaster, async (req, res, next) => {
|
|
331
331
|
if (!req.body || typeof req.body !== "object") {
|
|
332
332
|
res.status(400).json({ error: "Request body must be JSON" });
|
|
@@ -357,7 +357,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
357
357
|
const cleanMeta = metadata ? Object.fromEntries(
|
|
358
358
|
Object.entries(metadata).filter(([k]) => !k.startsWith("_"))
|
|
359
359
|
) : void 0;
|
|
360
|
-
const agent = await
|
|
360
|
+
const agent = await accountManager2.create({ name: accountName, domain, password: password || void 0, metadata: cleanMeta, role });
|
|
361
361
|
try {
|
|
362
362
|
db.prepare("UPDATE agents SET last_activity_at = datetime('now') WHERE id = ?").run(agent.id);
|
|
363
363
|
} catch {
|
|
@@ -389,7 +389,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
389
389
|
});
|
|
390
390
|
router.get("/accounts", requireMaster, async (_req, res, next) => {
|
|
391
391
|
try {
|
|
392
|
-
const agents = await
|
|
392
|
+
const agents = await accountManager2.list();
|
|
393
393
|
res.json({ agents: agents.map(sanitizeAgent) });
|
|
394
394
|
} catch (err) {
|
|
395
395
|
next(err);
|
|
@@ -397,7 +397,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
397
397
|
});
|
|
398
398
|
router.get("/accounts/directory", requireAuth, async (_req, res, next) => {
|
|
399
399
|
try {
|
|
400
|
-
const agents = await
|
|
400
|
+
const agents = await accountManager2.list();
|
|
401
401
|
const directory = agents.map((a) => ({ name: a.name, email: a.email, role: a.role }));
|
|
402
402
|
res.json({ agents: directory });
|
|
403
403
|
} catch (err) {
|
|
@@ -406,7 +406,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
406
406
|
});
|
|
407
407
|
router.get("/accounts/directory/:name", requireAuth, async (req, res, next) => {
|
|
408
408
|
try {
|
|
409
|
-
const agent = await
|
|
409
|
+
const agent = await accountManager2.getByName(req.params.name);
|
|
410
410
|
if (!agent) {
|
|
411
411
|
res.status(404).json({ error: "Agent not found" });
|
|
412
412
|
return;
|
|
@@ -471,7 +471,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
471
471
|
const deleted = [];
|
|
472
472
|
for (const row of rows) {
|
|
473
473
|
try {
|
|
474
|
-
await
|
|
474
|
+
await accountManager2.delete(row.id);
|
|
475
475
|
deleted.push(row.name);
|
|
476
476
|
} catch {
|
|
477
477
|
}
|
|
@@ -483,7 +483,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
483
483
|
});
|
|
484
484
|
router.get("/accounts/:id", requireMaster, async (req, res, next) => {
|
|
485
485
|
try {
|
|
486
|
-
const agent = await
|
|
486
|
+
const agent = await accountManager2.getById(req.params.id);
|
|
487
487
|
if (!agent) {
|
|
488
488
|
res.status(404).json({ error: "Agent not found" });
|
|
489
489
|
return;
|
|
@@ -501,7 +501,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
501
501
|
res.status(400).json({ error: "metadata must be an object" });
|
|
502
502
|
return;
|
|
503
503
|
}
|
|
504
|
-
const updated = await
|
|
504
|
+
const updated = await accountManager2.updateMetadata(agent.id, metadata);
|
|
505
505
|
if (!updated) {
|
|
506
506
|
res.status(404).json({ error: "Agent not found" });
|
|
507
507
|
return;
|
|
@@ -526,7 +526,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
526
526
|
});
|
|
527
527
|
router.delete("/accounts/:id", requireMaster, async (req, res, next) => {
|
|
528
528
|
try {
|
|
529
|
-
const allAgents = await
|
|
529
|
+
const allAgents = await accountManager2.list();
|
|
530
530
|
if (allAgents.length <= 1) {
|
|
531
531
|
res.status(400).json({ error: "Cannot delete the last agent. At least one agent must remain." });
|
|
532
532
|
return;
|
|
@@ -544,7 +544,7 @@ function createAccountRoutes(accountManager, db, config) {
|
|
|
544
544
|
}
|
|
545
545
|
res.json(summary);
|
|
546
546
|
} else {
|
|
547
|
-
const deleted = await
|
|
547
|
+
const deleted = await accountManager2.delete(req.params.id);
|
|
548
548
|
if (!deleted) {
|
|
549
549
|
res.status(404).json({ error: "Agent not found" });
|
|
550
550
|
return;
|
|
@@ -828,6 +828,8 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
828
828
|
return;
|
|
829
829
|
}
|
|
830
830
|
const agent = req.agent;
|
|
831
|
+
const wakeList = normalizeWakeList(req.body?.wake);
|
|
832
|
+
const customHeaders = wakeHeaders(wakeList);
|
|
831
833
|
const mailOpts = {
|
|
832
834
|
to: draft.to_addr,
|
|
833
835
|
subject: draft.subject || "(no subject)",
|
|
@@ -836,7 +838,8 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
836
838
|
cc: draft.cc || void 0,
|
|
837
839
|
bcc: draft.bcc || void 0,
|
|
838
840
|
inReplyTo: draft.in_reply_to || void 0,
|
|
839
|
-
references: draft.refs ? JSON.parse(draft.refs) : void 0
|
|
841
|
+
references: draft.refs ? JSON.parse(draft.refs) : void 0,
|
|
842
|
+
...Object.keys(customHeaders).length > 0 ? { headers: customHeaders } : {}
|
|
840
843
|
};
|
|
841
844
|
if (gatewayManager) {
|
|
842
845
|
const gatewayResult = await gatewayManager.routeOutbound(agent.name, mailOpts);
|
|
@@ -857,6 +860,19 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
857
860
|
try {
|
|
858
861
|
const result = await sender.send(mailOpts);
|
|
859
862
|
db.prepare("DELETE FROM drafts WHERE id = ?").run(draft.id);
|
|
863
|
+
notifyLocalRecipientsOfNewMail(
|
|
864
|
+
accountManager,
|
|
865
|
+
mailOpts.to,
|
|
866
|
+
mailOpts.cc,
|
|
867
|
+
mailOpts.bcc,
|
|
868
|
+
agent,
|
|
869
|
+
mailOpts.subject,
|
|
870
|
+
result.messageId,
|
|
871
|
+
config,
|
|
872
|
+
wakeList
|
|
873
|
+
).catch((err) => {
|
|
874
|
+
console.warn(`[drafts] SSE notify failed: ${err.message}`);
|
|
875
|
+
});
|
|
860
876
|
res.json(result);
|
|
861
877
|
} finally {
|
|
862
878
|
sender.close();
|
|
@@ -1078,20 +1094,24 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
1078
1094
|
res.status(404).json({ error: "Template not found" });
|
|
1079
1095
|
return;
|
|
1080
1096
|
}
|
|
1081
|
-
const { to, variables, cc, bcc } = req.body || {};
|
|
1097
|
+
const { to, variables, cc, bcc, wake } = req.body || {};
|
|
1082
1098
|
if (!to) {
|
|
1083
1099
|
res.status(400).json({ error: "to is required" });
|
|
1084
1100
|
return;
|
|
1085
1101
|
}
|
|
1086
1102
|
const applyVars = (text, vars2) => text.replace(/\{\{(\w+)\}\}/g, (m, key) => vars2[key] ?? m);
|
|
1103
|
+
const wakeList = normalizeWakeList(wake);
|
|
1104
|
+
const customHeaders = wakeHeaders(wakeList);
|
|
1087
1105
|
const vars = variables && typeof variables === "object" ? variables : {};
|
|
1106
|
+
const renderedSubject = applyVars(template.subject || "(no subject)", vars);
|
|
1088
1107
|
const mailOpts = {
|
|
1089
1108
|
to,
|
|
1090
|
-
subject:
|
|
1109
|
+
subject: renderedSubject,
|
|
1091
1110
|
text: template.text_body ? applyVars(template.text_body, vars) : void 0,
|
|
1092
1111
|
html: template.html_body ? applyVars(template.html_body, vars) : void 0,
|
|
1093
1112
|
cc: cc || void 0,
|
|
1094
|
-
bcc: bcc || void 0
|
|
1113
|
+
bcc: bcc || void 0,
|
|
1114
|
+
...Object.keys(customHeaders).length > 0 ? { headers: customHeaders } : {}
|
|
1095
1115
|
};
|
|
1096
1116
|
const agent = req.agent;
|
|
1097
1117
|
if (gatewayManager) {
|
|
@@ -1111,6 +1131,19 @@ function createFeatureRoutes(db, _accountManager, config, gatewayManager) {
|
|
|
1111
1131
|
});
|
|
1112
1132
|
try {
|
|
1113
1133
|
const result = await sender.send(mailOpts);
|
|
1134
|
+
notifyLocalRecipientsOfNewMail(
|
|
1135
|
+
accountManager,
|
|
1136
|
+
to,
|
|
1137
|
+
cc,
|
|
1138
|
+
bcc,
|
|
1139
|
+
agent,
|
|
1140
|
+
renderedSubject,
|
|
1141
|
+
result.messageId,
|
|
1142
|
+
config,
|
|
1143
|
+
wakeList
|
|
1144
|
+
).catch((err) => {
|
|
1145
|
+
console.warn(`[templates] SSE notify failed: ${err.message}`);
|
|
1146
|
+
});
|
|
1114
1147
|
res.json(result);
|
|
1115
1148
|
} finally {
|
|
1116
1149
|
sender.close();
|
|
@@ -1181,7 +1214,7 @@ function evaluateRules(db, agentId, email) {
|
|
|
1181
1214
|
}
|
|
1182
1215
|
return null;
|
|
1183
1216
|
}
|
|
1184
|
-
function startScheduledSender(db,
|
|
1217
|
+
function startScheduledSender(db, accountManager2, config, gatewayManager) {
|
|
1185
1218
|
return setInterval(async () => {
|
|
1186
1219
|
try {
|
|
1187
1220
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -1190,7 +1223,7 @@ function startScheduledSender(db, accountManager, config, gatewayManager) {
|
|
|
1190
1223
|
).all(now);
|
|
1191
1224
|
for (const row of pending) {
|
|
1192
1225
|
try {
|
|
1193
|
-
const agent = await
|
|
1226
|
+
const agent = await accountManager2.getById(row.agent_id);
|
|
1194
1227
|
if (!agent) {
|
|
1195
1228
|
db.prepare("UPDATE scheduled_emails SET status = 'failed', error = ? WHERE id = ?").run("Agent not found", row.id);
|
|
1196
1229
|
continue;
|
|
@@ -1289,7 +1322,7 @@ async function closeAllWatchers() {
|
|
|
1289
1322
|
}
|
|
1290
1323
|
activeWatchers.clear();
|
|
1291
1324
|
}
|
|
1292
|
-
function createEventRoutes(
|
|
1325
|
+
function createEventRoutes(accountManager2, config, db) {
|
|
1293
1326
|
const router = Router5();
|
|
1294
1327
|
router.get("/events", requireAgent, async (req, res, next) => {
|
|
1295
1328
|
try {
|
|
@@ -1677,7 +1710,18 @@ function normalizeMessageId(id) {
|
|
|
1677
1710
|
if (!id) return "";
|
|
1678
1711
|
return id.trim().replace(/^<+|>+$/g, "").toLowerCase();
|
|
1679
1712
|
}
|
|
1680
|
-
|
|
1713
|
+
function normalizeWakeList(value) {
|
|
1714
|
+
if (value === void 0 || value === null) return void 0;
|
|
1715
|
+
const strip = (s) => s.trim().replace(/@localhost$/i, "").toLowerCase();
|
|
1716
|
+
if (Array.isArray(value)) return value.map((v) => strip(String(v))).filter(Boolean);
|
|
1717
|
+
if (typeof value === "string") return value.split(",").map(strip).filter(Boolean);
|
|
1718
|
+
return void 0;
|
|
1719
|
+
}
|
|
1720
|
+
function wakeHeaders(wakeList) {
|
|
1721
|
+
if (wakeList === void 0) return {};
|
|
1722
|
+
return { "X-AgenticMail-Wake": wakeList.join(", ") };
|
|
1723
|
+
}
|
|
1724
|
+
async function notifyLocalRecipientsOfNewMail(accountManager2, toField, ccField, bccField, fromAgent, subject, messageId, config, wakeList) {
|
|
1681
1725
|
const collected = [];
|
|
1682
1726
|
const push = (v) => {
|
|
1683
1727
|
if (!v) return;
|
|
@@ -1707,7 +1751,7 @@ async function notifyLocalRecipientsOfNewMail(accountManager, toField, ccField,
|
|
|
1707
1751
|
if (addr === fromAgent.email.toLowerCase()) continue;
|
|
1708
1752
|
let recipient = null;
|
|
1709
1753
|
try {
|
|
1710
|
-
recipient = await
|
|
1754
|
+
recipient = await accountManager2.getByName(localPart);
|
|
1711
1755
|
} catch {
|
|
1712
1756
|
}
|
|
1713
1757
|
if (!recipient || notified.has(recipient.id)) continue;
|
|
@@ -1738,7 +1782,12 @@ async function notifyLocalRecipientsOfNewMail(accountManager, toField, ccField,
|
|
|
1738
1782
|
internal: true,
|
|
1739
1783
|
from: { name: fromAgent.name, address: fromAgent.email },
|
|
1740
1784
|
subject,
|
|
1741
|
-
messageId
|
|
1785
|
+
messageId,
|
|
1786
|
+
// Wake gating signal. Present iff the sender opted in. The
|
|
1787
|
+
// dispatcher reads this and spawns a Claude worker only for
|
|
1788
|
+
// recipients whose name is on the list (or for everyone if the
|
|
1789
|
+
// field is absent, preserving the v0.8.x default).
|
|
1790
|
+
...wakeList !== void 0 ? { wakeAllowlist: wakeList } : {}
|
|
1742
1791
|
});
|
|
1743
1792
|
}
|
|
1744
1793
|
}
|
|
@@ -1752,7 +1801,7 @@ function saveSentCopy(authUser, password, config, raw) {
|
|
|
1752
1801
|
}
|
|
1753
1802
|
})();
|
|
1754
1803
|
}
|
|
1755
|
-
function createMailRoutes(
|
|
1804
|
+
function createMailRoutes(accountManager2, config, db, gatewayManager) {
|
|
1756
1805
|
const router = Router6();
|
|
1757
1806
|
router.post("/mail/send", requireAgent, async (req, res, next) => {
|
|
1758
1807
|
try {
|
|
@@ -1761,7 +1810,7 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
|
|
|
1761
1810
|
return;
|
|
1762
1811
|
}
|
|
1763
1812
|
const agent = req.agent;
|
|
1764
|
-
const { to, subject, text, html, cc, bcc, replyTo, inReplyTo, references, attachments, allowSensitive } = req.body;
|
|
1813
|
+
const { to, subject, text, html, cc, bcc, replyTo, inReplyTo, references, attachments, allowSensitive, wake } = req.body;
|
|
1765
1814
|
if (!to || !subject) {
|
|
1766
1815
|
res.status(400).json({ error: "to and subject are required" });
|
|
1767
1816
|
return;
|
|
@@ -1789,7 +1838,21 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
|
|
|
1789
1838
|
const pendingId = crypto.randomUUID();
|
|
1790
1839
|
const ownerName2 = agent.metadata?.ownerName;
|
|
1791
1840
|
const fromName2 = ownerName2 ? `${agent.name} from ${ownerName2}` : agent.name;
|
|
1792
|
-
const
|
|
1841
|
+
const wakeListForPersist = normalizeWakeList(wake);
|
|
1842
|
+
const mailOptions = {
|
|
1843
|
+
to,
|
|
1844
|
+
subject,
|
|
1845
|
+
text,
|
|
1846
|
+
html,
|
|
1847
|
+
cc,
|
|
1848
|
+
bcc,
|
|
1849
|
+
replyTo,
|
|
1850
|
+
inReplyTo,
|
|
1851
|
+
references,
|
|
1852
|
+
attachments,
|
|
1853
|
+
fromName: fromName2,
|
|
1854
|
+
...wakeListForPersist !== void 0 ? { wakeList: wakeListForPersist } : {}
|
|
1855
|
+
};
|
|
1793
1856
|
db.prepare(
|
|
1794
1857
|
`INSERT INTO pending_outbound (id, agent_id, mail_options, warnings, summary) VALUES (?, ?, ?, ?, ?)`
|
|
1795
1858
|
).run(pendingId, agent.id, JSON.stringify(mailOptions), JSON.stringify(scanResult.warnings), scanResult.summary);
|
|
@@ -1858,7 +1921,22 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
|
|
|
1858
1921
|
}
|
|
1859
1922
|
const ownerName = agent.metadata?.ownerName;
|
|
1860
1923
|
const fromName = ownerName ? `${agent.name} from ${ownerName}` : agent.name;
|
|
1861
|
-
const
|
|
1924
|
+
const wakeList = normalizeWakeList(wake);
|
|
1925
|
+
const customHeaders = wakeHeaders(wakeList);
|
|
1926
|
+
const mailOpts = {
|
|
1927
|
+
to,
|
|
1928
|
+
subject,
|
|
1929
|
+
text,
|
|
1930
|
+
html,
|
|
1931
|
+
cc,
|
|
1932
|
+
bcc,
|
|
1933
|
+
replyTo,
|
|
1934
|
+
inReplyTo,
|
|
1935
|
+
references,
|
|
1936
|
+
attachments,
|
|
1937
|
+
fromName,
|
|
1938
|
+
...Object.keys(customHeaders).length > 0 ? { headers: customHeaders } : {}
|
|
1939
|
+
};
|
|
1862
1940
|
const password = getAgentPassword(agent);
|
|
1863
1941
|
if (gatewayManager) {
|
|
1864
1942
|
const gatewayResult = await gatewayManager.routeOutbound(agent.name, mailOpts);
|
|
@@ -1875,14 +1953,15 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
|
|
|
1875
1953
|
const result = await sender.send(mailOpts);
|
|
1876
1954
|
saveSentCopy(agent.stalwartPrincipal, password, config, result.raw);
|
|
1877
1955
|
notifyLocalRecipientsOfNewMail(
|
|
1878
|
-
|
|
1956
|
+
accountManager2,
|
|
1879
1957
|
to,
|
|
1880
1958
|
cc,
|
|
1881
1959
|
bcc,
|
|
1882
1960
|
agent,
|
|
1883
1961
|
subject,
|
|
1884
1962
|
result.messageId,
|
|
1885
|
-
config
|
|
1963
|
+
config,
|
|
1964
|
+
wakeList
|
|
1886
1965
|
).catch((err) => {
|
|
1887
1966
|
console.warn(`[mail] Internal SSE notify failed: ${err.message}`);
|
|
1888
1967
|
});
|
|
@@ -2454,7 +2533,7 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
|
|
|
2454
2533
|
res.status(400).json({ error: `Email already ${row.status}` });
|
|
2455
2534
|
return;
|
|
2456
2535
|
}
|
|
2457
|
-
const agent = await
|
|
2536
|
+
const agent = await accountManager2.getById(row.agent_id);
|
|
2458
2537
|
if (!agent) {
|
|
2459
2538
|
res.status(404).json({ error: "Agent account no longer exists" });
|
|
2460
2539
|
return;
|
|
@@ -2469,6 +2548,11 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
|
|
|
2469
2548
|
}
|
|
2470
2549
|
}
|
|
2471
2550
|
}
|
|
2551
|
+
const persistedWakeList = Array.isArray(mailOpts.wakeList) ? mailOpts.wakeList : void 0;
|
|
2552
|
+
if (persistedWakeList !== void 0) {
|
|
2553
|
+
mailOpts.headers = { ...mailOpts.headers ?? {}, ...wakeHeaders(persistedWakeList) };
|
|
2554
|
+
delete mailOpts.wakeList;
|
|
2555
|
+
}
|
|
2472
2556
|
const password = getAgentPassword(agent);
|
|
2473
2557
|
let response;
|
|
2474
2558
|
if (gatewayManager) {
|
|
@@ -2486,14 +2570,15 @@ function createMailRoutes(accountManager, config, db, gatewayManager) {
|
|
|
2486
2570
|
const result = await sender.send(mailOpts);
|
|
2487
2571
|
saveSentCopy(agent.stalwartPrincipal, password, config, result.raw);
|
|
2488
2572
|
notifyLocalRecipientsOfNewMail(
|
|
2489
|
-
|
|
2573
|
+
accountManager2,
|
|
2490
2574
|
mailOpts.to,
|
|
2491
2575
|
mailOpts.cc,
|
|
2492
2576
|
mailOpts.bcc,
|
|
2493
2577
|
agent,
|
|
2494
2578
|
mailOpts.subject,
|
|
2495
2579
|
result.messageId,
|
|
2496
|
-
config
|
|
2580
|
+
config,
|
|
2581
|
+
persistedWakeList
|
|
2497
2582
|
).catch((err) => {
|
|
2498
2583
|
console.warn(`[mail] Internal SSE notify (approve) failed: ${err.message}`);
|
|
2499
2584
|
});
|
|
@@ -2542,7 +2627,7 @@ var INBOUND_SECRET = process.env.AGENTICMAIL_INBOUND_SECRET || (() => {
|
|
|
2542
2627
|
return generated;
|
|
2543
2628
|
})();
|
|
2544
2629
|
var DEBUG = () => !!process.env.AGENTICMAIL_DEBUG;
|
|
2545
|
-
function createInboundRoutes(
|
|
2630
|
+
function createInboundRoutes(accountManager2, config, gatewayManager) {
|
|
2546
2631
|
const router = Router7();
|
|
2547
2632
|
router.post("/mail/inbound", async (req, res, next) => {
|
|
2548
2633
|
try {
|
|
@@ -2558,7 +2643,7 @@ function createInboundRoutes(accountManager, config, gatewayManager) {
|
|
|
2558
2643
|
}
|
|
2559
2644
|
const recipientEmail = typeof to === "string" ? to : to[0];
|
|
2560
2645
|
const localPart = recipientEmail.split("@")[0];
|
|
2561
|
-
const agent = await
|
|
2646
|
+
const agent = await accountManager2.getByName(localPart);
|
|
2562
2647
|
if (!agent) {
|
|
2563
2648
|
console.warn(`[Inbound] No agent found for "${localPart}" (${recipientEmail})`);
|
|
2564
2649
|
res.status(404).json({ error: `No agent found for ${recipientEmail}` });
|
|
@@ -3042,7 +3127,7 @@ import { Router as Router10 } from "express";
|
|
|
3042
3127
|
import { v4 as uuidv43 } from "uuid";
|
|
3043
3128
|
import { MailSender as MailSender4 } from "@agenticmail/core";
|
|
3044
3129
|
var rpcResolvers = /* @__PURE__ */ new Map();
|
|
3045
|
-
function createTaskRoutes(db,
|
|
3130
|
+
function createTaskRoutes(db, accountManager2, config) {
|
|
3046
3131
|
const router = Router10();
|
|
3047
3132
|
router.post("/tasks/assign", requireAuth, async (req, res, next) => {
|
|
3048
3133
|
try {
|
|
@@ -3051,7 +3136,7 @@ function createTaskRoutes(db, accountManager, config) {
|
|
|
3051
3136
|
res.status(400).json({ error: "assignee (agent name) is required" });
|
|
3052
3137
|
return;
|
|
3053
3138
|
}
|
|
3054
|
-
const target = await
|
|
3139
|
+
const target = await accountManager2.getByName(assignee);
|
|
3055
3140
|
if (!target) {
|
|
3056
3141
|
res.status(404).json({ error: `Agent "${assignee}" not found` });
|
|
3057
3142
|
return;
|
|
@@ -3110,7 +3195,7 @@ Please check your pending tasks.`
|
|
|
3110
3195
|
let assigneeId = req.agent.id;
|
|
3111
3196
|
const assigneeName = req.query.assignee;
|
|
3112
3197
|
if (assigneeName) {
|
|
3113
|
-
const target = await
|
|
3198
|
+
const target = await accountManager2.getByName(assigneeName);
|
|
3114
3199
|
if (target) assigneeId = target.id;
|
|
3115
3200
|
}
|
|
3116
3201
|
const rows = db.prepare(
|
|
@@ -3238,7 +3323,7 @@ Please check your pending tasks.`
|
|
|
3238
3323
|
res.status(400).json({ error: "target (agent name) and task are required" });
|
|
3239
3324
|
return;
|
|
3240
3325
|
}
|
|
3241
|
-
const targetAgent = await
|
|
3326
|
+
const targetAgent = await accountManager2.getByName(target);
|
|
3242
3327
|
if (!targetAgent) {
|
|
3243
3328
|
res.status(404).json({ error: `Agent "${target}" not found` });
|
|
3244
3329
|
return;
|
|
@@ -3372,7 +3457,7 @@ import {
|
|
|
3372
3457
|
normalizePhoneNumber,
|
|
3373
3458
|
isValidPhoneNumber
|
|
3374
3459
|
} from "@agenticmail/core";
|
|
3375
|
-
function createSmsRoutes(db,
|
|
3460
|
+
function createSmsRoutes(db, accountManager2, config, gatewayManager) {
|
|
3376
3461
|
const router = Router11();
|
|
3377
3462
|
const smsManager = new SmsManager(db);
|
|
3378
3463
|
function getAgent(req, res) {
|
|
@@ -3760,7 +3845,7 @@ function adaptBetterSqlite(raw) {
|
|
|
3760
3845
|
}
|
|
3761
3846
|
};
|
|
3762
3847
|
}
|
|
3763
|
-
function createStorageRoutes(rawDb,
|
|
3848
|
+
function createStorageRoutes(rawDb, accountManager2, config, dialect = "sqlite") {
|
|
3764
3849
|
const db = adaptBetterSqlite(rawDb);
|
|
3765
3850
|
const router = Router12();
|
|
3766
3851
|
function getAgent(req, res) {
|
|
@@ -4590,6 +4675,108 @@ function createStorageRoutes(rawDb, accountManager, config, dialect = "sqlite")
|
|
|
4590
4675
|
return router;
|
|
4591
4676
|
}
|
|
4592
4677
|
|
|
4678
|
+
// src/routes/dispatcher-activity.ts
|
|
4679
|
+
import { Router as Router13 } from "express";
|
|
4680
|
+
var ACTIVE_TTL_MS = 30 * 60 * 1e3;
|
|
4681
|
+
var RECENT_TTL_MS = 2 * 60 * 1e3;
|
|
4682
|
+
var HARD_CAP = 256;
|
|
4683
|
+
var active = /* @__PURE__ */ new Map();
|
|
4684
|
+
var recent = /* @__PURE__ */ new Map();
|
|
4685
|
+
function prune(nowMs) {
|
|
4686
|
+
for (const [id, w] of active) {
|
|
4687
|
+
if (nowMs - w.startedAtMs > ACTIVE_TTL_MS) active.delete(id);
|
|
4688
|
+
}
|
|
4689
|
+
for (const [id, w] of recent) {
|
|
4690
|
+
const t = w.endedAtMs ?? w.startedAtMs;
|
|
4691
|
+
if (nowMs - t > RECENT_TTL_MS) recent.delete(id);
|
|
4692
|
+
}
|
|
4693
|
+
while (active.size > HARD_CAP) {
|
|
4694
|
+
const first = active.keys().next().value;
|
|
4695
|
+
if (!first) break;
|
|
4696
|
+
active.delete(first);
|
|
4697
|
+
}
|
|
4698
|
+
while (recent.size > HARD_CAP) {
|
|
4699
|
+
const first = recent.keys().next().value;
|
|
4700
|
+
if (!first) break;
|
|
4701
|
+
recent.delete(first);
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
function createDispatcherActivityRoutes() {
|
|
4705
|
+
const router = Router13();
|
|
4706
|
+
router.post("/dispatcher/worker-started", requireMaster, (req, res) => {
|
|
4707
|
+
const body = req.body ?? {};
|
|
4708
|
+
if (typeof body.workerId !== "string" || typeof body.agentName !== "string") {
|
|
4709
|
+
res.status(400).json({ error: "workerId and agentName are required" });
|
|
4710
|
+
return;
|
|
4711
|
+
}
|
|
4712
|
+
const info = {
|
|
4713
|
+
workerId: body.workerId,
|
|
4714
|
+
agentName: body.agentName,
|
|
4715
|
+
agentEmail: typeof body.agentEmail === "string" ? body.agentEmail : void 0,
|
|
4716
|
+
kind: typeof body.kind === "string" ? body.kind : "unknown",
|
|
4717
|
+
trigger: body.trigger && typeof body.trigger === "object" ? body.trigger : void 0,
|
|
4718
|
+
startedAtMs: Date.now()
|
|
4719
|
+
};
|
|
4720
|
+
prune(info.startedAtMs);
|
|
4721
|
+
active.set(info.workerId, info);
|
|
4722
|
+
try {
|
|
4723
|
+
pushSystemEvent({
|
|
4724
|
+
type: "worker_started",
|
|
4725
|
+
worker: { ...info }
|
|
4726
|
+
});
|
|
4727
|
+
} catch {
|
|
4728
|
+
}
|
|
4729
|
+
res.status(201).json({ ok: true });
|
|
4730
|
+
});
|
|
4731
|
+
router.post("/dispatcher/worker-finished", requireMaster, (req, res) => {
|
|
4732
|
+
const body = req.body ?? {};
|
|
4733
|
+
if (typeof body.workerId !== "string") {
|
|
4734
|
+
res.status(400).json({ error: "workerId is required" });
|
|
4735
|
+
return;
|
|
4736
|
+
}
|
|
4737
|
+
const existing = active.get(body.workerId);
|
|
4738
|
+
const nowMs = Date.now();
|
|
4739
|
+
const info = {
|
|
4740
|
+
...existing ?? {
|
|
4741
|
+
workerId: body.workerId,
|
|
4742
|
+
agentName: typeof body.agentName === "string" ? body.agentName : "unknown",
|
|
4743
|
+
kind: "unknown",
|
|
4744
|
+
startedAtMs: nowMs
|
|
4745
|
+
},
|
|
4746
|
+
endedAtMs: nowMs,
|
|
4747
|
+
ok: body.ok === false ? false : true,
|
|
4748
|
+
resultPreview: typeof body.resultPreview === "string" ? body.resultPreview.slice(0, 240) : void 0
|
|
4749
|
+
};
|
|
4750
|
+
active.delete(body.workerId);
|
|
4751
|
+
recent.set(body.workerId, info);
|
|
4752
|
+
prune(nowMs);
|
|
4753
|
+
try {
|
|
4754
|
+
pushSystemEvent({
|
|
4755
|
+
type: "worker_finished",
|
|
4756
|
+
worker: { ...info }
|
|
4757
|
+
});
|
|
4758
|
+
} catch {
|
|
4759
|
+
}
|
|
4760
|
+
res.json({ ok: true });
|
|
4761
|
+
});
|
|
4762
|
+
router.get("/dispatcher/activity", requireMaster, (_req, res) => {
|
|
4763
|
+
const nowMs = Date.now();
|
|
4764
|
+
prune(nowMs);
|
|
4765
|
+
res.json({
|
|
4766
|
+
now: nowMs,
|
|
4767
|
+
active: Array.from(active.values()).map((w) => ({
|
|
4768
|
+
...w,
|
|
4769
|
+
durationMs: nowMs - w.startedAtMs
|
|
4770
|
+
})),
|
|
4771
|
+
recent: Array.from(recent.values()).map((w) => ({
|
|
4772
|
+
...w,
|
|
4773
|
+
durationMs: (w.endedAtMs ?? nowMs) - w.startedAtMs
|
|
4774
|
+
}))
|
|
4775
|
+
});
|
|
4776
|
+
});
|
|
4777
|
+
return router;
|
|
4778
|
+
}
|
|
4779
|
+
|
|
4593
4780
|
// src/app.ts
|
|
4594
4781
|
var integrationRouteFactoryPromise = (async () => {
|
|
4595
4782
|
try {
|
|
@@ -4628,12 +4815,12 @@ function createApp(configOverrides) {
|
|
|
4628
4815
|
adminUser: config.stalwart.adminUser,
|
|
4629
4816
|
adminPassword: config.stalwart.adminPassword
|
|
4630
4817
|
});
|
|
4631
|
-
const
|
|
4818
|
+
const accountManager2 = new AccountManager(db, stalwart);
|
|
4632
4819
|
const domainManager = new DomainManager(db, stalwart);
|
|
4633
4820
|
const gatewayManager = new GatewayManager2({
|
|
4634
4821
|
db,
|
|
4635
4822
|
stalwart,
|
|
4636
|
-
accountManager,
|
|
4823
|
+
accountManager: accountManager2,
|
|
4637
4824
|
localSmtp: {
|
|
4638
4825
|
host: config.smtp.host,
|
|
4639
4826
|
port: config.smtp.port,
|
|
@@ -4660,27 +4847,28 @@ function createApp(configOverrides) {
|
|
|
4660
4847
|
})
|
|
4661
4848
|
);
|
|
4662
4849
|
app2.use("/api/agenticmail", createHealthRoutes(stalwart));
|
|
4663
|
-
app2.use("/api/agenticmail", createInboundRoutes(
|
|
4850
|
+
app2.use("/api/agenticmail", createInboundRoutes(accountManager2, config, gatewayManager));
|
|
4664
4851
|
const integrationFactory = readResolvedFactory(integrationRouteFactoryPromise);
|
|
4665
4852
|
if (integrationFactory) {
|
|
4666
4853
|
app2.use("/api/agenticmail", integrationFactory());
|
|
4667
4854
|
}
|
|
4668
|
-
app2.use("/api/agenticmail", createAuthMiddleware(config.masterKey,
|
|
4669
|
-
app2.use("/api/agenticmail", createAccountRoutes(
|
|
4670
|
-
app2.use("/api/agenticmail", createMailRoutes(
|
|
4671
|
-
app2.use("/api/agenticmail", createEventRoutes(
|
|
4855
|
+
app2.use("/api/agenticmail", createAuthMiddleware(config.masterKey, accountManager2, db));
|
|
4856
|
+
app2.use("/api/agenticmail", createAccountRoutes(accountManager2, db, config));
|
|
4857
|
+
app2.use("/api/agenticmail", createMailRoutes(accountManager2, config, db, gatewayManager));
|
|
4858
|
+
app2.use("/api/agenticmail", createEventRoutes(accountManager2, config, db));
|
|
4672
4859
|
app2.use("/api/agenticmail", createDomainRoutes(domainManager));
|
|
4673
4860
|
app2.use("/api/agenticmail", createGatewayRoutes(gatewayManager));
|
|
4674
|
-
app2.use("/api/agenticmail", createFeatureRoutes(db,
|
|
4675
|
-
app2.use("/api/agenticmail", createTaskRoutes(db,
|
|
4676
|
-
app2.use("/api/agenticmail", createSmsRoutes(db,
|
|
4677
|
-
app2.use("/api/agenticmail", createStorageRoutes(db,
|
|
4861
|
+
app2.use("/api/agenticmail", createFeatureRoutes(db, accountManager2, config, gatewayManager));
|
|
4862
|
+
app2.use("/api/agenticmail", createTaskRoutes(db, accountManager2, config));
|
|
4863
|
+
app2.use("/api/agenticmail", createSmsRoutes(db, accountManager2, config, gatewayManager));
|
|
4864
|
+
app2.use("/api/agenticmail", createStorageRoutes(db, accountManager2, config));
|
|
4678
4865
|
app2.use("/api/agenticmail", createSystemEventRoutes());
|
|
4866
|
+
app2.use("/api/agenticmail", createDispatcherActivityRoutes());
|
|
4679
4867
|
app2.use("/api/agenticmail", (_req, res) => {
|
|
4680
4868
|
res.status(404).json({ error: "Not found" });
|
|
4681
4869
|
});
|
|
4682
4870
|
app2.use(errorHandler);
|
|
4683
|
-
const context2 = { config, db, stalwart, accountManager, domainManager, gatewayManager };
|
|
4871
|
+
const context2 = { config, db, stalwart, accountManager: accountManager2, domainManager, gatewayManager };
|
|
4684
4872
|
return { app: app2, context: context2 };
|
|
4685
4873
|
}
|
|
4686
4874
|
|