@agenticmail/core 0.9.6 → 0.9.7
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/dist/index.cjs +310 -222
- package/dist/index.d.cts +69 -1
- package/dist/index.d.ts +69 -1
- package/dist/index.js +297 -212
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -390,8 +390,8 @@ var MailReceiver = class {
|
|
|
390
390
|
}));
|
|
391
391
|
}
|
|
392
392
|
/** Create a new IMAP folder */
|
|
393
|
-
async createFolder(
|
|
394
|
-
await this.client.mailboxCreate(
|
|
393
|
+
async createFolder(path2) {
|
|
394
|
+
await this.client.mailboxCreate(path2);
|
|
395
395
|
}
|
|
396
396
|
/** Batch mark multiple messages as seen */
|
|
397
397
|
async batchMarkSeen(uids, mailbox = "INBOX") {
|
|
@@ -931,8 +931,8 @@ var StalwartAdmin = class {
|
|
|
931
931
|
}
|
|
932
932
|
baseUrl;
|
|
933
933
|
authHeader;
|
|
934
|
-
async request(method,
|
|
935
|
-
const url = `${this.baseUrl}/api${
|
|
934
|
+
async request(method, path2, body) {
|
|
935
|
+
const url = `${this.baseUrl}/api${path2}`;
|
|
936
936
|
const response = await fetch(url, {
|
|
937
937
|
method,
|
|
938
938
|
headers: {
|
|
@@ -1093,14 +1093,14 @@ var StalwartAdmin = class {
|
|
|
1093
1093
|
if (!isValidDomain(domain)) {
|
|
1094
1094
|
throw new Error(`Invalid domain format: "${domain}"`);
|
|
1095
1095
|
}
|
|
1096
|
-
const { readFileSync:
|
|
1097
|
-
const { homedir:
|
|
1098
|
-
const { join:
|
|
1099
|
-
const configPath =
|
|
1096
|
+
const { readFileSync: readFileSync9, writeFileSync: writeFileSync10 } = await import("fs");
|
|
1097
|
+
const { homedir: homedir13 } = await import("os");
|
|
1098
|
+
const { join: join15 } = await import("path");
|
|
1099
|
+
const configPath = join15(homedir13(), ".agenticmail", "stalwart.toml");
|
|
1100
1100
|
try {
|
|
1101
|
-
let config =
|
|
1101
|
+
let config = readFileSync9(configPath, "utf-8");
|
|
1102
1102
|
config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
|
|
1103
|
-
|
|
1103
|
+
writeFileSync10(configPath, config);
|
|
1104
1104
|
console.log(`[Stalwart] Updated hostname to "${domain}" in stalwart.toml`);
|
|
1105
1105
|
} catch (err) {
|
|
1106
1106
|
throw new Error(`Failed to set config server.hostname=${domain}`);
|
|
@@ -1109,15 +1109,15 @@ var StalwartAdmin = class {
|
|
|
1109
1109
|
// --- DKIM ---
|
|
1110
1110
|
/** Path to the host-side stalwart.toml (mounted read-only into container) */
|
|
1111
1111
|
get configPath() {
|
|
1112
|
-
const { homedir:
|
|
1113
|
-
const { join:
|
|
1114
|
-
return
|
|
1112
|
+
const { homedir: homedir13 } = __require("os");
|
|
1113
|
+
const { join: join15 } = __require("path");
|
|
1114
|
+
return join15(homedir13(), ".agenticmail", "stalwart.toml");
|
|
1115
1115
|
}
|
|
1116
1116
|
/** Path to host-side DKIM key directory */
|
|
1117
1117
|
get dkimDir() {
|
|
1118
|
-
const { homedir:
|
|
1119
|
-
const { join:
|
|
1120
|
-
return
|
|
1118
|
+
const { homedir: homedir13 } = __require("os");
|
|
1119
|
+
const { join: join15 } = __require("path");
|
|
1120
|
+
return join15(homedir13(), ".agenticmail");
|
|
1121
1121
|
}
|
|
1122
1122
|
/**
|
|
1123
1123
|
* Create/reuse a DKIM signing key for a domain.
|
|
@@ -1218,12 +1218,12 @@ var StalwartAdmin = class {
|
|
|
1218
1218
|
* This bypasses the need for a PTR record on the sending IP.
|
|
1219
1219
|
*/
|
|
1220
1220
|
async configureOutboundRelay(config) {
|
|
1221
|
-
const { readFileSync:
|
|
1222
|
-
const { homedir:
|
|
1223
|
-
const { join:
|
|
1221
|
+
const { readFileSync: readFileSync9, writeFileSync: writeFileSync10 } = await import("fs");
|
|
1222
|
+
const { homedir: homedir13 } = await import("os");
|
|
1223
|
+
const { join: join15 } = await import("path");
|
|
1224
1224
|
const routeName = config.routeName ?? "gmail";
|
|
1225
|
-
const tomlPath =
|
|
1226
|
-
let toml =
|
|
1225
|
+
const tomlPath = join15(homedir13(), ".agenticmail", "stalwart.toml");
|
|
1226
|
+
let toml = readFileSync9(tomlPath, "utf-8");
|
|
1227
1227
|
toml = toml.replace(/\n\[queue\.route\.gmail\][\s\S]*?(?=\n\[|$)/, "");
|
|
1228
1228
|
toml = toml.replace(/\n\[queue\.strategy\][\s\S]*?(?=\n\[|$)/, "");
|
|
1229
1229
|
const safeRouteName = routeName.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
@@ -1243,7 +1243,7 @@ auth.secret = "${escapeTomlString(config.password)}"
|
|
|
1243
1243
|
route = [ { if = "is_local_domain('', rcpt_domain)", then = "'local'" },
|
|
1244
1244
|
{ else = "'${safeRouteName}'" } ]
|
|
1245
1245
|
`;
|
|
1246
|
-
|
|
1246
|
+
writeFileSync10(tomlPath, toml, "utf-8");
|
|
1247
1247
|
await this.restartContainer();
|
|
1248
1248
|
}
|
|
1249
1249
|
};
|
|
@@ -1626,11 +1626,11 @@ var AgentDeletionService = class {
|
|
|
1626
1626
|
};
|
|
1627
1627
|
}
|
|
1628
1628
|
saveToFile(report) {
|
|
1629
|
-
const
|
|
1630
|
-
mkdirSync2(
|
|
1629
|
+
const dir2 = join2(homedir2(), ".agenticmail", "deletions");
|
|
1630
|
+
mkdirSync2(dir2, { recursive: true });
|
|
1631
1631
|
const timestamp = report.deletedAt.replace(/[:.]/g, "-");
|
|
1632
1632
|
const filename = `${report.agent.name}_${timestamp}.json`;
|
|
1633
|
-
const filePath = join2(
|
|
1633
|
+
const filePath = join2(dir2, filename);
|
|
1634
1634
|
writeFileSync2(filePath, JSON.stringify(report, null, 2), "utf-8");
|
|
1635
1635
|
return filePath;
|
|
1636
1636
|
}
|
|
@@ -3917,8 +3917,8 @@ var CloudflareClient = class {
|
|
|
3917
3917
|
return resp.result;
|
|
3918
3918
|
}
|
|
3919
3919
|
// --- Internal ---
|
|
3920
|
-
async request(method,
|
|
3921
|
-
const url = `${CF_API_BASE}${
|
|
3920
|
+
async request(method, path2, body) {
|
|
3921
|
+
const url = `${CF_API_BASE}${path2}`;
|
|
3922
3922
|
const response = await fetch(url, {
|
|
3923
3923
|
method,
|
|
3924
3924
|
headers: {
|
|
@@ -4240,9 +4240,9 @@ var TunnelManager = class {
|
|
|
4240
4240
|
throw new Error(`Failed to download cloudflared: ${response.statusText}`);
|
|
4241
4241
|
}
|
|
4242
4242
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
4243
|
-
const { writeFile:
|
|
4243
|
+
const { writeFile: writeFile4, rename: rename2 } = await import("fs/promises");
|
|
4244
4244
|
const tmpPath = this.binPath + ".tmp";
|
|
4245
|
-
await
|
|
4245
|
+
await writeFile4(tmpPath, buffer);
|
|
4246
4246
|
await chmod(tmpPath, 493);
|
|
4247
4247
|
await rename2(tmpPath, this.binPath);
|
|
4248
4248
|
return this.binPath;
|
|
@@ -4534,6 +4534,33 @@ var SmsManager = class {
|
|
|
4534
4534
|
meta.sms = config;
|
|
4535
4535
|
this.db.prepare("UPDATE agents SET metadata = ?, updated_at = datetime('now') WHERE id = ?").run(JSON.stringify(meta), agentId);
|
|
4536
4536
|
}
|
|
4537
|
+
/**
|
|
4538
|
+
* Resolve the operator's "where do I get pinged" address from an
|
|
4539
|
+
* agent's SMS config. Used by the dispatcher's bridge-escalation
|
|
4540
|
+
* path: when sub-agents mail a bridge with no fresh host session
|
|
4541
|
+
* available, we email the operator a digest at this address. Their
|
|
4542
|
+
* phone's Gmail push notification surfaces it within seconds —
|
|
4543
|
+
* effectively a free, programmatic alert channel.
|
|
4544
|
+
*
|
|
4545
|
+
* Returns the configured `forwardingEmail` (the same Gmail Google
|
|
4546
|
+
* Voice forwards inbound SMS to, which the operator already has
|
|
4547
|
+
* push notifications enabled for) when SMS is configured AND
|
|
4548
|
+
* enabled. Returns null otherwise — caller falls through to a
|
|
4549
|
+
* silent log + system event.
|
|
4550
|
+
*
|
|
4551
|
+
* Why we don't try real-SMS delivery yet: Google Voice's
|
|
4552
|
+
* `<number>@txt.voice.google.com` email-to-SMS gateway was
|
|
4553
|
+
* deprecated by Google years ago. A future `carrier` field on
|
|
4554
|
+
* SmsConfig (Verizon vtext.com / AT&T txt.att.net / etc) will let
|
|
4555
|
+
* the operator opt into actual SMS, but that's a follow-up — the
|
|
4556
|
+
* email path already gets the operator a phone notification.
|
|
4557
|
+
*/
|
|
4558
|
+
getAlertEmail(agentId) {
|
|
4559
|
+
const cfg = this.getSmsConfig(agentId);
|
|
4560
|
+
if (!cfg || !cfg.enabled) return null;
|
|
4561
|
+
if (typeof cfg.forwardingEmail !== "string" || !cfg.forwardingEmail.includes("@")) return null;
|
|
4562
|
+
return cfg.forwardingEmail;
|
|
4563
|
+
}
|
|
4537
4564
|
/** Remove SMS config from agent metadata */
|
|
4538
4565
|
removeSmsConfig(agentId) {
|
|
4539
4566
|
const row = this.db.prepare("SELECT metadata FROM agents WHERE id = ?").get(agentId);
|
|
@@ -5149,12 +5176,12 @@ var GatewayManager = class {
|
|
|
5149
5176
|
zone = await this.cfClient.createZone(domain);
|
|
5150
5177
|
}
|
|
5151
5178
|
const existingRecords = await this.cfClient.listDnsRecords(zone.id);
|
|
5152
|
-
const { homedir:
|
|
5153
|
-
const backupDir = join4(
|
|
5179
|
+
const { homedir: homedir13 } = await import("os");
|
|
5180
|
+
const backupDir = join4(homedir13(), ".agenticmail");
|
|
5154
5181
|
const backupPath = join4(backupDir, `dns-backup-${domain}-${Date.now()}.json`);
|
|
5155
|
-
const { writeFileSync:
|
|
5156
|
-
|
|
5157
|
-
|
|
5182
|
+
const { writeFileSync: writeFileSync10, mkdirSync: mkdirSync11 } = await import("fs");
|
|
5183
|
+
mkdirSync11(backupDir, { recursive: true });
|
|
5184
|
+
writeFileSync10(backupPath, JSON.stringify({
|
|
5158
5185
|
domain,
|
|
5159
5186
|
zoneId: zone.id,
|
|
5160
5187
|
backedUpAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5915,8 +5942,8 @@ function isDisabled() {
|
|
|
5915
5942
|
function getInstallId() {
|
|
5916
5943
|
if (installId) return installId;
|
|
5917
5944
|
try {
|
|
5918
|
-
const
|
|
5919
|
-
const idFile = join5(
|
|
5945
|
+
const dir2 = join5(homedir4(), ".agenticmail");
|
|
5946
|
+
const idFile = join5(dir2, ".telemetry-id");
|
|
5920
5947
|
if (existsSync3(idFile)) {
|
|
5921
5948
|
const id = readFileSync2(idFile, "utf8").trim();
|
|
5922
5949
|
if (id && id.length > 10) {
|
|
@@ -5925,7 +5952,7 @@ function getInstallId() {
|
|
|
5925
5952
|
}
|
|
5926
5953
|
}
|
|
5927
5954
|
installId = randomUUID();
|
|
5928
|
-
if (!existsSync3(
|
|
5955
|
+
if (!existsSync3(dir2)) mkdirSync3(dir2, { recursive: true });
|
|
5929
5956
|
writeFileSync3(idFile, installId, "utf8");
|
|
5930
5957
|
return installId;
|
|
5931
5958
|
} catch {
|
|
@@ -6096,22 +6123,77 @@ function redactObject(input, _depth = 0) {
|
|
|
6096
6123
|
return out;
|
|
6097
6124
|
}
|
|
6098
6125
|
|
|
6099
|
-
// src/
|
|
6126
|
+
// src/operator-prefs.ts
|
|
6100
6127
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
6101
|
-
import { join as join7 } from "path";
|
|
6102
6128
|
import { homedir as homedir5 } from "os";
|
|
6103
|
-
|
|
6129
|
+
import { join as join7 } from "path";
|
|
6130
|
+
function dir() {
|
|
6104
6131
|
return join7(homedir5(), ".agenticmail");
|
|
6105
6132
|
}
|
|
6133
|
+
function path() {
|
|
6134
|
+
return join7(dir(), "operator-prefs.json");
|
|
6135
|
+
}
|
|
6136
|
+
function readFile() {
|
|
6137
|
+
if (!existsSync4(path())) return { version: 1 };
|
|
6138
|
+
try {
|
|
6139
|
+
const raw = readFileSync3(path(), "utf-8");
|
|
6140
|
+
if (!raw.trim()) return { version: 1 };
|
|
6141
|
+
const parsed = JSON.parse(raw);
|
|
6142
|
+
return { version: 1, operatorEmail: typeof parsed.operatorEmail === "string" ? parsed.operatorEmail : void 0 };
|
|
6143
|
+
} catch {
|
|
6144
|
+
return { version: 1 };
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
function writeFile(shape) {
|
|
6148
|
+
const d = dir();
|
|
6149
|
+
const p = path();
|
|
6150
|
+
if (!existsSync4(d)) mkdirSync4(d, { recursive: true });
|
|
6151
|
+
const tmp = `${p}.agenticmail-tmp-${process.pid}`;
|
|
6152
|
+
writeFileSync4(tmp, JSON.stringify(shape, null, 2), "utf-8");
|
|
6153
|
+
renameSync(tmp, p);
|
|
6154
|
+
}
|
|
6155
|
+
function getOperatorEmail() {
|
|
6156
|
+
const shape = readFile();
|
|
6157
|
+
const email = shape.operatorEmail;
|
|
6158
|
+
if (typeof email !== "string") return null;
|
|
6159
|
+
const trimmed = email.trim();
|
|
6160
|
+
return trimmed.length > 0 && trimmed.includes("@") ? trimmed : null;
|
|
6161
|
+
}
|
|
6162
|
+
function setOperatorEmail(email) {
|
|
6163
|
+
const shape = readFile();
|
|
6164
|
+
if (!email || !email.trim()) {
|
|
6165
|
+
delete shape.operatorEmail;
|
|
6166
|
+
writeFile(shape);
|
|
6167
|
+
return null;
|
|
6168
|
+
}
|
|
6169
|
+
const trimmed = email.trim();
|
|
6170
|
+
if (!trimmed.includes("@")) {
|
|
6171
|
+
throw new Error("operator email must contain an @");
|
|
6172
|
+
}
|
|
6173
|
+
shape.operatorEmail = trimmed;
|
|
6174
|
+
writeFile(shape);
|
|
6175
|
+
return trimmed;
|
|
6176
|
+
}
|
|
6177
|
+
function operatorPrefsStoragePath() {
|
|
6178
|
+
return path();
|
|
6179
|
+
}
|
|
6180
|
+
|
|
6181
|
+
// src/host-sessions.ts
|
|
6182
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync5, readFileSync as readFileSync4, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
6183
|
+
import { join as join8 } from "path";
|
|
6184
|
+
import { homedir as homedir6 } from "os";
|
|
6185
|
+
function storageDir() {
|
|
6186
|
+
return join8(homedir6(), ".agenticmail");
|
|
6187
|
+
}
|
|
6106
6188
|
function storagePath() {
|
|
6107
|
-
return
|
|
6189
|
+
return join8(storageDir(), "host-sessions.json");
|
|
6108
6190
|
}
|
|
6109
6191
|
var DEFAULT_SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
6110
|
-
function
|
|
6192
|
+
function readFile2() {
|
|
6111
6193
|
const p = storagePath();
|
|
6112
|
-
if (!
|
|
6194
|
+
if (!existsSync5(p)) return { version: 1, sessions: {} };
|
|
6113
6195
|
try {
|
|
6114
|
-
const raw =
|
|
6196
|
+
const raw = readFileSync4(p, "utf-8");
|
|
6115
6197
|
if (!raw.trim()) return { version: 1, sessions: {} };
|
|
6116
6198
|
const parsed = JSON.parse(raw);
|
|
6117
6199
|
return {
|
|
@@ -6122,28 +6204,28 @@ function readFile() {
|
|
|
6122
6204
|
return { version: 1, sessions: {} };
|
|
6123
6205
|
}
|
|
6124
6206
|
}
|
|
6125
|
-
function
|
|
6126
|
-
const
|
|
6207
|
+
function writeFile2(shape) {
|
|
6208
|
+
const dir2 = storageDir();
|
|
6127
6209
|
const p = storagePath();
|
|
6128
|
-
if (!
|
|
6210
|
+
if (!existsSync5(dir2)) mkdirSync5(dir2, { recursive: true });
|
|
6129
6211
|
const tmp = `${p}.agenticmail-tmp-${process.pid}`;
|
|
6130
|
-
|
|
6131
|
-
|
|
6212
|
+
writeFileSync5(tmp, JSON.stringify(shape, null, 2), "utf-8");
|
|
6213
|
+
renameSync2(tmp, p);
|
|
6132
6214
|
}
|
|
6133
6215
|
function saveHostSession(host, session) {
|
|
6134
6216
|
if (!session.sessionId) return;
|
|
6135
6217
|
try {
|
|
6136
|
-
const shape =
|
|
6218
|
+
const shape = readFile2();
|
|
6137
6219
|
shape.sessions[host] = {
|
|
6138
6220
|
...session,
|
|
6139
6221
|
lastSeenMs: Date.now()
|
|
6140
6222
|
};
|
|
6141
|
-
|
|
6223
|
+
writeFile2(shape);
|
|
6142
6224
|
} catch {
|
|
6143
6225
|
}
|
|
6144
6226
|
}
|
|
6145
6227
|
function loadHostSession(host, maxAgeMs = DEFAULT_SESSION_MAX_AGE_MS) {
|
|
6146
|
-
const shape =
|
|
6228
|
+
const shape = readFile2();
|
|
6147
6229
|
const record = shape.sessions[host];
|
|
6148
6230
|
if (!record) return null;
|
|
6149
6231
|
if (!isSessionFresh(record, maxAgeMs)) return null;
|
|
@@ -6155,10 +6237,10 @@ function isSessionFresh(session, maxAgeMs = DEFAULT_SESSION_MAX_AGE_MS) {
|
|
|
6155
6237
|
}
|
|
6156
6238
|
function forgetHostSession(host) {
|
|
6157
6239
|
try {
|
|
6158
|
-
const shape =
|
|
6240
|
+
const shape = readFile2();
|
|
6159
6241
|
if (!shape.sessions[host]) return;
|
|
6160
6242
|
delete shape.sessions[host];
|
|
6161
|
-
|
|
6243
|
+
writeFile2(shape);
|
|
6162
6244
|
} catch {
|
|
6163
6245
|
}
|
|
6164
6246
|
}
|
|
@@ -6208,21 +6290,21 @@ function validateApiUrl(raw) {
|
|
|
6208
6290
|
return parsed.origin;
|
|
6209
6291
|
}
|
|
6210
6292
|
function buildApiUrl(baseOrigin, pathAndQuery) {
|
|
6211
|
-
const
|
|
6212
|
-
return new URL(
|
|
6293
|
+
const path2 = pathAndQuery.startsWith("/") ? pathAndQuery : `/${pathAndQuery}`;
|
|
6294
|
+
return new URL(path2, baseOrigin + "/").toString();
|
|
6213
6295
|
}
|
|
6214
6296
|
|
|
6215
6297
|
// src/setup/index.ts
|
|
6216
6298
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
6217
|
-
import { existsSync as
|
|
6218
|
-
import { join as
|
|
6219
|
-
import { homedir as
|
|
6299
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync7, mkdirSync as mkdirSync8, chmodSync as chmodSync2 } from "fs";
|
|
6300
|
+
import { join as join12 } from "path";
|
|
6301
|
+
import { homedir as homedir10 } from "os";
|
|
6220
6302
|
|
|
6221
6303
|
// src/setup/deps.ts
|
|
6222
6304
|
import { execFileSync } from "child_process";
|
|
6223
|
-
import { existsSync as
|
|
6224
|
-
import { join as
|
|
6225
|
-
import { homedir as
|
|
6305
|
+
import { existsSync as existsSync6 } from "fs";
|
|
6306
|
+
import { join as join9 } from "path";
|
|
6307
|
+
import { homedir as homedir7 } from "os";
|
|
6226
6308
|
var DependencyChecker = class {
|
|
6227
6309
|
async checkAll() {
|
|
6228
6310
|
return Promise.all([
|
|
@@ -6272,8 +6354,8 @@ var DependencyChecker = class {
|
|
|
6272
6354
|
}
|
|
6273
6355
|
}
|
|
6274
6356
|
async checkCloudflared() {
|
|
6275
|
-
const binPath =
|
|
6276
|
-
if (
|
|
6357
|
+
const binPath = join9(homedir7(), ".agenticmail", "bin", "cloudflared");
|
|
6358
|
+
if (existsSync6(binPath)) {
|
|
6277
6359
|
let version;
|
|
6278
6360
|
try {
|
|
6279
6361
|
const output = execFileSync(binPath, ["--version"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
@@ -6295,10 +6377,10 @@ var DependencyChecker = class {
|
|
|
6295
6377
|
|
|
6296
6378
|
// src/setup/installer.ts
|
|
6297
6379
|
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
|
|
6380
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, symlinkSync } from "fs";
|
|
6381
|
+
import { writeFile as writeFile3, rename, chmod as chmod2, mkdir as mkdir2, unlink } from "fs/promises";
|
|
6382
|
+
import { join as join10 } from "path";
|
|
6383
|
+
import { homedir as homedir8, platform as platform3, arch as arch2 } from "os";
|
|
6302
6384
|
function runShellWithRollingOutput(cmd, opts = {}) {
|
|
6303
6385
|
const maxLines = opts.maxLines ?? 20;
|
|
6304
6386
|
const timeout = opts.timeout ?? 3e5;
|
|
@@ -6474,11 +6556,11 @@ var DependencyInstaller = class {
|
|
|
6474
6556
|
try {
|
|
6475
6557
|
const composeBin = execFileSync2("which", ["docker-compose"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6476
6558
|
if (!composeBin) return;
|
|
6477
|
-
const pluginDir =
|
|
6478
|
-
const pluginPath =
|
|
6479
|
-
if (
|
|
6559
|
+
const pluginDir = join10(homedir8(), ".docker", "cli-plugins");
|
|
6560
|
+
const pluginPath = join10(pluginDir, "docker-compose");
|
|
6561
|
+
if (existsSync7(pluginPath)) return;
|
|
6480
6562
|
try {
|
|
6481
|
-
|
|
6563
|
+
mkdirSync6(pluginDir, { recursive: true });
|
|
6482
6564
|
} catch {
|
|
6483
6565
|
}
|
|
6484
6566
|
try {
|
|
@@ -6541,9 +6623,9 @@ var DependencyInstaller = class {
|
|
|
6541
6623
|
return;
|
|
6542
6624
|
}
|
|
6543
6625
|
this.onProgress("__progress__:5:Installing Docker Engine...");
|
|
6544
|
-
const tmpDir =
|
|
6626
|
+
const tmpDir = join10(homedir8(), ".agenticmail", "tmp");
|
|
6545
6627
|
await mkdir2(tmpDir, { recursive: true });
|
|
6546
|
-
const scriptPath =
|
|
6628
|
+
const scriptPath = join10(tmpDir, "install-docker.sh");
|
|
6547
6629
|
const dlResult = await runShellWithRollingOutput(
|
|
6548
6630
|
`curl -fsSL https://get.docker.com -o "${scriptPath}" && sudo sh "${scriptPath}"`,
|
|
6549
6631
|
{ timeout: 3e5 }
|
|
@@ -6611,8 +6693,8 @@ var DependencyInstaller = class {
|
|
|
6611
6693
|
}
|
|
6612
6694
|
try {
|
|
6613
6695
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
6614
|
-
const dockerExe =
|
|
6615
|
-
if (
|
|
6696
|
+
const dockerExe = join10(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
6697
|
+
if (existsSync7(dockerExe)) {
|
|
6616
6698
|
execSync(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
6617
6699
|
}
|
|
6618
6700
|
} catch {
|
|
@@ -6662,8 +6744,8 @@ var DependencyInstaller = class {
|
|
|
6662
6744
|
this.onProgress("__progress__:70:Docker Desktop installed. Starting...");
|
|
6663
6745
|
try {
|
|
6664
6746
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
6665
|
-
const dockerExe =
|
|
6666
|
-
if (
|
|
6747
|
+
const dockerExe = join10(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
6748
|
+
if (existsSync7(dockerExe)) {
|
|
6667
6749
|
execSync(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
6668
6750
|
}
|
|
6669
6751
|
} catch {
|
|
@@ -6700,7 +6782,7 @@ var DependencyInstaller = class {
|
|
|
6700
6782
|
* Start the Stalwart mail server Docker container.
|
|
6701
6783
|
*/
|
|
6702
6784
|
async startStalwart(composePath) {
|
|
6703
|
-
if (!
|
|
6785
|
+
if (!existsSync7(composePath)) {
|
|
6704
6786
|
throw new Error(`docker-compose.yml not found at: ${composePath}`);
|
|
6705
6787
|
}
|
|
6706
6788
|
if (!this.isDockerReady()) {
|
|
@@ -6759,16 +6841,16 @@ var DependencyInstaller = class {
|
|
|
6759
6841
|
*/
|
|
6760
6842
|
async installCloudflared() {
|
|
6761
6843
|
const os = platform3();
|
|
6762
|
-
const binDir =
|
|
6844
|
+
const binDir = join10(homedir8(), ".agenticmail", "bin");
|
|
6763
6845
|
const binName = os === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
6764
|
-
const binPath =
|
|
6765
|
-
if (
|
|
6846
|
+
const binPath = join10(binDir, binName);
|
|
6847
|
+
if (existsSync7(binPath)) {
|
|
6766
6848
|
return binPath;
|
|
6767
6849
|
}
|
|
6768
6850
|
try {
|
|
6769
6851
|
const whichCmd = os === "win32" ? "where" : "which";
|
|
6770
6852
|
const sysPath = execFileSync2(whichCmd, ["cloudflared"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim().split("\n")[0];
|
|
6771
|
-
if (sysPath &&
|
|
6853
|
+
if (sysPath && existsSync7(sysPath)) return sysPath;
|
|
6772
6854
|
} catch {
|
|
6773
6855
|
}
|
|
6774
6856
|
this.onProgress("Downloading cloudflared...");
|
|
@@ -6791,8 +6873,8 @@ var DependencyInstaller = class {
|
|
|
6791
6873
|
}
|
|
6792
6874
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
6793
6875
|
if (os === "darwin") {
|
|
6794
|
-
const tgzPath =
|
|
6795
|
-
await
|
|
6876
|
+
const tgzPath = join10(binDir, "cloudflared.tgz");
|
|
6877
|
+
await writeFile3(tgzPath, buffer);
|
|
6796
6878
|
try {
|
|
6797
6879
|
execFileSync2("tar", ["-xzf", tgzPath, "-C", binDir, "cloudflared"], { timeout: 15e3, stdio: "ignore" });
|
|
6798
6880
|
await chmod2(binPath, 493);
|
|
@@ -6804,11 +6886,11 @@ var DependencyInstaller = class {
|
|
|
6804
6886
|
}
|
|
6805
6887
|
} else {
|
|
6806
6888
|
const tmpPath = binPath + ".tmp";
|
|
6807
|
-
await
|
|
6889
|
+
await writeFile3(tmpPath, buffer);
|
|
6808
6890
|
if (os !== "win32") await chmod2(tmpPath, 493);
|
|
6809
6891
|
await rename(tmpPath, binPath);
|
|
6810
6892
|
}
|
|
6811
|
-
if (!
|
|
6893
|
+
if (!existsSync7(binPath)) {
|
|
6812
6894
|
throw new Error("cloudflared download succeeded but binary not found after extraction");
|
|
6813
6895
|
}
|
|
6814
6896
|
this.onProgress("cloudflared installed");
|
|
@@ -6826,9 +6908,9 @@ var DependencyInstaller = class {
|
|
|
6826
6908
|
|
|
6827
6909
|
// src/setup/service.ts
|
|
6828
6910
|
import { execFileSync as execFileSync3, execSync as execSync2 } from "child_process";
|
|
6829
|
-
import { existsSync as
|
|
6830
|
-
import { join as
|
|
6831
|
-
import { homedir as
|
|
6911
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync6, unlinkSync, mkdirSync as mkdirSync7, chmodSync } from "fs";
|
|
6912
|
+
import { join as join11 } from "path";
|
|
6913
|
+
import { homedir as homedir9, platform as platform4 } from "os";
|
|
6832
6914
|
import { createRequire as createRequire2 } from "module";
|
|
6833
6915
|
var PLIST_LABEL = "com.agenticmail.server";
|
|
6834
6916
|
var SYSTEMD_UNIT = "agenticmail.service";
|
|
@@ -6839,9 +6921,9 @@ var ServiceManager = class {
|
|
|
6839
6921
|
*/
|
|
6840
6922
|
getServicePath() {
|
|
6841
6923
|
if (this.os === "darwin") {
|
|
6842
|
-
return
|
|
6924
|
+
return join11(homedir9(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
6843
6925
|
} else {
|
|
6844
|
-
return
|
|
6926
|
+
return join11(homedir9(), ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
6845
6927
|
}
|
|
6846
6928
|
}
|
|
6847
6929
|
/**
|
|
@@ -6875,40 +6957,40 @@ var ServiceManager = class {
|
|
|
6875
6957
|
try {
|
|
6876
6958
|
const req = createRequire2(import.meta.url);
|
|
6877
6959
|
const resolved = req.resolve("@agenticmail/api");
|
|
6878
|
-
if (
|
|
6960
|
+
if (existsSync8(resolved)) return resolved;
|
|
6879
6961
|
} catch {
|
|
6880
6962
|
}
|
|
6881
6963
|
const parentPackages = [
|
|
6882
|
-
|
|
6964
|
+
join11("@agenticmail", "cli"),
|
|
6883
6965
|
// current scoped package
|
|
6884
6966
|
"agenticmail"
|
|
6885
6967
|
// legacy unscoped package
|
|
6886
6968
|
];
|
|
6887
6969
|
const baseDirs = [
|
|
6888
6970
|
// user-local install
|
|
6889
|
-
|
|
6971
|
+
join11(homedir9(), "node_modules")
|
|
6890
6972
|
];
|
|
6891
6973
|
try {
|
|
6892
6974
|
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6893
|
-
baseDirs.push(
|
|
6894
|
-
baseDirs.push(
|
|
6975
|
+
baseDirs.push(join11(prefix, "lib", "node_modules"));
|
|
6976
|
+
baseDirs.push(join11(prefix, "node_modules"));
|
|
6895
6977
|
} catch {
|
|
6896
6978
|
}
|
|
6897
6979
|
baseDirs.push("/opt/homebrew/lib/node_modules");
|
|
6898
6980
|
baseDirs.push("/usr/local/lib/node_modules");
|
|
6899
6981
|
for (const base of baseDirs) {
|
|
6900
|
-
const sibling =
|
|
6901
|
-
if (
|
|
6982
|
+
const sibling = join11(base, "@agenticmail", "api", "dist", "index.js");
|
|
6983
|
+
if (existsSync8(sibling)) return sibling;
|
|
6902
6984
|
for (const parent of parentPackages) {
|
|
6903
|
-
const nested =
|
|
6904
|
-
if (
|
|
6985
|
+
const nested = join11(base, parent, "node_modules", "@agenticmail", "api", "dist", "index.js");
|
|
6986
|
+
if (existsSync8(nested)) return nested;
|
|
6905
6987
|
}
|
|
6906
6988
|
}
|
|
6907
|
-
const dataDir =
|
|
6908
|
-
const entryCache =
|
|
6909
|
-
if (
|
|
6910
|
-
const cached =
|
|
6911
|
-
if (cached &&
|
|
6989
|
+
const dataDir = join11(homedir9(), ".agenticmail");
|
|
6990
|
+
const entryCache = join11(dataDir, "api-entry.path");
|
|
6991
|
+
if (existsSync8(entryCache)) {
|
|
6992
|
+
const cached = readFileSync5(entryCache, "utf-8").trim();
|
|
6993
|
+
if (cached && existsSync8(cached)) return cached;
|
|
6912
6994
|
}
|
|
6913
6995
|
throw new Error("Could not find @agenticmail/api entry point. Run `agenticmail start` first to populate the cache.");
|
|
6914
6996
|
}
|
|
@@ -6916,9 +6998,9 @@ var ServiceManager = class {
|
|
|
6916
6998
|
* Cache the API entry path so the service can find it later.
|
|
6917
6999
|
*/
|
|
6918
7000
|
cacheApiEntryPath(entryPath) {
|
|
6919
|
-
const dataDir =
|
|
6920
|
-
if (!
|
|
6921
|
-
|
|
7001
|
+
const dataDir = join11(homedir9(), ".agenticmail");
|
|
7002
|
+
if (!existsSync8(dataDir)) mkdirSync7(dataDir, { recursive: true });
|
|
7003
|
+
writeFileSync6(join11(dataDir, "api-entry.path"), entryPath);
|
|
6922
7004
|
}
|
|
6923
7005
|
/**
|
|
6924
7006
|
* Get the current package version.
|
|
@@ -6931,36 +7013,36 @@ var ServiceManager = class {
|
|
|
6931
7013
|
try {
|
|
6932
7014
|
const req = createRequire2(import.meta.url);
|
|
6933
7015
|
const pkgJson = req.resolve("@agenticmail/cli/package.json");
|
|
6934
|
-
if (
|
|
6935
|
-
const pkg = JSON.parse(
|
|
7016
|
+
if (existsSync8(pkgJson)) {
|
|
7017
|
+
const pkg = JSON.parse(readFileSync5(pkgJson, "utf-8"));
|
|
6936
7018
|
if (pkg.version) return pkg.version;
|
|
6937
7019
|
}
|
|
6938
7020
|
} catch {
|
|
6939
7021
|
}
|
|
6940
7022
|
try {
|
|
6941
7023
|
const apiEntry = this.getApiEntryPath();
|
|
6942
|
-
const apiPkg =
|
|
6943
|
-
if (
|
|
6944
|
-
const pkg = JSON.parse(
|
|
7024
|
+
const apiPkg = join11(apiEntry, "..", "..", "package.json");
|
|
7025
|
+
if (existsSync8(apiPkg)) {
|
|
7026
|
+
const pkg = JSON.parse(readFileSync5(apiPkg, "utf-8"));
|
|
6945
7027
|
if (pkg.version) return pkg.version;
|
|
6946
7028
|
}
|
|
6947
7029
|
} catch {
|
|
6948
7030
|
}
|
|
6949
7031
|
const candidates = [
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
7032
|
+
join11(homedir9(), "node_modules", "@agenticmail", "cli", "package.json"),
|
|
7033
|
+
join11(homedir9(), "node_modules", "agenticmail", "package.json"),
|
|
7034
|
+
join11(homedir9(), ".agenticmail", "package-version.json")
|
|
6953
7035
|
];
|
|
6954
7036
|
try {
|
|
6955
7037
|
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6956
|
-
candidates.push(
|
|
6957
|
-
candidates.push(
|
|
7038
|
+
candidates.push(join11(prefix, "lib", "node_modules", "@agenticmail", "cli", "package.json"));
|
|
7039
|
+
candidates.push(join11(prefix, "lib", "node_modules", "agenticmail", "package.json"));
|
|
6958
7040
|
} catch {
|
|
6959
7041
|
}
|
|
6960
7042
|
for (const p of candidates) {
|
|
6961
7043
|
try {
|
|
6962
|
-
if (
|
|
6963
|
-
const pkg = JSON.parse(
|
|
7044
|
+
if (existsSync8(p)) {
|
|
7045
|
+
const pkg = JSON.parse(readFileSync5(p, "utf-8"));
|
|
6964
7046
|
if (pkg.version) return pkg.version;
|
|
6965
7047
|
}
|
|
6966
7048
|
} catch {
|
|
@@ -6973,9 +7055,9 @@ var ServiceManager = class {
|
|
|
6973
7055
|
* This ensures AgenticMail doesn't fail on boot when Docker is still loading.
|
|
6974
7056
|
*/
|
|
6975
7057
|
generateStartScript(nodePath, apiEntry) {
|
|
6976
|
-
const scriptPath =
|
|
6977
|
-
const scriptDir =
|
|
6978
|
-
if (!
|
|
7058
|
+
const scriptPath = join11(homedir9(), ".agenticmail", "bin", "start-server.sh");
|
|
7059
|
+
const scriptDir = join11(homedir9(), ".agenticmail", "bin");
|
|
7060
|
+
if (!existsSync8(scriptDir)) mkdirSync7(scriptDir, { recursive: true });
|
|
6979
7061
|
const script = [
|
|
6980
7062
|
"#!/bin/bash",
|
|
6981
7063
|
"# AgenticMail auto-start script",
|
|
@@ -7026,7 +7108,7 @@ var ServiceManager = class {
|
|
|
7026
7108
|
`log "Starting API server: ${nodePath} ${apiEntry}"`,
|
|
7027
7109
|
`exec "${nodePath}" "${apiEntry}"`
|
|
7028
7110
|
].join("\n") + "\n";
|
|
7029
|
-
|
|
7111
|
+
writeFileSync6(scriptPath, script, { mode: 493 });
|
|
7030
7112
|
return scriptPath;
|
|
7031
7113
|
}
|
|
7032
7114
|
/**
|
|
@@ -7039,9 +7121,9 @@ var ServiceManager = class {
|
|
|
7039
7121
|
* - Service version tracking in env vars
|
|
7040
7122
|
*/
|
|
7041
7123
|
generatePlist(nodePath, apiEntry, configPath) {
|
|
7042
|
-
const config = JSON.parse(
|
|
7043
|
-
const logDir =
|
|
7044
|
-
if (!
|
|
7124
|
+
const config = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
7125
|
+
const logDir = join11(homedir9(), ".agenticmail", "logs");
|
|
7126
|
+
if (!existsSync8(logDir)) mkdirSync7(logDir, { recursive: true });
|
|
7045
7127
|
const version = this.getVersion();
|
|
7046
7128
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
7047
7129
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -7062,9 +7144,9 @@ var ServiceManager = class {
|
|
|
7062
7144
|
<key>EnvironmentVariables</key>
|
|
7063
7145
|
<dict>
|
|
7064
7146
|
<key>HOME</key>
|
|
7065
|
-
<string>${
|
|
7147
|
+
<string>${homedir9()}</string>
|
|
7066
7148
|
<key>AGENTICMAIL_DATA_DIR</key>
|
|
7067
|
-
<string>${config.dataDir ||
|
|
7149
|
+
<string>${config.dataDir || join11(homedir9(), ".agenticmail")}</string>
|
|
7068
7150
|
<key>PATH</key>
|
|
7069
7151
|
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
7070
7152
|
<key>AGENTICMAIL_SERVICE_VERSION</key>
|
|
@@ -7117,8 +7199,8 @@ var ServiceManager = class {
|
|
|
7117
7199
|
* - Proper dependency ordering
|
|
7118
7200
|
*/
|
|
7119
7201
|
generateSystemdUnit(nodePath, apiEntry, configPath) {
|
|
7120
|
-
const config = JSON.parse(
|
|
7121
|
-
const dataDir = config.dataDir ||
|
|
7202
|
+
const config = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
7203
|
+
const dataDir = config.dataDir || join11(homedir9(), ".agenticmail");
|
|
7122
7204
|
const version = this.getVersion();
|
|
7123
7205
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
7124
7206
|
return `[Unit]
|
|
@@ -7135,7 +7217,7 @@ Restart=always
|
|
|
7135
7217
|
RestartSec=15
|
|
7136
7218
|
TimeoutStartSec=660
|
|
7137
7219
|
LimitNOFILE=8192
|
|
7138
|
-
Environment=HOME=${
|
|
7220
|
+
Environment=HOME=${homedir9()}
|
|
7139
7221
|
Environment=AGENTICMAIL_DATA_DIR=${dataDir}
|
|
7140
7222
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin
|
|
7141
7223
|
Environment=AGENTICMAIL_SERVICE_VERSION=${version}
|
|
@@ -7148,8 +7230,8 @@ WantedBy=default.target
|
|
|
7148
7230
|
* Install the auto-start service.
|
|
7149
7231
|
*/
|
|
7150
7232
|
install() {
|
|
7151
|
-
const configPath =
|
|
7152
|
-
if (!
|
|
7233
|
+
const configPath = join11(homedir9(), ".agenticmail", "config.json");
|
|
7234
|
+
if (!existsSync8(configPath)) {
|
|
7153
7235
|
return { installed: false, message: "Config not found. Run agenticmail setup first." };
|
|
7154
7236
|
}
|
|
7155
7237
|
const nodePath = this.getNodePath();
|
|
@@ -7161,16 +7243,16 @@ WantedBy=default.target
|
|
|
7161
7243
|
}
|
|
7162
7244
|
const servicePath = this.getServicePath();
|
|
7163
7245
|
if (this.os === "darwin") {
|
|
7164
|
-
const
|
|
7165
|
-
if (!
|
|
7166
|
-
if (
|
|
7246
|
+
const dir2 = join11(homedir9(), "Library", "LaunchAgents");
|
|
7247
|
+
if (!existsSync8(dir2)) mkdirSync7(dir2, { recursive: true });
|
|
7248
|
+
if (existsSync8(servicePath)) {
|
|
7167
7249
|
try {
|
|
7168
7250
|
execFileSync3("launchctl", ["unload", servicePath], { timeout: 1e4, stdio: "ignore" });
|
|
7169
7251
|
} catch {
|
|
7170
7252
|
}
|
|
7171
7253
|
}
|
|
7172
7254
|
const plist = this.generatePlist(nodePath, apiEntry, configPath);
|
|
7173
|
-
|
|
7255
|
+
writeFileSync6(servicePath, plist);
|
|
7174
7256
|
chmodSync(servicePath, 384);
|
|
7175
7257
|
try {
|
|
7176
7258
|
execFileSync3("launchctl", ["load", servicePath], { timeout: 1e4, stdio: "ignore" });
|
|
@@ -7179,10 +7261,10 @@ WantedBy=default.target
|
|
|
7179
7261
|
}
|
|
7180
7262
|
return { installed: true, message: `Service installed at ${servicePath}` };
|
|
7181
7263
|
} else if (this.os === "linux") {
|
|
7182
|
-
const
|
|
7183
|
-
if (!
|
|
7264
|
+
const dir2 = join11(homedir9(), ".config", "systemd", "user");
|
|
7265
|
+
if (!existsSync8(dir2)) mkdirSync7(dir2, { recursive: true });
|
|
7184
7266
|
const unit = this.generateSystemdUnit(nodePath, apiEntry, configPath);
|
|
7185
|
-
|
|
7267
|
+
writeFileSync6(servicePath, unit);
|
|
7186
7268
|
chmodSync(servicePath, 384);
|
|
7187
7269
|
try {
|
|
7188
7270
|
execFileSync3("systemctl", ["--user", "daemon-reload"], { timeout: 1e4, stdio: "ignore" });
|
|
@@ -7205,7 +7287,7 @@ WantedBy=default.target
|
|
|
7205
7287
|
*/
|
|
7206
7288
|
uninstall() {
|
|
7207
7289
|
const servicePath = this.getServicePath();
|
|
7208
|
-
if (!
|
|
7290
|
+
if (!existsSync8(servicePath)) {
|
|
7209
7291
|
return { removed: false, message: "Service is not installed." };
|
|
7210
7292
|
}
|
|
7211
7293
|
if (this.os === "darwin") {
|
|
@@ -7243,7 +7325,7 @@ WantedBy=default.target
|
|
|
7243
7325
|
status() {
|
|
7244
7326
|
const servicePath = this.getServicePath();
|
|
7245
7327
|
const plat = this.os === "darwin" ? "launchd" : this.os === "linux" ? "systemd" : "unsupported";
|
|
7246
|
-
const installed =
|
|
7328
|
+
const installed = existsSync8(servicePath);
|
|
7247
7329
|
let running = false;
|
|
7248
7330
|
if (installed) {
|
|
7249
7331
|
if (this.os === "darwin") {
|
|
@@ -7298,34 +7380,34 @@ WantedBy=default.target
|
|
|
7298
7380
|
needsRepair() {
|
|
7299
7381
|
if (this.os !== "darwin" && this.os !== "linux") return null;
|
|
7300
7382
|
const servicePath = this.getServicePath();
|
|
7301
|
-
if (!
|
|
7383
|
+
if (!existsSync8(servicePath)) return null;
|
|
7302
7384
|
let serviceContent = "";
|
|
7303
7385
|
try {
|
|
7304
|
-
serviceContent =
|
|
7386
|
+
serviceContent = readFileSync5(servicePath, "utf-8");
|
|
7305
7387
|
} catch {
|
|
7306
7388
|
return { reason: "Service file unreadable" };
|
|
7307
7389
|
}
|
|
7308
|
-
const startScript =
|
|
7390
|
+
const startScript = join11(homedir9(), ".agenticmail", "bin", "start-server.sh");
|
|
7309
7391
|
if (serviceContent.includes(startScript)) {
|
|
7310
|
-
if (!
|
|
7392
|
+
if (!existsSync8(startScript)) {
|
|
7311
7393
|
return { reason: "start-server.sh is missing" };
|
|
7312
7394
|
}
|
|
7313
7395
|
let scriptContent = "";
|
|
7314
7396
|
try {
|
|
7315
|
-
scriptContent =
|
|
7397
|
+
scriptContent = readFileSync5(startScript, "utf-8");
|
|
7316
7398
|
} catch {
|
|
7317
7399
|
return { reason: "start-server.sh unreadable" };
|
|
7318
7400
|
}
|
|
7319
7401
|
const apiPathMatch = scriptContent.match(/(\/[^"\s]+@agenticmail\/api\/dist\/index\.js)/);
|
|
7320
7402
|
if (apiPathMatch) {
|
|
7321
7403
|
const referenced = apiPathMatch[1];
|
|
7322
|
-
if (!
|
|
7404
|
+
if (!existsSync8(referenced)) {
|
|
7323
7405
|
return { reason: `start-server.sh references missing path: ${referenced}` };
|
|
7324
7406
|
}
|
|
7325
7407
|
}
|
|
7326
7408
|
if (/node_modules\/agenticmail\/(?!.*@agenticmail\/cli)/.test(scriptContent)) {
|
|
7327
7409
|
const stale = /(\S*node_modules\/agenticmail\/\S*)/.exec(scriptContent)?.[1];
|
|
7328
|
-
if (stale && !
|
|
7410
|
+
if (stale && !existsSync8(stale)) {
|
|
7329
7411
|
return { reason: `start-server.sh references legacy unscoped path: ${stale}` };
|
|
7330
7412
|
}
|
|
7331
7413
|
}
|
|
@@ -7338,11 +7420,11 @@ WantedBy=default.target
|
|
|
7338
7420
|
return { reason: `Service version drift (current CLI is v${currentVersion})` };
|
|
7339
7421
|
}
|
|
7340
7422
|
}
|
|
7341
|
-
const entryCache =
|
|
7342
|
-
if (
|
|
7423
|
+
const entryCache = join11(homedir9(), ".agenticmail", "api-entry.path");
|
|
7424
|
+
if (existsSync8(entryCache)) {
|
|
7343
7425
|
try {
|
|
7344
|
-
const cached =
|
|
7345
|
-
if (cached && !
|
|
7426
|
+
const cached = readFileSync5(entryCache, "utf-8").trim();
|
|
7427
|
+
if (cached && !existsSync8(cached)) {
|
|
7346
7428
|
return { reason: `Cached API entry path no longer exists: ${cached}` };
|
|
7347
7429
|
}
|
|
7348
7430
|
} catch {
|
|
@@ -7381,8 +7463,8 @@ var SetupManager = class {
|
|
|
7381
7463
|
async ensureStalwart(composePath) {
|
|
7382
7464
|
const status = await this.checker.checkStalwart();
|
|
7383
7465
|
if (status.installed) return;
|
|
7384
|
-
const
|
|
7385
|
-
await this.installer.startStalwart(
|
|
7466
|
+
const path2 = composePath ?? this.getComposePath();
|
|
7467
|
+
await this.installer.startStalwart(path2);
|
|
7386
7468
|
}
|
|
7387
7469
|
async ensureCloudflared() {
|
|
7388
7470
|
return this.installer.installCloudflared();
|
|
@@ -7393,13 +7475,13 @@ var SetupManager = class {
|
|
|
7393
7475
|
* falls back to monorepo location.
|
|
7394
7476
|
*/
|
|
7395
7477
|
getComposePath() {
|
|
7396
|
-
const standalonePath =
|
|
7397
|
-
if (
|
|
7478
|
+
const standalonePath = join12(homedir10(), ".agenticmail", "docker-compose.yml");
|
|
7479
|
+
if (existsSync9(standalonePath)) return standalonePath;
|
|
7398
7480
|
const cwd = process.cwd();
|
|
7399
|
-
const candidates = [cwd,
|
|
7400
|
-
for (const
|
|
7401
|
-
const p =
|
|
7402
|
-
if (
|
|
7481
|
+
const candidates = [cwd, join12(cwd, "..")];
|
|
7482
|
+
for (const dir2 of candidates) {
|
|
7483
|
+
const p = join12(dir2, "docker-compose.yml");
|
|
7484
|
+
if (existsSync9(p)) return p;
|
|
7403
7485
|
}
|
|
7404
7486
|
return standalonePath;
|
|
7405
7487
|
}
|
|
@@ -7409,19 +7491,19 @@ var SetupManager = class {
|
|
|
7409
7491
|
* Always regenerates Docker files to keep passwords in sync.
|
|
7410
7492
|
*/
|
|
7411
7493
|
initConfig() {
|
|
7412
|
-
const dataDir =
|
|
7413
|
-
const configPath =
|
|
7414
|
-
const envPath =
|
|
7415
|
-
if (
|
|
7494
|
+
const dataDir = join12(homedir10(), ".agenticmail");
|
|
7495
|
+
const configPath = join12(dataDir, "config.json");
|
|
7496
|
+
const envPath = join12(dataDir, ".env");
|
|
7497
|
+
if (existsSync9(configPath)) {
|
|
7416
7498
|
try {
|
|
7417
|
-
const existing = JSON.parse(
|
|
7499
|
+
const existing = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
7418
7500
|
this.generateDockerFiles(existing);
|
|
7419
7501
|
return { configPath, envPath, config: existing, isNew: false };
|
|
7420
7502
|
} catch {
|
|
7421
7503
|
}
|
|
7422
7504
|
}
|
|
7423
|
-
if (!
|
|
7424
|
-
|
|
7505
|
+
if (!existsSync9(dataDir)) {
|
|
7506
|
+
mkdirSync8(dataDir, { recursive: true });
|
|
7425
7507
|
}
|
|
7426
7508
|
const masterKey = `mk_${randomBytes3(24).toString("hex")}`;
|
|
7427
7509
|
const stalwartPassword = randomBytes3(16).toString("hex");
|
|
@@ -7437,7 +7519,7 @@ var SetupManager = class {
|
|
|
7437
7519
|
api: { port: 3829, host: "127.0.0.1" },
|
|
7438
7520
|
dataDir
|
|
7439
7521
|
};
|
|
7440
|
-
|
|
7522
|
+
writeFileSync7(configPath, JSON.stringify(config, null, 2));
|
|
7441
7523
|
chmodSync2(configPath, 384);
|
|
7442
7524
|
const envContent = `# Auto-generated by agenticmail setup
|
|
7443
7525
|
STALWART_ADMIN_USER=admin
|
|
@@ -7453,7 +7535,7 @@ SMTP_PORT=587
|
|
|
7453
7535
|
IMAP_HOST=localhost
|
|
7454
7536
|
IMAP_PORT=143
|
|
7455
7537
|
`;
|
|
7456
|
-
|
|
7538
|
+
writeFileSync7(envPath, envContent);
|
|
7457
7539
|
chmodSync2(envPath, 384);
|
|
7458
7540
|
this.generateDockerFiles(config);
|
|
7459
7541
|
return { configPath, envPath, config, isNew: true };
|
|
@@ -7463,13 +7545,13 @@ IMAP_PORT=143
|
|
|
7463
7545
|
* with the correct admin password from config.
|
|
7464
7546
|
*/
|
|
7465
7547
|
generateDockerFiles(config) {
|
|
7466
|
-
const dataDir = config.dataDir ||
|
|
7467
|
-
if (!
|
|
7468
|
-
|
|
7548
|
+
const dataDir = config.dataDir || join12(homedir10(), ".agenticmail");
|
|
7549
|
+
if (!existsSync9(dataDir)) {
|
|
7550
|
+
mkdirSync8(dataDir, { recursive: true });
|
|
7469
7551
|
}
|
|
7470
7552
|
const password = config.stalwart?.adminPassword || "changeme";
|
|
7471
|
-
const composePath =
|
|
7472
|
-
|
|
7553
|
+
const composePath = join12(dataDir, "docker-compose.yml");
|
|
7554
|
+
writeFileSync7(composePath, `services:
|
|
7473
7555
|
stalwart:
|
|
7474
7556
|
# Pinned to v0.15.5 \u2014 Stalwart 0.16+ moved its config to JSON
|
|
7475
7557
|
# at /etc/stalwart/config.json (hardcoded into the container
|
|
@@ -7499,8 +7581,8 @@ volumes:
|
|
|
7499
7581
|
stalwart-data:
|
|
7500
7582
|
`);
|
|
7501
7583
|
chmodSync2(composePath, 384);
|
|
7502
|
-
const tomlPath =
|
|
7503
|
-
|
|
7584
|
+
const tomlPath = join12(dataDir, "stalwart.toml");
|
|
7585
|
+
writeFileSync7(tomlPath, `# Stalwart Mail Server \u2014 AgenticMail Configuration
|
|
7504
7586
|
|
|
7505
7587
|
[server]
|
|
7506
7588
|
hostname = "localhost"
|
|
@@ -7556,8 +7638,8 @@ secret = "${password}"
|
|
|
7556
7638
|
* Check if config has already been initialized.
|
|
7557
7639
|
*/
|
|
7558
7640
|
isInitialized() {
|
|
7559
|
-
const configPath =
|
|
7560
|
-
return
|
|
7641
|
+
const configPath = join12(homedir10(), ".agenticmail", "config.json");
|
|
7642
|
+
return existsSync9(configPath);
|
|
7561
7643
|
}
|
|
7562
7644
|
};
|
|
7563
7645
|
|
|
@@ -7596,18 +7678,18 @@ function threadIdFor(input) {
|
|
|
7596
7678
|
|
|
7597
7679
|
// src/threading/thread-cache.ts
|
|
7598
7680
|
import {
|
|
7599
|
-
existsSync as
|
|
7600
|
-
mkdirSync as
|
|
7601
|
-
readFileSync as
|
|
7602
|
-
writeFileSync as
|
|
7681
|
+
existsSync as existsSync10,
|
|
7682
|
+
mkdirSync as mkdirSync9,
|
|
7683
|
+
readFileSync as readFileSync7,
|
|
7684
|
+
writeFileSync as writeFileSync8,
|
|
7603
7685
|
readdirSync,
|
|
7604
7686
|
statSync,
|
|
7605
7687
|
rmSync,
|
|
7606
|
-
renameSync as
|
|
7688
|
+
renameSync as renameSync3
|
|
7607
7689
|
} from "fs";
|
|
7608
|
-
import { homedir as
|
|
7609
|
-
import { join as
|
|
7610
|
-
var CACHE_DIR_DEFAULT =
|
|
7690
|
+
import { homedir as homedir11 } from "os";
|
|
7691
|
+
import { join as join13 } from "path";
|
|
7692
|
+
var CACHE_DIR_DEFAULT = join13(homedir11(), ".agenticmail", "thread-cache");
|
|
7611
7693
|
var DEFAULT_K_MESSAGES = 10;
|
|
7612
7694
|
var DEFAULT_LRU_CAP = 5e3;
|
|
7613
7695
|
var PREVIEW_MAX_CHARS = 240;
|
|
@@ -7620,18 +7702,18 @@ var ThreadCache = class {
|
|
|
7620
7702
|
this.k = opts.k ?? DEFAULT_K_MESSAGES;
|
|
7621
7703
|
this.lruCap = opts.lruCap ?? DEFAULT_LRU_CAP;
|
|
7622
7704
|
try {
|
|
7623
|
-
|
|
7705
|
+
mkdirSync9(this.dir, { recursive: true });
|
|
7624
7706
|
} catch {
|
|
7625
7707
|
}
|
|
7626
7708
|
}
|
|
7627
7709
|
pathFor(threadId) {
|
|
7628
|
-
return
|
|
7710
|
+
return join13(this.dir, `${threadId}.json`);
|
|
7629
7711
|
}
|
|
7630
7712
|
read(threadId) {
|
|
7631
7713
|
const p = this.pathFor(threadId);
|
|
7632
|
-
if (!
|
|
7714
|
+
if (!existsSync10(p)) return null;
|
|
7633
7715
|
try {
|
|
7634
|
-
const raw =
|
|
7716
|
+
const raw = readFileSync7(p, "utf-8");
|
|
7635
7717
|
return JSON.parse(raw);
|
|
7636
7718
|
} catch {
|
|
7637
7719
|
try {
|
|
@@ -7696,8 +7778,8 @@ var ThreadCache = class {
|
|
|
7696
7778
|
writeAtomic(threadId, entry) {
|
|
7697
7779
|
const p = this.pathFor(threadId);
|
|
7698
7780
|
const tmp = `${p}.tmp`;
|
|
7699
|
-
|
|
7700
|
-
|
|
7781
|
+
writeFileSync8(tmp, JSON.stringify(entry), "utf-8");
|
|
7782
|
+
renameSync3(tmp, p);
|
|
7701
7783
|
}
|
|
7702
7784
|
/**
|
|
7703
7785
|
* Best-effort LRU eviction. Runs at most every 256 writes (we
|
|
@@ -7715,7 +7797,7 @@ var ThreadCache = class {
|
|
|
7715
7797
|
}
|
|
7716
7798
|
if (files.length <= this.lruCap) return;
|
|
7717
7799
|
const stats = files.map((f) => {
|
|
7718
|
-
const p =
|
|
7800
|
+
const p = join13(this.dir, f);
|
|
7719
7801
|
try {
|
|
7720
7802
|
return { p, mtime: statSync(p).mtimeMs };
|
|
7721
7803
|
} catch {
|
|
@@ -7746,36 +7828,36 @@ function dedupAndCap(messages, k) {
|
|
|
7746
7828
|
|
|
7747
7829
|
// src/threading/agent-memory.ts
|
|
7748
7830
|
import {
|
|
7749
|
-
existsSync as
|
|
7750
|
-
mkdirSync as
|
|
7751
|
-
readFileSync as
|
|
7752
|
-
writeFileSync as
|
|
7831
|
+
existsSync as existsSync11,
|
|
7832
|
+
mkdirSync as mkdirSync10,
|
|
7833
|
+
readFileSync as readFileSync8,
|
|
7834
|
+
writeFileSync as writeFileSync9,
|
|
7753
7835
|
rmSync as rmSync2,
|
|
7754
|
-
renameSync as
|
|
7836
|
+
renameSync as renameSync4
|
|
7755
7837
|
} from "fs";
|
|
7756
|
-
import { homedir as
|
|
7757
|
-
import { join as
|
|
7758
|
-
var MEMORY_DIR_DEFAULT =
|
|
7838
|
+
import { homedir as homedir12 } from "os";
|
|
7839
|
+
import { join as join14 } from "path";
|
|
7840
|
+
var MEMORY_DIR_DEFAULT = join14(homedir12(), ".agenticmail", "agent-memory");
|
|
7759
7841
|
var AgentMemoryStore = class {
|
|
7760
7842
|
dir;
|
|
7761
7843
|
constructor(opts = {}) {
|
|
7762
7844
|
this.dir = opts.memoryDir ?? MEMORY_DIR_DEFAULT;
|
|
7763
7845
|
try {
|
|
7764
|
-
|
|
7846
|
+
mkdirSync10(this.dir, { recursive: true });
|
|
7765
7847
|
} catch {
|
|
7766
7848
|
}
|
|
7767
7849
|
}
|
|
7768
7850
|
dirFor(agentId) {
|
|
7769
|
-
return
|
|
7851
|
+
return join14(this.dir, sanitizeId(agentId));
|
|
7770
7852
|
}
|
|
7771
7853
|
pathFor(agentId, threadId) {
|
|
7772
|
-
return
|
|
7854
|
+
return join14(this.dirFor(agentId), `${sanitizeId(threadId)}.md`);
|
|
7773
7855
|
}
|
|
7774
7856
|
read(agentId, threadId) {
|
|
7775
7857
|
const p = this.pathFor(agentId, threadId);
|
|
7776
|
-
if (!
|
|
7858
|
+
if (!existsSync11(p)) return null;
|
|
7777
7859
|
try {
|
|
7778
|
-
const raw =
|
|
7860
|
+
const raw = readFileSync8(p, "utf-8");
|
|
7779
7861
|
const parsed = parse(raw);
|
|
7780
7862
|
return { ...parsed, raw };
|
|
7781
7863
|
} catch {
|
|
@@ -7785,14 +7867,14 @@ var AgentMemoryStore = class {
|
|
|
7785
7867
|
write(agentId, threadId, fields) {
|
|
7786
7868
|
const agentDir = this.dirFor(agentId);
|
|
7787
7869
|
try {
|
|
7788
|
-
|
|
7870
|
+
mkdirSync10(agentDir, { recursive: true });
|
|
7789
7871
|
} catch {
|
|
7790
7872
|
}
|
|
7791
7873
|
const body = render({ ...fields, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7792
7874
|
const p = this.pathFor(agentId, threadId);
|
|
7793
7875
|
const tmp = `${p}.tmp`;
|
|
7794
|
-
|
|
7795
|
-
|
|
7876
|
+
writeFileSync9(tmp, body, "utf-8");
|
|
7877
|
+
renameSync4(tmp, p);
|
|
7796
7878
|
}
|
|
7797
7879
|
delete(agentId, threadId) {
|
|
7798
7880
|
try {
|
|
@@ -7899,6 +7981,7 @@ export {
|
|
|
7899
7981
|
flushTelemetry,
|
|
7900
7982
|
forgetHostSession,
|
|
7901
7983
|
getDatabase,
|
|
7984
|
+
getOperatorEmail,
|
|
7902
7985
|
hostSessionStoragePath,
|
|
7903
7986
|
isInternalEmail,
|
|
7904
7987
|
isSessionFresh,
|
|
@@ -7907,6 +7990,7 @@ export {
|
|
|
7907
7990
|
normalizeAddress,
|
|
7908
7991
|
normalizePhoneNumber,
|
|
7909
7992
|
normalizeSubject,
|
|
7993
|
+
operatorPrefsStoragePath,
|
|
7910
7994
|
parseEmail,
|
|
7911
7995
|
parseGoogleVoiceSms,
|
|
7912
7996
|
recordToolCall,
|
|
@@ -7919,6 +8003,7 @@ export {
|
|
|
7919
8003
|
saveHostSession,
|
|
7920
8004
|
scanOutboundEmail,
|
|
7921
8005
|
scoreEmail,
|
|
8006
|
+
setOperatorEmail,
|
|
7922
8007
|
setTelemetryVersion,
|
|
7923
8008
|
startRelayBridge,
|
|
7924
8009
|
threadIdFor,
|