@agenticmail/core 0.9.6 → 0.9.8
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 +12 -0
- package/dist/index.cjs +318 -222
- package/dist/index.d.cts +75 -1
- package/dist/index.d.ts +75 -1
- package/dist/index.js +305 -212
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -191,6 +191,14 @@ var MailReceiver = class {
|
|
|
191
191
|
name: a.name,
|
|
192
192
|
address: a.address ?? ""
|
|
193
193
|
})),
|
|
194
|
+
cc: (env.cc ?? []).map((a) => ({
|
|
195
|
+
name: a.name,
|
|
196
|
+
address: a.address ?? ""
|
|
197
|
+
})),
|
|
198
|
+
bcc: (env.bcc ?? []).map((a) => ({
|
|
199
|
+
name: a.name,
|
|
200
|
+
address: a.address ?? ""
|
|
201
|
+
})),
|
|
194
202
|
date: env.date ?? /* @__PURE__ */ new Date(),
|
|
195
203
|
flags: msg.flags ?? /* @__PURE__ */ new Set(),
|
|
196
204
|
size: msg.size ?? 0
|
|
@@ -390,8 +398,8 @@ var MailReceiver = class {
|
|
|
390
398
|
}));
|
|
391
399
|
}
|
|
392
400
|
/** Create a new IMAP folder */
|
|
393
|
-
async createFolder(
|
|
394
|
-
await this.client.mailboxCreate(
|
|
401
|
+
async createFolder(path2) {
|
|
402
|
+
await this.client.mailboxCreate(path2);
|
|
395
403
|
}
|
|
396
404
|
/** Batch mark multiple messages as seen */
|
|
397
405
|
async batchMarkSeen(uids, mailbox = "INBOX") {
|
|
@@ -931,8 +939,8 @@ var StalwartAdmin = class {
|
|
|
931
939
|
}
|
|
932
940
|
baseUrl;
|
|
933
941
|
authHeader;
|
|
934
|
-
async request(method,
|
|
935
|
-
const url = `${this.baseUrl}/api${
|
|
942
|
+
async request(method, path2, body) {
|
|
943
|
+
const url = `${this.baseUrl}/api${path2}`;
|
|
936
944
|
const response = await fetch(url, {
|
|
937
945
|
method,
|
|
938
946
|
headers: {
|
|
@@ -1093,14 +1101,14 @@ var StalwartAdmin = class {
|
|
|
1093
1101
|
if (!isValidDomain(domain)) {
|
|
1094
1102
|
throw new Error(`Invalid domain format: "${domain}"`);
|
|
1095
1103
|
}
|
|
1096
|
-
const { readFileSync:
|
|
1097
|
-
const { homedir:
|
|
1098
|
-
const { join:
|
|
1099
|
-
const configPath =
|
|
1104
|
+
const { readFileSync: readFileSync9, writeFileSync: writeFileSync10 } = await import("fs");
|
|
1105
|
+
const { homedir: homedir13 } = await import("os");
|
|
1106
|
+
const { join: join15 } = await import("path");
|
|
1107
|
+
const configPath = join15(homedir13(), ".agenticmail", "stalwart.toml");
|
|
1100
1108
|
try {
|
|
1101
|
-
let config =
|
|
1109
|
+
let config = readFileSync9(configPath, "utf-8");
|
|
1102
1110
|
config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
|
|
1103
|
-
|
|
1111
|
+
writeFileSync10(configPath, config);
|
|
1104
1112
|
console.log(`[Stalwart] Updated hostname to "${domain}" in stalwart.toml`);
|
|
1105
1113
|
} catch (err) {
|
|
1106
1114
|
throw new Error(`Failed to set config server.hostname=${domain}`);
|
|
@@ -1109,15 +1117,15 @@ var StalwartAdmin = class {
|
|
|
1109
1117
|
// --- DKIM ---
|
|
1110
1118
|
/** Path to the host-side stalwart.toml (mounted read-only into container) */
|
|
1111
1119
|
get configPath() {
|
|
1112
|
-
const { homedir:
|
|
1113
|
-
const { join:
|
|
1114
|
-
return
|
|
1120
|
+
const { homedir: homedir13 } = __require("os");
|
|
1121
|
+
const { join: join15 } = __require("path");
|
|
1122
|
+
return join15(homedir13(), ".agenticmail", "stalwart.toml");
|
|
1115
1123
|
}
|
|
1116
1124
|
/** Path to host-side DKIM key directory */
|
|
1117
1125
|
get dkimDir() {
|
|
1118
|
-
const { homedir:
|
|
1119
|
-
const { join:
|
|
1120
|
-
return
|
|
1126
|
+
const { homedir: homedir13 } = __require("os");
|
|
1127
|
+
const { join: join15 } = __require("path");
|
|
1128
|
+
return join15(homedir13(), ".agenticmail");
|
|
1121
1129
|
}
|
|
1122
1130
|
/**
|
|
1123
1131
|
* Create/reuse a DKIM signing key for a domain.
|
|
@@ -1218,12 +1226,12 @@ var StalwartAdmin = class {
|
|
|
1218
1226
|
* This bypasses the need for a PTR record on the sending IP.
|
|
1219
1227
|
*/
|
|
1220
1228
|
async configureOutboundRelay(config) {
|
|
1221
|
-
const { readFileSync:
|
|
1222
|
-
const { homedir:
|
|
1223
|
-
const { join:
|
|
1229
|
+
const { readFileSync: readFileSync9, writeFileSync: writeFileSync10 } = await import("fs");
|
|
1230
|
+
const { homedir: homedir13 } = await import("os");
|
|
1231
|
+
const { join: join15 } = await import("path");
|
|
1224
1232
|
const routeName = config.routeName ?? "gmail";
|
|
1225
|
-
const tomlPath =
|
|
1226
|
-
let toml =
|
|
1233
|
+
const tomlPath = join15(homedir13(), ".agenticmail", "stalwart.toml");
|
|
1234
|
+
let toml = readFileSync9(tomlPath, "utf-8");
|
|
1227
1235
|
toml = toml.replace(/\n\[queue\.route\.gmail\][\s\S]*?(?=\n\[|$)/, "");
|
|
1228
1236
|
toml = toml.replace(/\n\[queue\.strategy\][\s\S]*?(?=\n\[|$)/, "");
|
|
1229
1237
|
const safeRouteName = routeName.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
@@ -1243,7 +1251,7 @@ auth.secret = "${escapeTomlString(config.password)}"
|
|
|
1243
1251
|
route = [ { if = "is_local_domain('', rcpt_domain)", then = "'local'" },
|
|
1244
1252
|
{ else = "'${safeRouteName}'" } ]
|
|
1245
1253
|
`;
|
|
1246
|
-
|
|
1254
|
+
writeFileSync10(tomlPath, toml, "utf-8");
|
|
1247
1255
|
await this.restartContainer();
|
|
1248
1256
|
}
|
|
1249
1257
|
};
|
|
@@ -1626,11 +1634,11 @@ var AgentDeletionService = class {
|
|
|
1626
1634
|
};
|
|
1627
1635
|
}
|
|
1628
1636
|
saveToFile(report) {
|
|
1629
|
-
const
|
|
1630
|
-
mkdirSync2(
|
|
1637
|
+
const dir2 = join2(homedir2(), ".agenticmail", "deletions");
|
|
1638
|
+
mkdirSync2(dir2, { recursive: true });
|
|
1631
1639
|
const timestamp = report.deletedAt.replace(/[:.]/g, "-");
|
|
1632
1640
|
const filename = `${report.agent.name}_${timestamp}.json`;
|
|
1633
|
-
const filePath = join2(
|
|
1641
|
+
const filePath = join2(dir2, filename);
|
|
1634
1642
|
writeFileSync2(filePath, JSON.stringify(report, null, 2), "utf-8");
|
|
1635
1643
|
return filePath;
|
|
1636
1644
|
}
|
|
@@ -3917,8 +3925,8 @@ var CloudflareClient = class {
|
|
|
3917
3925
|
return resp.result;
|
|
3918
3926
|
}
|
|
3919
3927
|
// --- Internal ---
|
|
3920
|
-
async request(method,
|
|
3921
|
-
const url = `${CF_API_BASE}${
|
|
3928
|
+
async request(method, path2, body) {
|
|
3929
|
+
const url = `${CF_API_BASE}${path2}`;
|
|
3922
3930
|
const response = await fetch(url, {
|
|
3923
3931
|
method,
|
|
3924
3932
|
headers: {
|
|
@@ -4240,9 +4248,9 @@ var TunnelManager = class {
|
|
|
4240
4248
|
throw new Error(`Failed to download cloudflared: ${response.statusText}`);
|
|
4241
4249
|
}
|
|
4242
4250
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
4243
|
-
const { writeFile:
|
|
4251
|
+
const { writeFile: writeFile4, rename: rename2 } = await import("fs/promises");
|
|
4244
4252
|
const tmpPath = this.binPath + ".tmp";
|
|
4245
|
-
await
|
|
4253
|
+
await writeFile4(tmpPath, buffer);
|
|
4246
4254
|
await chmod(tmpPath, 493);
|
|
4247
4255
|
await rename2(tmpPath, this.binPath);
|
|
4248
4256
|
return this.binPath;
|
|
@@ -4534,6 +4542,33 @@ var SmsManager = class {
|
|
|
4534
4542
|
meta.sms = config;
|
|
4535
4543
|
this.db.prepare("UPDATE agents SET metadata = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(meta), agentId);
|
|
4536
4544
|
}
|
|
4545
|
+
/**
|
|
4546
|
+
* Resolve the operator's "where do I get pinged" address from an
|
|
4547
|
+
* agent's SMS config. Used by the dispatcher's bridge-escalation
|
|
4548
|
+
* path: when sub-agents mail a bridge with no fresh host session
|
|
4549
|
+
* available, we email the operator a digest at this address. Their
|
|
4550
|
+
* phone's Gmail push notification surfaces it within seconds —
|
|
4551
|
+
* effectively a free, programmatic alert channel.
|
|
4552
|
+
*
|
|
4553
|
+
* Returns the configured `forwardingEmail` (the same Gmail Google
|
|
4554
|
+
* Voice forwards inbound SMS to, which the operator already has
|
|
4555
|
+
* push notifications enabled for) when SMS is configured AND
|
|
4556
|
+
* enabled. Returns null otherwise — caller falls through to a
|
|
4557
|
+
* silent log + system event.
|
|
4558
|
+
*
|
|
4559
|
+
* Why we don't try real-SMS delivery yet: Google Voice's
|
|
4560
|
+
* `<number>@txt.voice.google.com` email-to-SMS gateway was
|
|
4561
|
+
* deprecated by Google years ago. A future `carrier` field on
|
|
4562
|
+
* SmsConfig (Verizon vtext.com / AT&T txt.att.net / etc) will let
|
|
4563
|
+
* the operator opt into actual SMS, but that's a follow-up — the
|
|
4564
|
+
* email path already gets the operator a phone notification.
|
|
4565
|
+
*/
|
|
4566
|
+
getAlertEmail(agentId) {
|
|
4567
|
+
const cfg = this.getSmsConfig(agentId);
|
|
4568
|
+
if (!cfg || !cfg.enabled) return null;
|
|
4569
|
+
if (typeof cfg.forwardingEmail !== "string" || !cfg.forwardingEmail.includes("@")) return null;
|
|
4570
|
+
return cfg.forwardingEmail;
|
|
4571
|
+
}
|
|
4537
4572
|
/** Remove SMS config from agent metadata */
|
|
4538
4573
|
removeSmsConfig(agentId) {
|
|
4539
4574
|
const row = this.db.prepare("SELECT metadata FROM agents WHERE id = ?").get(agentId);
|
|
@@ -5149,12 +5184,12 @@ var GatewayManager = class {
|
|
|
5149
5184
|
zone = await this.cfClient.createZone(domain);
|
|
5150
5185
|
}
|
|
5151
5186
|
const existingRecords = await this.cfClient.listDnsRecords(zone.id);
|
|
5152
|
-
const { homedir:
|
|
5153
|
-
const backupDir = join4(
|
|
5187
|
+
const { homedir: homedir13 } = await import("os");
|
|
5188
|
+
const backupDir = join4(homedir13(), ".agenticmail");
|
|
5154
5189
|
const backupPath = join4(backupDir, `dns-backup-${domain}-${Date.now()}.json`);
|
|
5155
|
-
const { writeFileSync:
|
|
5156
|
-
|
|
5157
|
-
|
|
5190
|
+
const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync11 } = await import("fs");
|
|
5191
|
+
mkdirSync11(backupDir, { recursive: true });
|
|
5192
|
+
writeFileSync10(backupPath, JSON.stringify({
|
|
5158
5193
|
domain,
|
|
5159
5194
|
zoneId: zone.id,
|
|
5160
5195
|
backedUpAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5915,8 +5950,8 @@ function isDisabled() {
|
|
|
5915
5950
|
function getInstallId() {
|
|
5916
5951
|
if (installId) return installId;
|
|
5917
5952
|
try {
|
|
5918
|
-
const
|
|
5919
|
-
const idFile = join5(
|
|
5953
|
+
const dir2 = join5(homedir4(), ".agenticmail");
|
|
5954
|
+
const idFile = join5(dir2, ".telemetry-id");
|
|
5920
5955
|
if (existsSync3(idFile)) {
|
|
5921
5956
|
const id = readFileSync2(idFile, "utf8").trim();
|
|
5922
5957
|
if (id && id.length > 10) {
|
|
@@ -5925,7 +5960,7 @@ function getInstallId() {
|
|
|
5925
5960
|
}
|
|
5926
5961
|
}
|
|
5927
5962
|
installId = randomUUID();
|
|
5928
|
-
if (!existsSync3(
|
|
5963
|
+
if (!existsSync3(dir2)) mkdirSync3(dir2, { recursive: true });
|
|
5929
5964
|
writeFileSync3(idFile, installId, "utf8");
|
|
5930
5965
|
return installId;
|
|
5931
5966
|
} catch {
|
|
@@ -6096,22 +6131,77 @@ function redactObject(input, _depth = 0) {
|
|
|
6096
6131
|
return out;
|
|
6097
6132
|
}
|
|
6098
6133
|
|
|
6099
|
-
// src/
|
|
6134
|
+
// src/operator-prefs.ts
|
|
6100
6135
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
6101
|
-
import { join as join7 } from "path";
|
|
6102
6136
|
import { homedir as homedir5 } from "os";
|
|
6103
|
-
|
|
6137
|
+
import { join as join7 } from "path";
|
|
6138
|
+
function dir() {
|
|
6104
6139
|
return join7(homedir5(), ".agenticmail");
|
|
6105
6140
|
}
|
|
6141
|
+
function path() {
|
|
6142
|
+
return join7(dir(), "operator-prefs.json");
|
|
6143
|
+
}
|
|
6144
|
+
function readFile() {
|
|
6145
|
+
if (!existsSync4(path())) return { version: 1 };
|
|
6146
|
+
try {
|
|
6147
|
+
const raw = readFileSync3(path(), "utf-8");
|
|
6148
|
+
if (!raw.trim()) return { version: 1 };
|
|
6149
|
+
const parsed = JSON.parse(raw);
|
|
6150
|
+
return { version: 1, operatorEmail: typeof parsed.operatorEmail === "string" ? parsed.operatorEmail : void 0 };
|
|
6151
|
+
} catch {
|
|
6152
|
+
return { version: 1 };
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
function writeFile(shape) {
|
|
6156
|
+
const d = dir();
|
|
6157
|
+
const p = path();
|
|
6158
|
+
if (!existsSync4(d)) mkdirSync4(d, { recursive: true });
|
|
6159
|
+
const tmp = `${p}.agenticmail-tmp-${process.pid}`;
|
|
6160
|
+
writeFileSync4(tmp, JSON.stringify(shape, null, 2), "utf-8");
|
|
6161
|
+
renameSync(tmp, p);
|
|
6162
|
+
}
|
|
6163
|
+
function getOperatorEmail() {
|
|
6164
|
+
const shape = readFile();
|
|
6165
|
+
const email = shape.operatorEmail;
|
|
6166
|
+
if (typeof email !== "string") return null;
|
|
6167
|
+
const trimmed = email.trim();
|
|
6168
|
+
return trimmed.length > 0 && trimmed.includes("@") ? trimmed : null;
|
|
6169
|
+
}
|
|
6170
|
+
function setOperatorEmail(email) {
|
|
6171
|
+
const shape = readFile();
|
|
6172
|
+
if (!email || !email.trim()) {
|
|
6173
|
+
delete shape.operatorEmail;
|
|
6174
|
+
writeFile(shape);
|
|
6175
|
+
return null;
|
|
6176
|
+
}
|
|
6177
|
+
const trimmed = email.trim();
|
|
6178
|
+
if (!trimmed.includes("@")) {
|
|
6179
|
+
throw new Error("operator email must contain an @");
|
|
6180
|
+
}
|
|
6181
|
+
shape.operatorEmail = trimmed;
|
|
6182
|
+
writeFile(shape);
|
|
6183
|
+
return trimmed;
|
|
6184
|
+
}
|
|
6185
|
+
function operatorPrefsStoragePath() {
|
|
6186
|
+
return path();
|
|
6187
|
+
}
|
|
6188
|
+
|
|
6189
|
+
// src/host-sessions.ts
|
|
6190
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
6191
|
+
import { join as join8 } from "path";
|
|
6192
|
+
import { homedir as homedir6 } from "os";
|
|
6193
|
+
function storageDir() {
|
|
6194
|
+
return join8(homedir6(), ".agenticmail");
|
|
6195
|
+
}
|
|
6106
6196
|
function storagePath() {
|
|
6107
|
-
return
|
|
6197
|
+
return join8(storageDir(), "host-sessions.json");
|
|
6108
6198
|
}
|
|
6109
6199
|
var DEFAULT_SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
6110
|
-
function
|
|
6200
|
+
function readFile2() {
|
|
6111
6201
|
const p = storagePath();
|
|
6112
|
-
if (!
|
|
6202
|
+
if (!existsSync5(p)) return { version: 1, sessions: {} };
|
|
6113
6203
|
try {
|
|
6114
|
-
const raw =
|
|
6204
|
+
const raw = readFileSync4(p, "utf-8");
|
|
6115
6205
|
if (!raw.trim()) return { version: 1, sessions: {} };
|
|
6116
6206
|
const parsed = JSON.parse(raw);
|
|
6117
6207
|
return {
|
|
@@ -6122,28 +6212,28 @@ function readFile() {
|
|
|
6122
6212
|
return { version: 1, sessions: {} };
|
|
6123
6213
|
}
|
|
6124
6214
|
}
|
|
6125
|
-
function
|
|
6126
|
-
const
|
|
6215
|
+
function writeFile2(shape) {
|
|
6216
|
+
const dir2 = storageDir();
|
|
6127
6217
|
const p = storagePath();
|
|
6128
|
-
if (!
|
|
6218
|
+
if (!existsSync5(dir2)) mkdirSync5(dir2, { recursive: true });
|
|
6129
6219
|
const tmp = `${p}.agenticmail-tmp-${process.pid}`;
|
|
6130
|
-
|
|
6131
|
-
|
|
6220
|
+
writeFileSync5(tmp, JSON.stringify(shape, null, 2), "utf-8");
|
|
6221
|
+
renameSync2(tmp, p);
|
|
6132
6222
|
}
|
|
6133
6223
|
function saveHostSession(host, session) {
|
|
6134
6224
|
if (!session.sessionId) return;
|
|
6135
6225
|
try {
|
|
6136
|
-
const shape =
|
|
6226
|
+
const shape = readFile2();
|
|
6137
6227
|
shape.sessions[host] = {
|
|
6138
6228
|
...session,
|
|
6139
6229
|
lastSeenMs: Date.now()
|
|
6140
6230
|
};
|
|
6141
|
-
|
|
6231
|
+
writeFile2(shape);
|
|
6142
6232
|
} catch {
|
|
6143
6233
|
}
|
|
6144
6234
|
}
|
|
6145
6235
|
function loadHostSession(host, maxAgeMs = DEFAULT_SESSION_MAX_AGE_MS) {
|
|
6146
|
-
const shape =
|
|
6236
|
+
const shape = readFile2();
|
|
6147
6237
|
const record = shape.sessions[host];
|
|
6148
6238
|
if (!record) return null;
|
|
6149
6239
|
if (!isSessionFresh(record, maxAgeMs)) return null;
|
|
@@ -6155,10 +6245,10 @@ function isSessionFresh(session, maxAgeMs = DEFAULT_SESSION_MAX_AGE_MS) {
|
|
|
6155
6245
|
}
|
|
6156
6246
|
function forgetHostSession(host) {
|
|
6157
6247
|
try {
|
|
6158
|
-
const shape =
|
|
6248
|
+
const shape = readFile2();
|
|
6159
6249
|
if (!shape.sessions[host]) return;
|
|
6160
6250
|
delete shape.sessions[host];
|
|
6161
|
-
|
|
6251
|
+
writeFile2(shape);
|
|
6162
6252
|
} catch {
|
|
6163
6253
|
}
|
|
6164
6254
|
}
|
|
@@ -6208,21 +6298,21 @@ function validateApiUrl(raw) {
|
|
|
6208
6298
|
return parsed.origin;
|
|
6209
6299
|
}
|
|
6210
6300
|
function buildApiUrl(baseOrigin, pathAndQuery) {
|
|
6211
|
-
const
|
|
6212
|
-
return new URL(
|
|
6301
|
+
const path2 = pathAndQuery.startsWith("/") ? pathAndQuery : `/${pathAndQuery}`;
|
|
6302
|
+
return new URL(path2, baseOrigin + "/").toString();
|
|
6213
6303
|
}
|
|
6214
6304
|
|
|
6215
6305
|
// src/setup/index.ts
|
|
6216
6306
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
6217
|
-
import { existsSync as
|
|
6218
|
-
import { join as
|
|
6219
|
-
import { homedir as
|
|
6307
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, chmodSync as chmodSync2 } from "fs";
|
|
6308
|
+
import { join as join12 } from "path";
|
|
6309
|
+
import { homedir as homedir10 } from "os";
|
|
6220
6310
|
|
|
6221
6311
|
// src/setup/deps.ts
|
|
6222
6312
|
import { execFileSync } from "child_process";
|
|
6223
|
-
import { existsSync as
|
|
6224
|
-
import { join as
|
|
6225
|
-
import { homedir as
|
|
6313
|
+
import { existsSync as existsSync6 } from "fs";
|
|
6314
|
+
import { join as join9 } from "path";
|
|
6315
|
+
import { homedir as homedir7 } from "os";
|
|
6226
6316
|
var DependencyChecker = class {
|
|
6227
6317
|
async checkAll() {
|
|
6228
6318
|
return Promise.all([
|
|
@@ -6272,8 +6362,8 @@ var DependencyChecker = class {
|
|
|
6272
6362
|
}
|
|
6273
6363
|
}
|
|
6274
6364
|
async checkCloudflared() {
|
|
6275
|
-
const binPath =
|
|
6276
|
-
if (
|
|
6365
|
+
const binPath = join9(homedir7(), ".agenticmail", "bin", "cloudflared");
|
|
6366
|
+
if (existsSync6(binPath)) {
|
|
6277
6367
|
let version;
|
|
6278
6368
|
try {
|
|
6279
6369
|
const output = execFileSync(binPath, ["--version"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
@@ -6295,10 +6385,10 @@ var DependencyChecker = class {
|
|
|
6295
6385
|
|
|
6296
6386
|
// src/setup/installer.ts
|
|
6297
6387
|
import { execFileSync as execFileSync2, execSync, spawn as spawnChild } from "child_process";
|
|
6298
|
-
import { existsSync as
|
|
6299
|
-
import { writeFile as
|
|
6300
|
-
import { join as
|
|
6301
|
-
import { homedir as
|
|
6388
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, symlinkSync } from "fs";
|
|
6389
|
+
import { writeFile as writeFile3, rename, chmod as chmod2, mkdir as mkdir2, unlink } from "fs/promises";
|
|
6390
|
+
import { join as join10 } from "path";
|
|
6391
|
+
import { homedir as homedir8, platform as platform3, arch as arch2 } from "os";
|
|
6302
6392
|
function runShellWithRollingOutput(cmd, opts = {}) {
|
|
6303
6393
|
const maxLines = opts.maxLines ?? 20;
|
|
6304
6394
|
const timeout = opts.timeout ?? 3e5;
|
|
@@ -6474,11 +6564,11 @@ var DependencyInstaller = class {
|
|
|
6474
6564
|
try {
|
|
6475
6565
|
const composeBin = execFileSync2("which", ["docker-compose"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6476
6566
|
if (!composeBin) return;
|
|
6477
|
-
const pluginDir =
|
|
6478
|
-
const pluginPath =
|
|
6479
|
-
if (
|
|
6567
|
+
const pluginDir = join10(homedir8(), ".docker", "cli-plugins");
|
|
6568
|
+
const pluginPath = join10(pluginDir, "docker-compose");
|
|
6569
|
+
if (existsSync7(pluginPath)) return;
|
|
6480
6570
|
try {
|
|
6481
|
-
|
|
6571
|
+
mkdirSync6(pluginDir, { recursive: true });
|
|
6482
6572
|
} catch {
|
|
6483
6573
|
}
|
|
6484
6574
|
try {
|
|
@@ -6541,9 +6631,9 @@ var DependencyInstaller = class {
|
|
|
6541
6631
|
return;
|
|
6542
6632
|
}
|
|
6543
6633
|
this.onProgress("__progress__:5:Installing Docker Engine...");
|
|
6544
|
-
const tmpDir =
|
|
6634
|
+
const tmpDir = join10(homedir8(), ".agenticmail", "tmp");
|
|
6545
6635
|
await mkdir2(tmpDir, { recursive: true });
|
|
6546
|
-
const scriptPath =
|
|
6636
|
+
const scriptPath = join10(tmpDir, "install-docker.sh");
|
|
6547
6637
|
const dlResult = await runShellWithRollingOutput(
|
|
6548
6638
|
`curl -fsSL https://get.docker.com -o "${scriptPath}" && sudo sh "${scriptPath}"`,
|
|
6549
6639
|
{ timeout: 3e5 }
|
|
@@ -6611,8 +6701,8 @@ var DependencyInstaller = class {
|
|
|
6611
6701
|
}
|
|
6612
6702
|
try {
|
|
6613
6703
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
6614
|
-
const dockerExe =
|
|
6615
|
-
if (
|
|
6704
|
+
const dockerExe = join10(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
6705
|
+
if (existsSync7(dockerExe)) {
|
|
6616
6706
|
execSync(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
6617
6707
|
}
|
|
6618
6708
|
} catch {
|
|
@@ -6662,8 +6752,8 @@ var DependencyInstaller = class {
|
|
|
6662
6752
|
this.onProgress("__progress__:70:Docker Desktop installed. Starting...");
|
|
6663
6753
|
try {
|
|
6664
6754
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
6665
|
-
const dockerExe =
|
|
6666
|
-
if (
|
|
6755
|
+
const dockerExe = join10(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
6756
|
+
if (existsSync7(dockerExe)) {
|
|
6667
6757
|
execSync(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
6668
6758
|
}
|
|
6669
6759
|
} catch {
|
|
@@ -6700,7 +6790,7 @@ var DependencyInstaller = class {
|
|
|
6700
6790
|
* Start the Stalwart mail server Docker container.
|
|
6701
6791
|
*/
|
|
6702
6792
|
async startStalwart(composePath) {
|
|
6703
|
-
if (!
|
|
6793
|
+
if (!existsSync7(composePath)) {
|
|
6704
6794
|
throw new Error(`docker-compose.yml not found at: ${composePath}`);
|
|
6705
6795
|
}
|
|
6706
6796
|
if (!this.isDockerReady()) {
|
|
@@ -6759,16 +6849,16 @@ var DependencyInstaller = class {
|
|
|
6759
6849
|
*/
|
|
6760
6850
|
async installCloudflared() {
|
|
6761
6851
|
const os = platform3();
|
|
6762
|
-
const binDir =
|
|
6852
|
+
const binDir = join10(homedir8(), ".agenticmail", "bin");
|
|
6763
6853
|
const binName = os === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
6764
|
-
const binPath =
|
|
6765
|
-
if (
|
|
6854
|
+
const binPath = join10(binDir, binName);
|
|
6855
|
+
if (existsSync7(binPath)) {
|
|
6766
6856
|
return binPath;
|
|
6767
6857
|
}
|
|
6768
6858
|
try {
|
|
6769
6859
|
const whichCmd = os === "win32" ? "where" : "which";
|
|
6770
6860
|
const sysPath = execFileSync2(whichCmd, ["cloudflared"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim().split("\n")[0];
|
|
6771
|
-
if (sysPath &&
|
|
6861
|
+
if (sysPath && existsSync7(sysPath)) return sysPath;
|
|
6772
6862
|
} catch {
|
|
6773
6863
|
}
|
|
6774
6864
|
this.onProgress("Downloading cloudflared...");
|
|
@@ -6791,8 +6881,8 @@ var DependencyInstaller = class {
|
|
|
6791
6881
|
}
|
|
6792
6882
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
6793
6883
|
if (os === "darwin") {
|
|
6794
|
-
const tgzPath =
|
|
6795
|
-
await
|
|
6884
|
+
const tgzPath = join10(binDir, "cloudflared.tgz");
|
|
6885
|
+
await writeFile3(tgzPath, buffer);
|
|
6796
6886
|
try {
|
|
6797
6887
|
execFileSync2("tar", ["-xzf", tgzPath, "-C", binDir, "cloudflared"], { timeout: 15e3, stdio: "ignore" });
|
|
6798
6888
|
await chmod2(binPath, 493);
|
|
@@ -6804,11 +6894,11 @@ var DependencyInstaller = class {
|
|
|
6804
6894
|
}
|
|
6805
6895
|
} else {
|
|
6806
6896
|
const tmpPath = binPath + ".tmp";
|
|
6807
|
-
await
|
|
6897
|
+
await writeFile3(tmpPath, buffer);
|
|
6808
6898
|
if (os !== "win32") await chmod2(tmpPath, 493);
|
|
6809
6899
|
await rename(tmpPath, binPath);
|
|
6810
6900
|
}
|
|
6811
|
-
if (!
|
|
6901
|
+
if (!existsSync7(binPath)) {
|
|
6812
6902
|
throw new Error("cloudflared download succeeded but binary not found after extraction");
|
|
6813
6903
|
}
|
|
6814
6904
|
this.onProgress("cloudflared installed");
|
|
@@ -6826,9 +6916,9 @@ var DependencyInstaller = class {
|
|
|
6826
6916
|
|
|
6827
6917
|
// src/setup/service.ts
|
|
6828
6918
|
import { execFileSync as execFileSync3, execSync as execSync2 } from "child_process";
|
|
6829
|
-
import { existsSync as
|
|
6830
|
-
import { join as
|
|
6831
|
-
import { homedir as
|
|
6919
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync6, unlinkSync, mkdirSync as mkdirSync7, chmodSync } from "fs";
|
|
6920
|
+
import { join as join11 } from "path";
|
|
6921
|
+
import { homedir as homedir9, platform as platform4 } from "os";
|
|
6832
6922
|
import { createRequire as createRequire2 } from "module";
|
|
6833
6923
|
var PLIST_LABEL = "com.agenticmail.server";
|
|
6834
6924
|
var SYSTEMD_UNIT = "agenticmail.service";
|
|
@@ -6839,9 +6929,9 @@ var ServiceManager = class {
|
|
|
6839
6929
|
*/
|
|
6840
6930
|
getServicePath() {
|
|
6841
6931
|
if (this.os === "darwin") {
|
|
6842
|
-
return
|
|
6932
|
+
return join11(homedir9(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
6843
6933
|
} else {
|
|
6844
|
-
return
|
|
6934
|
+
return join11(homedir9(), ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
6845
6935
|
}
|
|
6846
6936
|
}
|
|
6847
6937
|
/**
|
|
@@ -6875,40 +6965,40 @@ var ServiceManager = class {
|
|
|
6875
6965
|
try {
|
|
6876
6966
|
const req = createRequire2(import.meta.url);
|
|
6877
6967
|
const resolved = req.resolve("@agenticmail/api");
|
|
6878
|
-
if (
|
|
6968
|
+
if (existsSync8(resolved)) return resolved;
|
|
6879
6969
|
} catch {
|
|
6880
6970
|
}
|
|
6881
6971
|
const parentPackages = [
|
|
6882
|
-
|
|
6972
|
+
join11("@agenticmail", "cli"),
|
|
6883
6973
|
// current scoped package
|
|
6884
6974
|
"agenticmail"
|
|
6885
6975
|
// legacy unscoped package
|
|
6886
6976
|
];
|
|
6887
6977
|
const baseDirs = [
|
|
6888
6978
|
// user-local install
|
|
6889
|
-
|
|
6979
|
+
join11(homedir9(), "node_modules")
|
|
6890
6980
|
];
|
|
6891
6981
|
try {
|
|
6892
6982
|
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6893
|
-
baseDirs.push(
|
|
6894
|
-
baseDirs.push(
|
|
6983
|
+
baseDirs.push(join11(prefix, "lib", "node_modules"));
|
|
6984
|
+
baseDirs.push(join11(prefix, "node_modules"));
|
|
6895
6985
|
} catch {
|
|
6896
6986
|
}
|
|
6897
6987
|
baseDirs.push("/opt/homebrew/lib/node_modules");
|
|
6898
6988
|
baseDirs.push("/usr/local/lib/node_modules");
|
|
6899
6989
|
for (const base of baseDirs) {
|
|
6900
|
-
const sibling =
|
|
6901
|
-
if (
|
|
6990
|
+
const sibling = join11(base, "@agenticmail", "api", "dist", "index.js");
|
|
6991
|
+
if (existsSync8(sibling)) return sibling;
|
|
6902
6992
|
for (const parent of parentPackages) {
|
|
6903
|
-
const nested =
|
|
6904
|
-
if (
|
|
6993
|
+
const nested = join11(base, parent, "node_modules", "@agenticmail", "api", "dist", "index.js");
|
|
6994
|
+
if (existsSync8(nested)) return nested;
|
|
6905
6995
|
}
|
|
6906
6996
|
}
|
|
6907
|
-
const dataDir =
|
|
6908
|
-
const entryCache =
|
|
6909
|
-
if (
|
|
6910
|
-
const cached =
|
|
6911
|
-
if (cached &&
|
|
6997
|
+
const dataDir = join11(homedir9(), ".agenticmail");
|
|
6998
|
+
const entryCache = join11(dataDir, "api-entry.path");
|
|
6999
|
+
if (existsSync8(entryCache)) {
|
|
7000
|
+
const cached = readFileSync5(entryCache, "utf-8").trim();
|
|
7001
|
+
if (cached && existsSync8(cached)) return cached;
|
|
6912
7002
|
}
|
|
6913
7003
|
throw new Error("Could not find @agenticmail/api entry point. Run `agenticmail start` first to populate the cache.");
|
|
6914
7004
|
}
|
|
@@ -6916,9 +7006,9 @@ var ServiceManager = class {
|
|
|
6916
7006
|
* Cache the API entry path so the service can find it later.
|
|
6917
7007
|
*/
|
|
6918
7008
|
cacheApiEntryPath(entryPath) {
|
|
6919
|
-
const dataDir =
|
|
6920
|
-
if (!
|
|
6921
|
-
|
|
7009
|
+
const dataDir = join11(homedir9(), ".agenticmail");
|
|
7010
|
+
if (!existsSync8(dataDir)) mkdirSync7(dataDir, { recursive: true });
|
|
7011
|
+
writeFileSync6(join11(dataDir, "api-entry.path"), entryPath);
|
|
6922
7012
|
}
|
|
6923
7013
|
/**
|
|
6924
7014
|
* Get the current package version.
|
|
@@ -6931,36 +7021,36 @@ var ServiceManager = class {
|
|
|
6931
7021
|
try {
|
|
6932
7022
|
const req = createRequire2(import.meta.url);
|
|
6933
7023
|
const pkgJson = req.resolve("@agenticmail/cli/package.json");
|
|
6934
|
-
if (
|
|
6935
|
-
const pkg = JSON.parse(
|
|
7024
|
+
if (existsSync8(pkgJson)) {
|
|
7025
|
+
const pkg = JSON.parse(readFileSync5(pkgJson, "utf-8"));
|
|
6936
7026
|
if (pkg.version) return pkg.version;
|
|
6937
7027
|
}
|
|
6938
7028
|
} catch {
|
|
6939
7029
|
}
|
|
6940
7030
|
try {
|
|
6941
7031
|
const apiEntry = this.getApiEntryPath();
|
|
6942
|
-
const apiPkg =
|
|
6943
|
-
if (
|
|
6944
|
-
const pkg = JSON.parse(
|
|
7032
|
+
const apiPkg = join11(apiEntry, "..", "..", "package.json");
|
|
7033
|
+
if (existsSync8(apiPkg)) {
|
|
7034
|
+
const pkg = JSON.parse(readFileSync5(apiPkg, "utf-8"));
|
|
6945
7035
|
if (pkg.version) return pkg.version;
|
|
6946
7036
|
}
|
|
6947
7037
|
} catch {
|
|
6948
7038
|
}
|
|
6949
7039
|
const candidates = [
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
7040
|
+
join11(homedir9(), "node_modules", "@agenticmail", "cli", "package.json"),
|
|
7041
|
+
join11(homedir9(), "node_modules", "agenticmail", "package.json"),
|
|
7042
|
+
join11(homedir9(), ".agenticmail", "package-version.json")
|
|
6953
7043
|
];
|
|
6954
7044
|
try {
|
|
6955
7045
|
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6956
|
-
candidates.push(
|
|
6957
|
-
candidates.push(
|
|
7046
|
+
candidates.push(join11(prefix, "lib", "node_modules", "@agenticmail", "cli", "package.json"));
|
|
7047
|
+
candidates.push(join11(prefix, "lib", "node_modules", "agenticmail", "package.json"));
|
|
6958
7048
|
} catch {
|
|
6959
7049
|
}
|
|
6960
7050
|
for (const p of candidates) {
|
|
6961
7051
|
try {
|
|
6962
|
-
if (
|
|
6963
|
-
const pkg = JSON.parse(
|
|
7052
|
+
if (existsSync8(p)) {
|
|
7053
|
+
const pkg = JSON.parse(readFileSync5(p, "utf-8"));
|
|
6964
7054
|
if (pkg.version) return pkg.version;
|
|
6965
7055
|
}
|
|
6966
7056
|
} catch {
|
|
@@ -6973,9 +7063,9 @@ var ServiceManager = class {
|
|
|
6973
7063
|
* This ensures AgenticMail doesn't fail on boot when Docker is still loading.
|
|
6974
7064
|
*/
|
|
6975
7065
|
generateStartScript(nodePath, apiEntry) {
|
|
6976
|
-
const scriptPath =
|
|
6977
|
-
const scriptDir =
|
|
6978
|
-
if (!
|
|
7066
|
+
const scriptPath = join11(homedir9(), ".agenticmail", "bin", "start-server.sh");
|
|
7067
|
+
const scriptDir = join11(homedir9(), ".agenticmail", "bin");
|
|
7068
|
+
if (!existsSync8(scriptDir)) mkdirSync7(scriptDir, { recursive: true });
|
|
6979
7069
|
const script = [
|
|
6980
7070
|
"#!/bin/bash",
|
|
6981
7071
|
"# AgenticMail auto-start script",
|
|
@@ -7026,7 +7116,7 @@ var ServiceManager = class {
|
|
|
7026
7116
|
`log "Starting API server: ${nodePath} ${apiEntry}"`,
|
|
7027
7117
|
`exec "${nodePath}" "${apiEntry}"`
|
|
7028
7118
|
].join("\n") + "\n";
|
|
7029
|
-
|
|
7119
|
+
writeFileSync6(scriptPath, script, { mode: 493 });
|
|
7030
7120
|
return scriptPath;
|
|
7031
7121
|
}
|
|
7032
7122
|
/**
|
|
@@ -7039,9 +7129,9 @@ var ServiceManager = class {
|
|
|
7039
7129
|
* - Service version tracking in env vars
|
|
7040
7130
|
*/
|
|
7041
7131
|
generatePlist(nodePath, apiEntry, configPath) {
|
|
7042
|
-
const config = JSON.parse(
|
|
7043
|
-
const logDir =
|
|
7044
|
-
if (!
|
|
7132
|
+
const config = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
7133
|
+
const logDir = join11(homedir9(), ".agenticmail", "logs");
|
|
7134
|
+
if (!existsSync8(logDir)) mkdirSync7(logDir, { recursive: true });
|
|
7045
7135
|
const version = this.getVersion();
|
|
7046
7136
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
7047
7137
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -7062,9 +7152,9 @@ var ServiceManager = class {
|
|
|
7062
7152
|
<key>EnvironmentVariables</key>
|
|
7063
7153
|
<dict>
|
|
7064
7154
|
<key>HOME</key>
|
|
7065
|
-
<string>${
|
|
7155
|
+
<string>${homedir9()}</string>
|
|
7066
7156
|
<key>AGENTICMAIL_DATA_DIR</key>
|
|
7067
|
-
<string>${config.dataDir ||
|
|
7157
|
+
<string>${config.dataDir || join11(homedir9(), ".agenticmail")}</string>
|
|
7068
7158
|
<key>PATH</key>
|
|
7069
7159
|
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
7070
7160
|
<key>AGENTICMAIL_SERVICE_VERSION</key>
|
|
@@ -7117,8 +7207,8 @@ var ServiceManager = class {
|
|
|
7117
7207
|
* - Proper dependency ordering
|
|
7118
7208
|
*/
|
|
7119
7209
|
generateSystemdUnit(nodePath, apiEntry, configPath) {
|
|
7120
|
-
const config = JSON.parse(
|
|
7121
|
-
const dataDir = config.dataDir ||
|
|
7210
|
+
const config = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
7211
|
+
const dataDir = config.dataDir || join11(homedir9(), ".agenticmail");
|
|
7122
7212
|
const version = this.getVersion();
|
|
7123
7213
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
7124
7214
|
return `[Unit]
|
|
@@ -7135,7 +7225,7 @@ Restart=always
|
|
|
7135
7225
|
RestartSec=15
|
|
7136
7226
|
TimeoutStartSec=660
|
|
7137
7227
|
LimitNOFILE=8192
|
|
7138
|
-
Environment=HOME=${
|
|
7228
|
+
Environment=HOME=${homedir9()}
|
|
7139
7229
|
Environment=AGENTICMAIL_DATA_DIR=${dataDir}
|
|
7140
7230
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin
|
|
7141
7231
|
Environment=AGENTICMAIL_SERVICE_VERSION=${version}
|
|
@@ -7148,8 +7238,8 @@ WantedBy=default.target
|
|
|
7148
7238
|
* Install the auto-start service.
|
|
7149
7239
|
*/
|
|
7150
7240
|
install() {
|
|
7151
|
-
const configPath =
|
|
7152
|
-
if (!
|
|
7241
|
+
const configPath = join11(homedir9(), ".agenticmail", "config.json");
|
|
7242
|
+
if (!existsSync8(configPath)) {
|
|
7153
7243
|
return { installed: false, message: "Config not found. Run agenticmail setup first." };
|
|
7154
7244
|
}
|
|
7155
7245
|
const nodePath = this.getNodePath();
|
|
@@ -7161,16 +7251,16 @@ WantedBy=default.target
|
|
|
7161
7251
|
}
|
|
7162
7252
|
const servicePath = this.getServicePath();
|
|
7163
7253
|
if (this.os === "darwin") {
|
|
7164
|
-
const
|
|
7165
|
-
if (!
|
|
7166
|
-
if (
|
|
7254
|
+
const dir2 = join11(homedir9(), "Library", "LaunchAgents");
|
|
7255
|
+
if (!existsSync8(dir2)) mkdirSync7(dir2, { recursive: true });
|
|
7256
|
+
if (existsSync8(servicePath)) {
|
|
7167
7257
|
try {
|
|
7168
7258
|
execFileSync3("launchctl", ["unload", servicePath], { timeout: 1e4, stdio: "ignore" });
|
|
7169
7259
|
} catch {
|
|
7170
7260
|
}
|
|
7171
7261
|
}
|
|
7172
7262
|
const plist = this.generatePlist(nodePath, apiEntry, configPath);
|
|
7173
|
-
|
|
7263
|
+
writeFileSync6(servicePath, plist);
|
|
7174
7264
|
chmodSync(servicePath, 384);
|
|
7175
7265
|
try {
|
|
7176
7266
|
execFileSync3("launchctl", ["load", servicePath], { timeout: 1e4, stdio: "ignore" });
|
|
@@ -7179,10 +7269,10 @@ WantedBy=default.target
|
|
|
7179
7269
|
}
|
|
7180
7270
|
return { installed: true, message: `Service installed at ${servicePath}` };
|
|
7181
7271
|
} else if (this.os === "linux") {
|
|
7182
|
-
const
|
|
7183
|
-
if (!
|
|
7272
|
+
const dir2 = join11(homedir9(), ".config", "systemd", "user");
|
|
7273
|
+
if (!existsSync8(dir2)) mkdirSync7(dir2, { recursive: true });
|
|
7184
7274
|
const unit = this.generateSystemdUnit(nodePath, apiEntry, configPath);
|
|
7185
|
-
|
|
7275
|
+
writeFileSync6(servicePath, unit);
|
|
7186
7276
|
chmodSync(servicePath, 384);
|
|
7187
7277
|
try {
|
|
7188
7278
|
execFileSync3("systemctl", ["--user", "daemon-reload"], { timeout: 1e4, stdio: "ignore" });
|
|
@@ -7205,7 +7295,7 @@ WantedBy=default.target
|
|
|
7205
7295
|
*/
|
|
7206
7296
|
uninstall() {
|
|
7207
7297
|
const servicePath = this.getServicePath();
|
|
7208
|
-
if (!
|
|
7298
|
+
if (!existsSync8(servicePath)) {
|
|
7209
7299
|
return { removed: false, message: "Service is not installed." };
|
|
7210
7300
|
}
|
|
7211
7301
|
if (this.os === "darwin") {
|
|
@@ -7243,7 +7333,7 @@ WantedBy=default.target
|
|
|
7243
7333
|
status() {
|
|
7244
7334
|
const servicePath = this.getServicePath();
|
|
7245
7335
|
const plat = this.os === "darwin" ? "launchd" : this.os === "linux" ? "systemd" : "unsupported";
|
|
7246
|
-
const installed =
|
|
7336
|
+
const installed = existsSync8(servicePath);
|
|
7247
7337
|
let running = false;
|
|
7248
7338
|
if (installed) {
|
|
7249
7339
|
if (this.os === "darwin") {
|
|
@@ -7298,34 +7388,34 @@ WantedBy=default.target
|
|
|
7298
7388
|
needsRepair() {
|
|
7299
7389
|
if (this.os !== "darwin" && this.os !== "linux") return null;
|
|
7300
7390
|
const servicePath = this.getServicePath();
|
|
7301
|
-
if (!
|
|
7391
|
+
if (!existsSync8(servicePath)) return null;
|
|
7302
7392
|
let serviceContent = "";
|
|
7303
7393
|
try {
|
|
7304
|
-
serviceContent =
|
|
7394
|
+
serviceContent = readFileSync5(servicePath, "utf-8");
|
|
7305
7395
|
} catch {
|
|
7306
7396
|
return { reason: "Service file unreadable" };
|
|
7307
7397
|
}
|
|
7308
|
-
const startScript =
|
|
7398
|
+
const startScript = join11(homedir9(), ".agenticmail", "bin", "start-server.sh");
|
|
7309
7399
|
if (serviceContent.includes(startScript)) {
|
|
7310
|
-
if (!
|
|
7400
|
+
if (!existsSync8(startScript)) {
|
|
7311
7401
|
return { reason: "start-server.sh is missing" };
|
|
7312
7402
|
}
|
|
7313
7403
|
let scriptContent = "";
|
|
7314
7404
|
try {
|
|
7315
|
-
scriptContent =
|
|
7405
|
+
scriptContent = readFileSync5(startScript, "utf-8");
|
|
7316
7406
|
} catch {
|
|
7317
7407
|
return { reason: "start-server.sh unreadable" };
|
|
7318
7408
|
}
|
|
7319
7409
|
const apiPathMatch = scriptContent.match(/(\/[^"\s]+@agenticmail\/api\/dist\/index\.js)/);
|
|
7320
7410
|
if (apiPathMatch) {
|
|
7321
7411
|
const referenced = apiPathMatch[1];
|
|
7322
|
-
if (!
|
|
7412
|
+
if (!existsSync8(referenced)) {
|
|
7323
7413
|
return { reason: `start-server.sh references missing path: ${referenced}` };
|
|
7324
7414
|
}
|
|
7325
7415
|
}
|
|
7326
7416
|
if (/node_modules\/agenticmail\/(?!.*@agenticmail\/cli)/.test(scriptContent)) {
|
|
7327
7417
|
const stale = /(\S*node_modules\/agenticmail\/\S*)/.exec(scriptContent)?.[1];
|
|
7328
|
-
if (stale && !
|
|
7418
|
+
if (stale && !existsSync8(stale)) {
|
|
7329
7419
|
return { reason: `start-server.sh references legacy unscoped path: ${stale}` };
|
|
7330
7420
|
}
|
|
7331
7421
|
}
|
|
@@ -7338,11 +7428,11 @@ WantedBy=default.target
|
|
|
7338
7428
|
return { reason: `Service version drift (current CLI is v${currentVersion})` };
|
|
7339
7429
|
}
|
|
7340
7430
|
}
|
|
7341
|
-
const entryCache =
|
|
7342
|
-
if (
|
|
7431
|
+
const entryCache = join11(homedir9(), ".agenticmail", "api-entry.path");
|
|
7432
|
+
if (existsSync8(entryCache)) {
|
|
7343
7433
|
try {
|
|
7344
|
-
const cached =
|
|
7345
|
-
if (cached && !
|
|
7434
|
+
const cached = readFileSync5(entryCache, "utf-8").trim();
|
|
7435
|
+
if (cached && !existsSync8(cached)) {
|
|
7346
7436
|
return { reason: `Cached API entry path no longer exists: ${cached}` };
|
|
7347
7437
|
}
|
|
7348
7438
|
} catch {
|
|
@@ -7381,8 +7471,8 @@ var SetupManager = class {
|
|
|
7381
7471
|
async ensureStalwart(composePath) {
|
|
7382
7472
|
const status = await this.checker.checkStalwart();
|
|
7383
7473
|
if (status.installed) return;
|
|
7384
|
-
const
|
|
7385
|
-
await this.installer.startStalwart(
|
|
7474
|
+
const path2 = composePath ?? this.getComposePath();
|
|
7475
|
+
await this.installer.startStalwart(path2);
|
|
7386
7476
|
}
|
|
7387
7477
|
async ensureCloudflared() {
|
|
7388
7478
|
return this.installer.installCloudflared();
|
|
@@ -7393,13 +7483,13 @@ var SetupManager = class {
|
|
|
7393
7483
|
* falls back to monorepo location.
|
|
7394
7484
|
*/
|
|
7395
7485
|
getComposePath() {
|
|
7396
|
-
const standalonePath =
|
|
7397
|
-
if (
|
|
7486
|
+
const standalonePath = join12(homedir10(), ".agenticmail", "docker-compose.yml");
|
|
7487
|
+
if (existsSync9(standalonePath)) return standalonePath;
|
|
7398
7488
|
const cwd = process.cwd();
|
|
7399
|
-
const candidates = [cwd,
|
|
7400
|
-
for (const
|
|
7401
|
-
const p =
|
|
7402
|
-
if (
|
|
7489
|
+
const candidates = [cwd, join12(cwd, "..")];
|
|
7490
|
+
for (const dir2 of candidates) {
|
|
7491
|
+
const p = join12(dir2, "docker-compose.yml");
|
|
7492
|
+
if (existsSync9(p)) return p;
|
|
7403
7493
|
}
|
|
7404
7494
|
return standalonePath;
|
|
7405
7495
|
}
|
|
@@ -7409,19 +7499,19 @@ var SetupManager = class {
|
|
|
7409
7499
|
* Always regenerates Docker files to keep passwords in sync.
|
|
7410
7500
|
*/
|
|
7411
7501
|
initConfig() {
|
|
7412
|
-
const dataDir =
|
|
7413
|
-
const configPath =
|
|
7414
|
-
const envPath =
|
|
7415
|
-
if (
|
|
7502
|
+
const dataDir = join12(homedir10(), ".agenticmail");
|
|
7503
|
+
const configPath = join12(dataDir, "config.json");
|
|
7504
|
+
const envPath = join12(dataDir, ".env");
|
|
7505
|
+
if (existsSync9(configPath)) {
|
|
7416
7506
|
try {
|
|
7417
|
-
const existing = JSON.parse(
|
|
7507
|
+
const existing = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
7418
7508
|
this.generateDockerFiles(existing);
|
|
7419
7509
|
return { configPath, envPath, config: existing, isNew: false };
|
|
7420
7510
|
} catch {
|
|
7421
7511
|
}
|
|
7422
7512
|
}
|
|
7423
|
-
if (!
|
|
7424
|
-
|
|
7513
|
+
if (!existsSync9(dataDir)) {
|
|
7514
|
+
mkdirSync8(dataDir, { recursive: true });
|
|
7425
7515
|
}
|
|
7426
7516
|
const masterKey = `mk_${randomBytes3(24).toString("hex")}`;
|
|
7427
7517
|
const stalwartPassword = randomBytes3(16).toString("hex");
|
|
@@ -7437,7 +7527,7 @@ var SetupManager = class {
|
|
|
7437
7527
|
api: { port: 3829, host: "127.0.0.1" },
|
|
7438
7528
|
dataDir
|
|
7439
7529
|
};
|
|
7440
|
-
|
|
7530
|
+
writeFileSync7(configPath, JSON.stringify(config, null, 2));
|
|
7441
7531
|
chmodSync2(configPath, 384);
|
|
7442
7532
|
const envContent = `# Auto-generated by agenticmail setup
|
|
7443
7533
|
STALWART_ADMIN_USER=admin
|
|
@@ -7453,7 +7543,7 @@ SMTP_PORT=587
|
|
|
7453
7543
|
IMAP_HOST=localhost
|
|
7454
7544
|
IMAP_PORT=143
|
|
7455
7545
|
`;
|
|
7456
|
-
|
|
7546
|
+
writeFileSync7(envPath, envContent);
|
|
7457
7547
|
chmodSync2(envPath, 384);
|
|
7458
7548
|
this.generateDockerFiles(config);
|
|
7459
7549
|
return { configPath, envPath, config, isNew: true };
|
|
@@ -7463,13 +7553,13 @@ IMAP_PORT=143
|
|
|
7463
7553
|
* with the correct admin password from config.
|
|
7464
7554
|
*/
|
|
7465
7555
|
generateDockerFiles(config) {
|
|
7466
|
-
const dataDir = config.dataDir ||
|
|
7467
|
-
if (!
|
|
7468
|
-
|
|
7556
|
+
const dataDir = config.dataDir || join12(homedir10(), ".agenticmail");
|
|
7557
|
+
if (!existsSync9(dataDir)) {
|
|
7558
|
+
mkdirSync8(dataDir, { recursive: true });
|
|
7469
7559
|
}
|
|
7470
7560
|
const password = config.stalwart?.adminPassword || "changeme";
|
|
7471
|
-
const composePath =
|
|
7472
|
-
|
|
7561
|
+
const composePath = join12(dataDir, "docker-compose.yml");
|
|
7562
|
+
writeFileSync7(composePath, `services:
|
|
7473
7563
|
stalwart:
|
|
7474
7564
|
# Pinned to v0.15.5 \u2014 Stalwart 0.16+ moved its config to JSON
|
|
7475
7565
|
# at /etc/stalwart/config.json (hardcoded into the container
|
|
@@ -7499,8 +7589,8 @@ volumes:
|
|
|
7499
7589
|
stalwart-data:
|
|
7500
7590
|
`);
|
|
7501
7591
|
chmodSync2(composePath, 384);
|
|
7502
|
-
const tomlPath =
|
|
7503
|
-
|
|
7592
|
+
const tomlPath = join12(dataDir, "stalwart.toml");
|
|
7593
|
+
writeFileSync7(tomlPath, `# Stalwart Mail Server \u2014 AgenticMail Configuration
|
|
7504
7594
|
|
|
7505
7595
|
[server]
|
|
7506
7596
|
hostname = "localhost"
|
|
@@ -7556,8 +7646,8 @@ secret = "${password}"
|
|
|
7556
7646
|
* Check if config has already been initialized.
|
|
7557
7647
|
*/
|
|
7558
7648
|
isInitialized() {
|
|
7559
|
-
const configPath =
|
|
7560
|
-
return
|
|
7649
|
+
const configPath = join12(homedir10(), ".agenticmail", "config.json");
|
|
7650
|
+
return existsSync9(configPath);
|
|
7561
7651
|
}
|
|
7562
7652
|
};
|
|
7563
7653
|
|
|
@@ -7596,18 +7686,18 @@ function threadIdFor(input) {
|
|
|
7596
7686
|
|
|
7597
7687
|
// src/threading/thread-cache.ts
|
|
7598
7688
|
import {
|
|
7599
|
-
existsSync as
|
|
7600
|
-
mkdirSync as
|
|
7601
|
-
readFileSync as
|
|
7602
|
-
writeFileSync as
|
|
7689
|
+
existsSync as existsSync10,
|
|
7690
|
+
mkdirSync as mkdirSync9,
|
|
7691
|
+
readFileSync as readFileSync7,
|
|
7692
|
+
writeFileSync as writeFileSync8,
|
|
7603
7693
|
readdirSync,
|
|
7604
7694
|
statSync,
|
|
7605
7695
|
rmSync,
|
|
7606
|
-
renameSync as
|
|
7696
|
+
renameSync as renameSync3
|
|
7607
7697
|
} from "fs";
|
|
7608
|
-
import { homedir as
|
|
7609
|
-
import { join as
|
|
7610
|
-
var CACHE_DIR_DEFAULT =
|
|
7698
|
+
import { homedir as homedir11 } from "os";
|
|
7699
|
+
import { join as join13 } from "path";
|
|
7700
|
+
var CACHE_DIR_DEFAULT = join13(homedir11(), ".agenticmail", "thread-cache");
|
|
7611
7701
|
var DEFAULT_K_MESSAGES = 10;
|
|
7612
7702
|
var DEFAULT_LRU_CAP = 5e3;
|
|
7613
7703
|
var PREVIEW_MAX_CHARS = 240;
|
|
@@ -7620,18 +7710,18 @@ var ThreadCache = class {
|
|
|
7620
7710
|
this.k = opts.k ?? DEFAULT_K_MESSAGES;
|
|
7621
7711
|
this.lruCap = opts.lruCap ?? DEFAULT_LRU_CAP;
|
|
7622
7712
|
try {
|
|
7623
|
-
|
|
7713
|
+
mkdirSync9(this.dir, { recursive: true });
|
|
7624
7714
|
} catch {
|
|
7625
7715
|
}
|
|
7626
7716
|
}
|
|
7627
7717
|
pathFor(threadId) {
|
|
7628
|
-
return
|
|
7718
|
+
return join13(this.dir, `${threadId}.json`);
|
|
7629
7719
|
}
|
|
7630
7720
|
read(threadId) {
|
|
7631
7721
|
const p = this.pathFor(threadId);
|
|
7632
|
-
if (!
|
|
7722
|
+
if (!existsSync10(p)) return null;
|
|
7633
7723
|
try {
|
|
7634
|
-
const raw =
|
|
7724
|
+
const raw = readFileSync7(p, "utf-8");
|
|
7635
7725
|
return JSON.parse(raw);
|
|
7636
7726
|
} catch {
|
|
7637
7727
|
try {
|
|
@@ -7696,8 +7786,8 @@ var ThreadCache = class {
|
|
|
7696
7786
|
writeAtomic(threadId, entry) {
|
|
7697
7787
|
const p = this.pathFor(threadId);
|
|
7698
7788
|
const tmp = `${p}.tmp`;
|
|
7699
|
-
|
|
7700
|
-
|
|
7789
|
+
writeFileSync8(tmp, JSON.stringify(entry), "utf-8");
|
|
7790
|
+
renameSync3(tmp, p);
|
|
7701
7791
|
}
|
|
7702
7792
|
/**
|
|
7703
7793
|
* Best-effort LRU eviction. Runs at most every 256 writes (we
|
|
@@ -7715,7 +7805,7 @@ var ThreadCache = class {
|
|
|
7715
7805
|
}
|
|
7716
7806
|
if (files.length <= this.lruCap) return;
|
|
7717
7807
|
const stats = files.map((f) => {
|
|
7718
|
-
const p =
|
|
7808
|
+
const p = join13(this.dir, f);
|
|
7719
7809
|
try {
|
|
7720
7810
|
return { p, mtime: statSync(p).mtimeMs };
|
|
7721
7811
|
} catch {
|
|
@@ -7746,36 +7836,36 @@ function dedupAndCap(messages, k) {
|
|
|
7746
7836
|
|
|
7747
7837
|
// src/threading/agent-memory.ts
|
|
7748
7838
|
import {
|
|
7749
|
-
existsSync as
|
|
7750
|
-
mkdirSync as
|
|
7751
|
-
readFileSync as
|
|
7752
|
-
writeFileSync as
|
|
7839
|
+
existsSync as existsSync11,
|
|
7840
|
+
mkdirSync as mkdirSync10,
|
|
7841
|
+
readFileSync as readFileSync8,
|
|
7842
|
+
writeFileSync as writeFileSync9,
|
|
7753
7843
|
rmSync as rmSync2,
|
|
7754
|
-
renameSync as
|
|
7844
|
+
renameSync as renameSync4
|
|
7755
7845
|
} from "fs";
|
|
7756
|
-
import { homedir as
|
|
7757
|
-
import { join as
|
|
7758
|
-
var MEMORY_DIR_DEFAULT =
|
|
7846
|
+
import { homedir as homedir12 } from "os";
|
|
7847
|
+
import { join as join14 } from "path";
|
|
7848
|
+
var MEMORY_DIR_DEFAULT = join14(homedir12(), ".agenticmail", "agent-memory");
|
|
7759
7849
|
var AgentMemoryStore = class {
|
|
7760
7850
|
dir;
|
|
7761
7851
|
constructor(opts = {}) {
|
|
7762
7852
|
this.dir = opts.memoryDir ?? MEMORY_DIR_DEFAULT;
|
|
7763
7853
|
try {
|
|
7764
|
-
|
|
7854
|
+
mkdirSync10(this.dir, { recursive: true });
|
|
7765
7855
|
} catch {
|
|
7766
7856
|
}
|
|
7767
7857
|
}
|
|
7768
7858
|
dirFor(agentId) {
|
|
7769
|
-
return
|
|
7859
|
+
return join14(this.dir, sanitizeId(agentId));
|
|
7770
7860
|
}
|
|
7771
7861
|
pathFor(agentId, threadId) {
|
|
7772
|
-
return
|
|
7862
|
+
return join14(this.dirFor(agentId), `${sanitizeId(threadId)}.md`);
|
|
7773
7863
|
}
|
|
7774
7864
|
read(agentId, threadId) {
|
|
7775
7865
|
const p = this.pathFor(agentId, threadId);
|
|
7776
|
-
if (!
|
|
7866
|
+
if (!existsSync11(p)) return null;
|
|
7777
7867
|
try {
|
|
7778
|
-
const raw =
|
|
7868
|
+
const raw = readFileSync8(p, "utf-8");
|
|
7779
7869
|
const parsed = parse(raw);
|
|
7780
7870
|
return { ...parsed, raw };
|
|
7781
7871
|
} catch {
|
|
@@ -7785,14 +7875,14 @@ var AgentMemoryStore = class {
|
|
|
7785
7875
|
write(agentId, threadId, fields) {
|
|
7786
7876
|
const agentDir = this.dirFor(agentId);
|
|
7787
7877
|
try {
|
|
7788
|
-
|
|
7878
|
+
mkdirSync10(agentDir, { recursive: true });
|
|
7789
7879
|
} catch {
|
|
7790
7880
|
}
|
|
7791
7881
|
const body = render({ ...fields, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7792
7882
|
const p = this.pathFor(agentId, threadId);
|
|
7793
7883
|
const tmp = `${p}.tmp`;
|
|
7794
|
-
|
|
7795
|
-
|
|
7884
|
+
writeFileSync9(tmp, body, "utf-8");
|
|
7885
|
+
renameSync4(tmp, p);
|
|
7796
7886
|
}
|
|
7797
7887
|
delete(agentId, threadId) {
|
|
7798
7888
|
try {
|
|
@@ -7899,6 +7989,7 @@ export {
|
|
|
7899
7989
|
flushTelemetry,
|
|
7900
7990
|
forgetHostSession,
|
|
7901
7991
|
getDatabase,
|
|
7992
|
+
getOperatorEmail,
|
|
7902
7993
|
hostSessionStoragePath,
|
|
7903
7994
|
isInternalEmail,
|
|
7904
7995
|
isSessionFresh,
|
|
@@ -7907,6 +7998,7 @@ export {
|
|
|
7907
7998
|
normalizeAddress,
|
|
7908
7999
|
normalizePhoneNumber,
|
|
7909
8000
|
normalizeSubject,
|
|
8001
|
+
operatorPrefsStoragePath,
|
|
7910
8002
|
parseEmail,
|
|
7911
8003
|
parseGoogleVoiceSms,
|
|
7912
8004
|
recordToolCall,
|
|
@@ -7919,6 +8011,7 @@ export {
|
|
|
7919
8011
|
saveHostSession,
|
|
7920
8012
|
scanOutboundEmail,
|
|
7921
8013
|
scoreEmail,
|
|
8014
|
+
setOperatorEmail,
|
|
7922
8015
|
setTelemetryVersion,
|
|
7923
8016
|
startRelayBridge,
|
|
7924
8017
|
threadIdFor,
|