@agenticmail/core 0.9.5 → 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 +375 -205
- package/dist/index.d.cts +192 -1
- package/dist/index.d.ts +192 -1
- package/dist/index.js +356 -195
- 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,6 +6123,131 @@ function redactObject(input, _depth = 0) {
|
|
|
6096
6123
|
return out;
|
|
6097
6124
|
}
|
|
6098
6125
|
|
|
6126
|
+
// src/operator-prefs.ts
|
|
6127
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync4, readFileSync as readFileSync3, renameSync, writeFileSync as writeFileSync4 } from "fs";
|
|
6128
|
+
import { homedir as homedir5 } from "os";
|
|
6129
|
+
import { join as join7 } from "path";
|
|
6130
|
+
function dir() {
|
|
6131
|
+
return join7(homedir5(), ".agenticmail");
|
|
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
|
+
}
|
|
6188
|
+
function storagePath() {
|
|
6189
|
+
return join8(storageDir(), "host-sessions.json");
|
|
6190
|
+
}
|
|
6191
|
+
var DEFAULT_SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
6192
|
+
function readFile2() {
|
|
6193
|
+
const p = storagePath();
|
|
6194
|
+
if (!existsSync5(p)) return { version: 1, sessions: {} };
|
|
6195
|
+
try {
|
|
6196
|
+
const raw = readFileSync4(p, "utf-8");
|
|
6197
|
+
if (!raw.trim()) return { version: 1, sessions: {} };
|
|
6198
|
+
const parsed = JSON.parse(raw);
|
|
6199
|
+
return {
|
|
6200
|
+
version: 1,
|
|
6201
|
+
sessions: parsed.sessions && typeof parsed.sessions === "object" ? parsed.sessions : {}
|
|
6202
|
+
};
|
|
6203
|
+
} catch {
|
|
6204
|
+
return { version: 1, sessions: {} };
|
|
6205
|
+
}
|
|
6206
|
+
}
|
|
6207
|
+
function writeFile2(shape) {
|
|
6208
|
+
const dir2 = storageDir();
|
|
6209
|
+
const p = storagePath();
|
|
6210
|
+
if (!existsSync5(dir2)) mkdirSync5(dir2, { recursive: true });
|
|
6211
|
+
const tmp = `${p}.agenticmail-tmp-${process.pid}`;
|
|
6212
|
+
writeFileSync5(tmp, JSON.stringify(shape, null, 2), "utf-8");
|
|
6213
|
+
renameSync2(tmp, p);
|
|
6214
|
+
}
|
|
6215
|
+
function saveHostSession(host, session) {
|
|
6216
|
+
if (!session.sessionId) return;
|
|
6217
|
+
try {
|
|
6218
|
+
const shape = readFile2();
|
|
6219
|
+
shape.sessions[host] = {
|
|
6220
|
+
...session,
|
|
6221
|
+
lastSeenMs: Date.now()
|
|
6222
|
+
};
|
|
6223
|
+
writeFile2(shape);
|
|
6224
|
+
} catch {
|
|
6225
|
+
}
|
|
6226
|
+
}
|
|
6227
|
+
function loadHostSession(host, maxAgeMs = DEFAULT_SESSION_MAX_AGE_MS) {
|
|
6228
|
+
const shape = readFile2();
|
|
6229
|
+
const record = shape.sessions[host];
|
|
6230
|
+
if (!record) return null;
|
|
6231
|
+
if (!isSessionFresh(record, maxAgeMs)) return null;
|
|
6232
|
+
return record;
|
|
6233
|
+
}
|
|
6234
|
+
function isSessionFresh(session, maxAgeMs = DEFAULT_SESSION_MAX_AGE_MS) {
|
|
6235
|
+
if (!session || !Number.isFinite(session.lastSeenMs)) return false;
|
|
6236
|
+
return Date.now() - session.lastSeenMs <= maxAgeMs;
|
|
6237
|
+
}
|
|
6238
|
+
function forgetHostSession(host) {
|
|
6239
|
+
try {
|
|
6240
|
+
const shape = readFile2();
|
|
6241
|
+
if (!shape.sessions[host]) return;
|
|
6242
|
+
delete shape.sessions[host];
|
|
6243
|
+
writeFile2(shape);
|
|
6244
|
+
} catch {
|
|
6245
|
+
}
|
|
6246
|
+
}
|
|
6247
|
+
function hostSessionStoragePath() {
|
|
6248
|
+
return storagePath();
|
|
6249
|
+
}
|
|
6250
|
+
|
|
6099
6251
|
// src/util/safe-url.ts
|
|
6100
6252
|
var UnsafeApiUrlError = class extends Error {
|
|
6101
6253
|
constructor(raw, reason) {
|
|
@@ -6138,21 +6290,21 @@ function validateApiUrl(raw) {
|
|
|
6138
6290
|
return parsed.origin;
|
|
6139
6291
|
}
|
|
6140
6292
|
function buildApiUrl(baseOrigin, pathAndQuery) {
|
|
6141
|
-
const
|
|
6142
|
-
return new URL(
|
|
6293
|
+
const path2 = pathAndQuery.startsWith("/") ? pathAndQuery : `/${pathAndQuery}`;
|
|
6294
|
+
return new URL(path2, baseOrigin + "/").toString();
|
|
6143
6295
|
}
|
|
6144
6296
|
|
|
6145
6297
|
// src/setup/index.ts
|
|
6146
6298
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
6147
|
-
import { existsSync as
|
|
6148
|
-
import { join as
|
|
6149
|
-
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";
|
|
6150
6302
|
|
|
6151
6303
|
// src/setup/deps.ts
|
|
6152
6304
|
import { execFileSync } from "child_process";
|
|
6153
|
-
import { existsSync as
|
|
6154
|
-
import { join as
|
|
6155
|
-
import { homedir as
|
|
6305
|
+
import { existsSync as existsSync6 } from "fs";
|
|
6306
|
+
import { join as join9 } from "path";
|
|
6307
|
+
import { homedir as homedir7 } from "os";
|
|
6156
6308
|
var DependencyChecker = class {
|
|
6157
6309
|
async checkAll() {
|
|
6158
6310
|
return Promise.all([
|
|
@@ -6202,8 +6354,8 @@ var DependencyChecker = class {
|
|
|
6202
6354
|
}
|
|
6203
6355
|
}
|
|
6204
6356
|
async checkCloudflared() {
|
|
6205
|
-
const binPath =
|
|
6206
|
-
if (
|
|
6357
|
+
const binPath = join9(homedir7(), ".agenticmail", "bin", "cloudflared");
|
|
6358
|
+
if (existsSync6(binPath)) {
|
|
6207
6359
|
let version;
|
|
6208
6360
|
try {
|
|
6209
6361
|
const output = execFileSync(binPath, ["--version"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
@@ -6225,10 +6377,10 @@ var DependencyChecker = class {
|
|
|
6225
6377
|
|
|
6226
6378
|
// src/setup/installer.ts
|
|
6227
6379
|
import { execFileSync as execFileSync2, execSync, spawn as spawnChild } from "child_process";
|
|
6228
|
-
import { existsSync as
|
|
6229
|
-
import { writeFile, rename, chmod as chmod2, mkdir as mkdir2, unlink } from "fs/promises";
|
|
6230
|
-
import { join as
|
|
6231
|
-
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";
|
|
6232
6384
|
function runShellWithRollingOutput(cmd, opts = {}) {
|
|
6233
6385
|
const maxLines = opts.maxLines ?? 20;
|
|
6234
6386
|
const timeout = opts.timeout ?? 3e5;
|
|
@@ -6404,11 +6556,11 @@ var DependencyInstaller = class {
|
|
|
6404
6556
|
try {
|
|
6405
6557
|
const composeBin = execFileSync2("which", ["docker-compose"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6406
6558
|
if (!composeBin) return;
|
|
6407
|
-
const pluginDir =
|
|
6408
|
-
const pluginPath =
|
|
6409
|
-
if (
|
|
6559
|
+
const pluginDir = join10(homedir8(), ".docker", "cli-plugins");
|
|
6560
|
+
const pluginPath = join10(pluginDir, "docker-compose");
|
|
6561
|
+
if (existsSync7(pluginPath)) return;
|
|
6410
6562
|
try {
|
|
6411
|
-
|
|
6563
|
+
mkdirSync6(pluginDir, { recursive: true });
|
|
6412
6564
|
} catch {
|
|
6413
6565
|
}
|
|
6414
6566
|
try {
|
|
@@ -6471,9 +6623,9 @@ var DependencyInstaller = class {
|
|
|
6471
6623
|
return;
|
|
6472
6624
|
}
|
|
6473
6625
|
this.onProgress("__progress__:5:Installing Docker Engine...");
|
|
6474
|
-
const tmpDir =
|
|
6626
|
+
const tmpDir = join10(homedir8(), ".agenticmail", "tmp");
|
|
6475
6627
|
await mkdir2(tmpDir, { recursive: true });
|
|
6476
|
-
const scriptPath =
|
|
6628
|
+
const scriptPath = join10(tmpDir, "install-docker.sh");
|
|
6477
6629
|
const dlResult = await runShellWithRollingOutput(
|
|
6478
6630
|
`curl -fsSL https://get.docker.com -o "${scriptPath}" && sudo sh "${scriptPath}"`,
|
|
6479
6631
|
{ timeout: 3e5 }
|
|
@@ -6541,8 +6693,8 @@ var DependencyInstaller = class {
|
|
|
6541
6693
|
}
|
|
6542
6694
|
try {
|
|
6543
6695
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
6544
|
-
const dockerExe =
|
|
6545
|
-
if (
|
|
6696
|
+
const dockerExe = join10(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
6697
|
+
if (existsSync7(dockerExe)) {
|
|
6546
6698
|
execSync(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
6547
6699
|
}
|
|
6548
6700
|
} catch {
|
|
@@ -6592,8 +6744,8 @@ var DependencyInstaller = class {
|
|
|
6592
6744
|
this.onProgress("__progress__:70:Docker Desktop installed. Starting...");
|
|
6593
6745
|
try {
|
|
6594
6746
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
6595
|
-
const dockerExe =
|
|
6596
|
-
if (
|
|
6747
|
+
const dockerExe = join10(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
6748
|
+
if (existsSync7(dockerExe)) {
|
|
6597
6749
|
execSync(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
6598
6750
|
}
|
|
6599
6751
|
} catch {
|
|
@@ -6630,7 +6782,7 @@ var DependencyInstaller = class {
|
|
|
6630
6782
|
* Start the Stalwart mail server Docker container.
|
|
6631
6783
|
*/
|
|
6632
6784
|
async startStalwart(composePath) {
|
|
6633
|
-
if (!
|
|
6785
|
+
if (!existsSync7(composePath)) {
|
|
6634
6786
|
throw new Error(`docker-compose.yml not found at: ${composePath}`);
|
|
6635
6787
|
}
|
|
6636
6788
|
if (!this.isDockerReady()) {
|
|
@@ -6689,16 +6841,16 @@ var DependencyInstaller = class {
|
|
|
6689
6841
|
*/
|
|
6690
6842
|
async installCloudflared() {
|
|
6691
6843
|
const os = platform3();
|
|
6692
|
-
const binDir =
|
|
6844
|
+
const binDir = join10(homedir8(), ".agenticmail", "bin");
|
|
6693
6845
|
const binName = os === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
6694
|
-
const binPath =
|
|
6695
|
-
if (
|
|
6846
|
+
const binPath = join10(binDir, binName);
|
|
6847
|
+
if (existsSync7(binPath)) {
|
|
6696
6848
|
return binPath;
|
|
6697
6849
|
}
|
|
6698
6850
|
try {
|
|
6699
6851
|
const whichCmd = os === "win32" ? "where" : "which";
|
|
6700
6852
|
const sysPath = execFileSync2(whichCmd, ["cloudflared"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim().split("\n")[0];
|
|
6701
|
-
if (sysPath &&
|
|
6853
|
+
if (sysPath && existsSync7(sysPath)) return sysPath;
|
|
6702
6854
|
} catch {
|
|
6703
6855
|
}
|
|
6704
6856
|
this.onProgress("Downloading cloudflared...");
|
|
@@ -6721,8 +6873,8 @@ var DependencyInstaller = class {
|
|
|
6721
6873
|
}
|
|
6722
6874
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
6723
6875
|
if (os === "darwin") {
|
|
6724
|
-
const tgzPath =
|
|
6725
|
-
await
|
|
6876
|
+
const tgzPath = join10(binDir, "cloudflared.tgz");
|
|
6877
|
+
await writeFile3(tgzPath, buffer);
|
|
6726
6878
|
try {
|
|
6727
6879
|
execFileSync2("tar", ["-xzf", tgzPath, "-C", binDir, "cloudflared"], { timeout: 15e3, stdio: "ignore" });
|
|
6728
6880
|
await chmod2(binPath, 493);
|
|
@@ -6734,11 +6886,11 @@ var DependencyInstaller = class {
|
|
|
6734
6886
|
}
|
|
6735
6887
|
} else {
|
|
6736
6888
|
const tmpPath = binPath + ".tmp";
|
|
6737
|
-
await
|
|
6889
|
+
await writeFile3(tmpPath, buffer);
|
|
6738
6890
|
if (os !== "win32") await chmod2(tmpPath, 493);
|
|
6739
6891
|
await rename(tmpPath, binPath);
|
|
6740
6892
|
}
|
|
6741
|
-
if (!
|
|
6893
|
+
if (!existsSync7(binPath)) {
|
|
6742
6894
|
throw new Error("cloudflared download succeeded but binary not found after extraction");
|
|
6743
6895
|
}
|
|
6744
6896
|
this.onProgress("cloudflared installed");
|
|
@@ -6756,9 +6908,9 @@ var DependencyInstaller = class {
|
|
|
6756
6908
|
|
|
6757
6909
|
// src/setup/service.ts
|
|
6758
6910
|
import { execFileSync as execFileSync3, execSync as execSync2 } from "child_process";
|
|
6759
|
-
import { existsSync as
|
|
6760
|
-
import { join as
|
|
6761
|
-
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";
|
|
6762
6914
|
import { createRequire as createRequire2 } from "module";
|
|
6763
6915
|
var PLIST_LABEL = "com.agenticmail.server";
|
|
6764
6916
|
var SYSTEMD_UNIT = "agenticmail.service";
|
|
@@ -6769,9 +6921,9 @@ var ServiceManager = class {
|
|
|
6769
6921
|
*/
|
|
6770
6922
|
getServicePath() {
|
|
6771
6923
|
if (this.os === "darwin") {
|
|
6772
|
-
return
|
|
6924
|
+
return join11(homedir9(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
6773
6925
|
} else {
|
|
6774
|
-
return
|
|
6926
|
+
return join11(homedir9(), ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
6775
6927
|
}
|
|
6776
6928
|
}
|
|
6777
6929
|
/**
|
|
@@ -6805,40 +6957,40 @@ var ServiceManager = class {
|
|
|
6805
6957
|
try {
|
|
6806
6958
|
const req = createRequire2(import.meta.url);
|
|
6807
6959
|
const resolved = req.resolve("@agenticmail/api");
|
|
6808
|
-
if (
|
|
6960
|
+
if (existsSync8(resolved)) return resolved;
|
|
6809
6961
|
} catch {
|
|
6810
6962
|
}
|
|
6811
6963
|
const parentPackages = [
|
|
6812
|
-
|
|
6964
|
+
join11("@agenticmail", "cli"),
|
|
6813
6965
|
// current scoped package
|
|
6814
6966
|
"agenticmail"
|
|
6815
6967
|
// legacy unscoped package
|
|
6816
6968
|
];
|
|
6817
6969
|
const baseDirs = [
|
|
6818
6970
|
// user-local install
|
|
6819
|
-
|
|
6971
|
+
join11(homedir9(), "node_modules")
|
|
6820
6972
|
];
|
|
6821
6973
|
try {
|
|
6822
6974
|
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6823
|
-
baseDirs.push(
|
|
6824
|
-
baseDirs.push(
|
|
6975
|
+
baseDirs.push(join11(prefix, "lib", "node_modules"));
|
|
6976
|
+
baseDirs.push(join11(prefix, "node_modules"));
|
|
6825
6977
|
} catch {
|
|
6826
6978
|
}
|
|
6827
6979
|
baseDirs.push("/opt/homebrew/lib/node_modules");
|
|
6828
6980
|
baseDirs.push("/usr/local/lib/node_modules");
|
|
6829
6981
|
for (const base of baseDirs) {
|
|
6830
|
-
const sibling =
|
|
6831
|
-
if (
|
|
6982
|
+
const sibling = join11(base, "@agenticmail", "api", "dist", "index.js");
|
|
6983
|
+
if (existsSync8(sibling)) return sibling;
|
|
6832
6984
|
for (const parent of parentPackages) {
|
|
6833
|
-
const nested =
|
|
6834
|
-
if (
|
|
6985
|
+
const nested = join11(base, parent, "node_modules", "@agenticmail", "api", "dist", "index.js");
|
|
6986
|
+
if (existsSync8(nested)) return nested;
|
|
6835
6987
|
}
|
|
6836
6988
|
}
|
|
6837
|
-
const dataDir =
|
|
6838
|
-
const entryCache =
|
|
6839
|
-
if (
|
|
6840
|
-
const cached =
|
|
6841
|
-
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;
|
|
6842
6994
|
}
|
|
6843
6995
|
throw new Error("Could not find @agenticmail/api entry point. Run `agenticmail start` first to populate the cache.");
|
|
6844
6996
|
}
|
|
@@ -6846,9 +6998,9 @@ var ServiceManager = class {
|
|
|
6846
6998
|
* Cache the API entry path so the service can find it later.
|
|
6847
6999
|
*/
|
|
6848
7000
|
cacheApiEntryPath(entryPath) {
|
|
6849
|
-
const dataDir =
|
|
6850
|
-
if (!
|
|
6851
|
-
|
|
7001
|
+
const dataDir = join11(homedir9(), ".agenticmail");
|
|
7002
|
+
if (!existsSync8(dataDir)) mkdirSync7(dataDir, { recursive: true });
|
|
7003
|
+
writeFileSync6(join11(dataDir, "api-entry.path"), entryPath);
|
|
6852
7004
|
}
|
|
6853
7005
|
/**
|
|
6854
7006
|
* Get the current package version.
|
|
@@ -6861,36 +7013,36 @@ var ServiceManager = class {
|
|
|
6861
7013
|
try {
|
|
6862
7014
|
const req = createRequire2(import.meta.url);
|
|
6863
7015
|
const pkgJson = req.resolve("@agenticmail/cli/package.json");
|
|
6864
|
-
if (
|
|
6865
|
-
const pkg = JSON.parse(
|
|
7016
|
+
if (existsSync8(pkgJson)) {
|
|
7017
|
+
const pkg = JSON.parse(readFileSync5(pkgJson, "utf-8"));
|
|
6866
7018
|
if (pkg.version) return pkg.version;
|
|
6867
7019
|
}
|
|
6868
7020
|
} catch {
|
|
6869
7021
|
}
|
|
6870
7022
|
try {
|
|
6871
7023
|
const apiEntry = this.getApiEntryPath();
|
|
6872
|
-
const apiPkg =
|
|
6873
|
-
if (
|
|
6874
|
-
const pkg = JSON.parse(
|
|
7024
|
+
const apiPkg = join11(apiEntry, "..", "..", "package.json");
|
|
7025
|
+
if (existsSync8(apiPkg)) {
|
|
7026
|
+
const pkg = JSON.parse(readFileSync5(apiPkg, "utf-8"));
|
|
6875
7027
|
if (pkg.version) return pkg.version;
|
|
6876
7028
|
}
|
|
6877
7029
|
} catch {
|
|
6878
7030
|
}
|
|
6879
7031
|
const candidates = [
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
7032
|
+
join11(homedir9(), "node_modules", "@agenticmail", "cli", "package.json"),
|
|
7033
|
+
join11(homedir9(), "node_modules", "agenticmail", "package.json"),
|
|
7034
|
+
join11(homedir9(), ".agenticmail", "package-version.json")
|
|
6883
7035
|
];
|
|
6884
7036
|
try {
|
|
6885
7037
|
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6886
|
-
candidates.push(
|
|
6887
|
-
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"));
|
|
6888
7040
|
} catch {
|
|
6889
7041
|
}
|
|
6890
7042
|
for (const p of candidates) {
|
|
6891
7043
|
try {
|
|
6892
|
-
if (
|
|
6893
|
-
const pkg = JSON.parse(
|
|
7044
|
+
if (existsSync8(p)) {
|
|
7045
|
+
const pkg = JSON.parse(readFileSync5(p, "utf-8"));
|
|
6894
7046
|
if (pkg.version) return pkg.version;
|
|
6895
7047
|
}
|
|
6896
7048
|
} catch {
|
|
@@ -6903,9 +7055,9 @@ var ServiceManager = class {
|
|
|
6903
7055
|
* This ensures AgenticMail doesn't fail on boot when Docker is still loading.
|
|
6904
7056
|
*/
|
|
6905
7057
|
generateStartScript(nodePath, apiEntry) {
|
|
6906
|
-
const scriptPath =
|
|
6907
|
-
const scriptDir =
|
|
6908
|
-
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 });
|
|
6909
7061
|
const script = [
|
|
6910
7062
|
"#!/bin/bash",
|
|
6911
7063
|
"# AgenticMail auto-start script",
|
|
@@ -6956,7 +7108,7 @@ var ServiceManager = class {
|
|
|
6956
7108
|
`log "Starting API server: ${nodePath} ${apiEntry}"`,
|
|
6957
7109
|
`exec "${nodePath}" "${apiEntry}"`
|
|
6958
7110
|
].join("\n") + "\n";
|
|
6959
|
-
|
|
7111
|
+
writeFileSync6(scriptPath, script, { mode: 493 });
|
|
6960
7112
|
return scriptPath;
|
|
6961
7113
|
}
|
|
6962
7114
|
/**
|
|
@@ -6969,9 +7121,9 @@ var ServiceManager = class {
|
|
|
6969
7121
|
* - Service version tracking in env vars
|
|
6970
7122
|
*/
|
|
6971
7123
|
generatePlist(nodePath, apiEntry, configPath) {
|
|
6972
|
-
const config = JSON.parse(
|
|
6973
|
-
const logDir =
|
|
6974
|
-
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 });
|
|
6975
7127
|
const version = this.getVersion();
|
|
6976
7128
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
6977
7129
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -6992,9 +7144,9 @@ var ServiceManager = class {
|
|
|
6992
7144
|
<key>EnvironmentVariables</key>
|
|
6993
7145
|
<dict>
|
|
6994
7146
|
<key>HOME</key>
|
|
6995
|
-
<string>${
|
|
7147
|
+
<string>${homedir9()}</string>
|
|
6996
7148
|
<key>AGENTICMAIL_DATA_DIR</key>
|
|
6997
|
-
<string>${config.dataDir ||
|
|
7149
|
+
<string>${config.dataDir || join11(homedir9(), ".agenticmail")}</string>
|
|
6998
7150
|
<key>PATH</key>
|
|
6999
7151
|
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
7000
7152
|
<key>AGENTICMAIL_SERVICE_VERSION</key>
|
|
@@ -7047,8 +7199,8 @@ var ServiceManager = class {
|
|
|
7047
7199
|
* - Proper dependency ordering
|
|
7048
7200
|
*/
|
|
7049
7201
|
generateSystemdUnit(nodePath, apiEntry, configPath) {
|
|
7050
|
-
const config = JSON.parse(
|
|
7051
|
-
const dataDir = config.dataDir ||
|
|
7202
|
+
const config = JSON.parse(readFileSync5(configPath, "utf-8"));
|
|
7203
|
+
const dataDir = config.dataDir || join11(homedir9(), ".agenticmail");
|
|
7052
7204
|
const version = this.getVersion();
|
|
7053
7205
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
7054
7206
|
return `[Unit]
|
|
@@ -7065,7 +7217,7 @@ Restart=always
|
|
|
7065
7217
|
RestartSec=15
|
|
7066
7218
|
TimeoutStartSec=660
|
|
7067
7219
|
LimitNOFILE=8192
|
|
7068
|
-
Environment=HOME=${
|
|
7220
|
+
Environment=HOME=${homedir9()}
|
|
7069
7221
|
Environment=AGENTICMAIL_DATA_DIR=${dataDir}
|
|
7070
7222
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin
|
|
7071
7223
|
Environment=AGENTICMAIL_SERVICE_VERSION=${version}
|
|
@@ -7078,8 +7230,8 @@ WantedBy=default.target
|
|
|
7078
7230
|
* Install the auto-start service.
|
|
7079
7231
|
*/
|
|
7080
7232
|
install() {
|
|
7081
|
-
const configPath =
|
|
7082
|
-
if (!
|
|
7233
|
+
const configPath = join11(homedir9(), ".agenticmail", "config.json");
|
|
7234
|
+
if (!existsSync8(configPath)) {
|
|
7083
7235
|
return { installed: false, message: "Config not found. Run agenticmail setup first." };
|
|
7084
7236
|
}
|
|
7085
7237
|
const nodePath = this.getNodePath();
|
|
@@ -7091,16 +7243,16 @@ WantedBy=default.target
|
|
|
7091
7243
|
}
|
|
7092
7244
|
const servicePath = this.getServicePath();
|
|
7093
7245
|
if (this.os === "darwin") {
|
|
7094
|
-
const
|
|
7095
|
-
if (!
|
|
7096
|
-
if (
|
|
7246
|
+
const dir2 = join11(homedir9(), "Library", "LaunchAgents");
|
|
7247
|
+
if (!existsSync8(dir2)) mkdirSync7(dir2, { recursive: true });
|
|
7248
|
+
if (existsSync8(servicePath)) {
|
|
7097
7249
|
try {
|
|
7098
7250
|
execFileSync3("launchctl", ["unload", servicePath], { timeout: 1e4, stdio: "ignore" });
|
|
7099
7251
|
} catch {
|
|
7100
7252
|
}
|
|
7101
7253
|
}
|
|
7102
7254
|
const plist = this.generatePlist(nodePath, apiEntry, configPath);
|
|
7103
|
-
|
|
7255
|
+
writeFileSync6(servicePath, plist);
|
|
7104
7256
|
chmodSync(servicePath, 384);
|
|
7105
7257
|
try {
|
|
7106
7258
|
execFileSync3("launchctl", ["load", servicePath], { timeout: 1e4, stdio: "ignore" });
|
|
@@ -7109,10 +7261,10 @@ WantedBy=default.target
|
|
|
7109
7261
|
}
|
|
7110
7262
|
return { installed: true, message: `Service installed at ${servicePath}` };
|
|
7111
7263
|
} else if (this.os === "linux") {
|
|
7112
|
-
const
|
|
7113
|
-
if (!
|
|
7264
|
+
const dir2 = join11(homedir9(), ".config", "systemd", "user");
|
|
7265
|
+
if (!existsSync8(dir2)) mkdirSync7(dir2, { recursive: true });
|
|
7114
7266
|
const unit = this.generateSystemdUnit(nodePath, apiEntry, configPath);
|
|
7115
|
-
|
|
7267
|
+
writeFileSync6(servicePath, unit);
|
|
7116
7268
|
chmodSync(servicePath, 384);
|
|
7117
7269
|
try {
|
|
7118
7270
|
execFileSync3("systemctl", ["--user", "daemon-reload"], { timeout: 1e4, stdio: "ignore" });
|
|
@@ -7135,7 +7287,7 @@ WantedBy=default.target
|
|
|
7135
7287
|
*/
|
|
7136
7288
|
uninstall() {
|
|
7137
7289
|
const servicePath = this.getServicePath();
|
|
7138
|
-
if (!
|
|
7290
|
+
if (!existsSync8(servicePath)) {
|
|
7139
7291
|
return { removed: false, message: "Service is not installed." };
|
|
7140
7292
|
}
|
|
7141
7293
|
if (this.os === "darwin") {
|
|
@@ -7173,7 +7325,7 @@ WantedBy=default.target
|
|
|
7173
7325
|
status() {
|
|
7174
7326
|
const servicePath = this.getServicePath();
|
|
7175
7327
|
const plat = this.os === "darwin" ? "launchd" : this.os === "linux" ? "systemd" : "unsupported";
|
|
7176
|
-
const installed =
|
|
7328
|
+
const installed = existsSync8(servicePath);
|
|
7177
7329
|
let running = false;
|
|
7178
7330
|
if (installed) {
|
|
7179
7331
|
if (this.os === "darwin") {
|
|
@@ -7228,34 +7380,34 @@ WantedBy=default.target
|
|
|
7228
7380
|
needsRepair() {
|
|
7229
7381
|
if (this.os !== "darwin" && this.os !== "linux") return null;
|
|
7230
7382
|
const servicePath = this.getServicePath();
|
|
7231
|
-
if (!
|
|
7383
|
+
if (!existsSync8(servicePath)) return null;
|
|
7232
7384
|
let serviceContent = "";
|
|
7233
7385
|
try {
|
|
7234
|
-
serviceContent =
|
|
7386
|
+
serviceContent = readFileSync5(servicePath, "utf-8");
|
|
7235
7387
|
} catch {
|
|
7236
7388
|
return { reason: "Service file unreadable" };
|
|
7237
7389
|
}
|
|
7238
|
-
const startScript =
|
|
7390
|
+
const startScript = join11(homedir9(), ".agenticmail", "bin", "start-server.sh");
|
|
7239
7391
|
if (serviceContent.includes(startScript)) {
|
|
7240
|
-
if (!
|
|
7392
|
+
if (!existsSync8(startScript)) {
|
|
7241
7393
|
return { reason: "start-server.sh is missing" };
|
|
7242
7394
|
}
|
|
7243
7395
|
let scriptContent = "";
|
|
7244
7396
|
try {
|
|
7245
|
-
scriptContent =
|
|
7397
|
+
scriptContent = readFileSync5(startScript, "utf-8");
|
|
7246
7398
|
} catch {
|
|
7247
7399
|
return { reason: "start-server.sh unreadable" };
|
|
7248
7400
|
}
|
|
7249
7401
|
const apiPathMatch = scriptContent.match(/(\/[^"\s]+@agenticmail\/api\/dist\/index\.js)/);
|
|
7250
7402
|
if (apiPathMatch) {
|
|
7251
7403
|
const referenced = apiPathMatch[1];
|
|
7252
|
-
if (!
|
|
7404
|
+
if (!existsSync8(referenced)) {
|
|
7253
7405
|
return { reason: `start-server.sh references missing path: ${referenced}` };
|
|
7254
7406
|
}
|
|
7255
7407
|
}
|
|
7256
7408
|
if (/node_modules\/agenticmail\/(?!.*@agenticmail\/cli)/.test(scriptContent)) {
|
|
7257
7409
|
const stale = /(\S*node_modules\/agenticmail\/\S*)/.exec(scriptContent)?.[1];
|
|
7258
|
-
if (stale && !
|
|
7410
|
+
if (stale && !existsSync8(stale)) {
|
|
7259
7411
|
return { reason: `start-server.sh references legacy unscoped path: ${stale}` };
|
|
7260
7412
|
}
|
|
7261
7413
|
}
|
|
@@ -7268,11 +7420,11 @@ WantedBy=default.target
|
|
|
7268
7420
|
return { reason: `Service version drift (current CLI is v${currentVersion})` };
|
|
7269
7421
|
}
|
|
7270
7422
|
}
|
|
7271
|
-
const entryCache =
|
|
7272
|
-
if (
|
|
7423
|
+
const entryCache = join11(homedir9(), ".agenticmail", "api-entry.path");
|
|
7424
|
+
if (existsSync8(entryCache)) {
|
|
7273
7425
|
try {
|
|
7274
|
-
const cached =
|
|
7275
|
-
if (cached && !
|
|
7426
|
+
const cached = readFileSync5(entryCache, "utf-8").trim();
|
|
7427
|
+
if (cached && !existsSync8(cached)) {
|
|
7276
7428
|
return { reason: `Cached API entry path no longer exists: ${cached}` };
|
|
7277
7429
|
}
|
|
7278
7430
|
} catch {
|
|
@@ -7311,8 +7463,8 @@ var SetupManager = class {
|
|
|
7311
7463
|
async ensureStalwart(composePath) {
|
|
7312
7464
|
const status = await this.checker.checkStalwart();
|
|
7313
7465
|
if (status.installed) return;
|
|
7314
|
-
const
|
|
7315
|
-
await this.installer.startStalwart(
|
|
7466
|
+
const path2 = composePath ?? this.getComposePath();
|
|
7467
|
+
await this.installer.startStalwart(path2);
|
|
7316
7468
|
}
|
|
7317
7469
|
async ensureCloudflared() {
|
|
7318
7470
|
return this.installer.installCloudflared();
|
|
@@ -7323,13 +7475,13 @@ var SetupManager = class {
|
|
|
7323
7475
|
* falls back to monorepo location.
|
|
7324
7476
|
*/
|
|
7325
7477
|
getComposePath() {
|
|
7326
|
-
const standalonePath =
|
|
7327
|
-
if (
|
|
7478
|
+
const standalonePath = join12(homedir10(), ".agenticmail", "docker-compose.yml");
|
|
7479
|
+
if (existsSync9(standalonePath)) return standalonePath;
|
|
7328
7480
|
const cwd = process.cwd();
|
|
7329
|
-
const candidates = [cwd,
|
|
7330
|
-
for (const
|
|
7331
|
-
const p =
|
|
7332
|
-
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;
|
|
7333
7485
|
}
|
|
7334
7486
|
return standalonePath;
|
|
7335
7487
|
}
|
|
@@ -7339,19 +7491,19 @@ var SetupManager = class {
|
|
|
7339
7491
|
* Always regenerates Docker files to keep passwords in sync.
|
|
7340
7492
|
*/
|
|
7341
7493
|
initConfig() {
|
|
7342
|
-
const dataDir =
|
|
7343
|
-
const configPath =
|
|
7344
|
-
const envPath =
|
|
7345
|
-
if (
|
|
7494
|
+
const dataDir = join12(homedir10(), ".agenticmail");
|
|
7495
|
+
const configPath = join12(dataDir, "config.json");
|
|
7496
|
+
const envPath = join12(dataDir, ".env");
|
|
7497
|
+
if (existsSync9(configPath)) {
|
|
7346
7498
|
try {
|
|
7347
|
-
const existing = JSON.parse(
|
|
7499
|
+
const existing = JSON.parse(readFileSync6(configPath, "utf-8"));
|
|
7348
7500
|
this.generateDockerFiles(existing);
|
|
7349
7501
|
return { configPath, envPath, config: existing, isNew: false };
|
|
7350
7502
|
} catch {
|
|
7351
7503
|
}
|
|
7352
7504
|
}
|
|
7353
|
-
if (!
|
|
7354
|
-
|
|
7505
|
+
if (!existsSync9(dataDir)) {
|
|
7506
|
+
mkdirSync8(dataDir, { recursive: true });
|
|
7355
7507
|
}
|
|
7356
7508
|
const masterKey = `mk_${randomBytes3(24).toString("hex")}`;
|
|
7357
7509
|
const stalwartPassword = randomBytes3(16).toString("hex");
|
|
@@ -7367,7 +7519,7 @@ var SetupManager = class {
|
|
|
7367
7519
|
api: { port: 3829, host: "127.0.0.1" },
|
|
7368
7520
|
dataDir
|
|
7369
7521
|
};
|
|
7370
|
-
|
|
7522
|
+
writeFileSync7(configPath, JSON.stringify(config, null, 2));
|
|
7371
7523
|
chmodSync2(configPath, 384);
|
|
7372
7524
|
const envContent = `# Auto-generated by agenticmail setup
|
|
7373
7525
|
STALWART_ADMIN_USER=admin
|
|
@@ -7383,7 +7535,7 @@ SMTP_PORT=587
|
|
|
7383
7535
|
IMAP_HOST=localhost
|
|
7384
7536
|
IMAP_PORT=143
|
|
7385
7537
|
`;
|
|
7386
|
-
|
|
7538
|
+
writeFileSync7(envPath, envContent);
|
|
7387
7539
|
chmodSync2(envPath, 384);
|
|
7388
7540
|
this.generateDockerFiles(config);
|
|
7389
7541
|
return { configPath, envPath, config, isNew: true };
|
|
@@ -7393,13 +7545,13 @@ IMAP_PORT=143
|
|
|
7393
7545
|
* with the correct admin password from config.
|
|
7394
7546
|
*/
|
|
7395
7547
|
generateDockerFiles(config) {
|
|
7396
|
-
const dataDir = config.dataDir ||
|
|
7397
|
-
if (!
|
|
7398
|
-
|
|
7548
|
+
const dataDir = config.dataDir || join12(homedir10(), ".agenticmail");
|
|
7549
|
+
if (!existsSync9(dataDir)) {
|
|
7550
|
+
mkdirSync8(dataDir, { recursive: true });
|
|
7399
7551
|
}
|
|
7400
7552
|
const password = config.stalwart?.adminPassword || "changeme";
|
|
7401
|
-
const composePath =
|
|
7402
|
-
|
|
7553
|
+
const composePath = join12(dataDir, "docker-compose.yml");
|
|
7554
|
+
writeFileSync7(composePath, `services:
|
|
7403
7555
|
stalwart:
|
|
7404
7556
|
# Pinned to v0.15.5 \u2014 Stalwart 0.16+ moved its config to JSON
|
|
7405
7557
|
# at /etc/stalwart/config.json (hardcoded into the container
|
|
@@ -7429,8 +7581,8 @@ volumes:
|
|
|
7429
7581
|
stalwart-data:
|
|
7430
7582
|
`);
|
|
7431
7583
|
chmodSync2(composePath, 384);
|
|
7432
|
-
const tomlPath =
|
|
7433
|
-
|
|
7584
|
+
const tomlPath = join12(dataDir, "stalwart.toml");
|
|
7585
|
+
writeFileSync7(tomlPath, `# Stalwart Mail Server \u2014 AgenticMail Configuration
|
|
7434
7586
|
|
|
7435
7587
|
[server]
|
|
7436
7588
|
hostname = "localhost"
|
|
@@ -7486,8 +7638,8 @@ secret = "${password}"
|
|
|
7486
7638
|
* Check if config has already been initialized.
|
|
7487
7639
|
*/
|
|
7488
7640
|
isInitialized() {
|
|
7489
|
-
const configPath =
|
|
7490
|
-
return
|
|
7641
|
+
const configPath = join12(homedir10(), ".agenticmail", "config.json");
|
|
7642
|
+
return existsSync9(configPath);
|
|
7491
7643
|
}
|
|
7492
7644
|
};
|
|
7493
7645
|
|
|
@@ -7526,18 +7678,18 @@ function threadIdFor(input) {
|
|
|
7526
7678
|
|
|
7527
7679
|
// src/threading/thread-cache.ts
|
|
7528
7680
|
import {
|
|
7529
|
-
existsSync as
|
|
7530
|
-
mkdirSync as
|
|
7531
|
-
readFileSync as
|
|
7532
|
-
writeFileSync as
|
|
7681
|
+
existsSync as existsSync10,
|
|
7682
|
+
mkdirSync as mkdirSync9,
|
|
7683
|
+
readFileSync as readFileSync7,
|
|
7684
|
+
writeFileSync as writeFileSync8,
|
|
7533
7685
|
readdirSync,
|
|
7534
7686
|
statSync,
|
|
7535
7687
|
rmSync,
|
|
7536
|
-
renameSync
|
|
7688
|
+
renameSync as renameSync3
|
|
7537
7689
|
} from "fs";
|
|
7538
|
-
import { homedir as
|
|
7539
|
-
import { join as
|
|
7540
|
-
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");
|
|
7541
7693
|
var DEFAULT_K_MESSAGES = 10;
|
|
7542
7694
|
var DEFAULT_LRU_CAP = 5e3;
|
|
7543
7695
|
var PREVIEW_MAX_CHARS = 240;
|
|
@@ -7550,18 +7702,18 @@ var ThreadCache = class {
|
|
|
7550
7702
|
this.k = opts.k ?? DEFAULT_K_MESSAGES;
|
|
7551
7703
|
this.lruCap = opts.lruCap ?? DEFAULT_LRU_CAP;
|
|
7552
7704
|
try {
|
|
7553
|
-
|
|
7705
|
+
mkdirSync9(this.dir, { recursive: true });
|
|
7554
7706
|
} catch {
|
|
7555
7707
|
}
|
|
7556
7708
|
}
|
|
7557
7709
|
pathFor(threadId) {
|
|
7558
|
-
return
|
|
7710
|
+
return join13(this.dir, `${threadId}.json`);
|
|
7559
7711
|
}
|
|
7560
7712
|
read(threadId) {
|
|
7561
7713
|
const p = this.pathFor(threadId);
|
|
7562
|
-
if (!
|
|
7714
|
+
if (!existsSync10(p)) return null;
|
|
7563
7715
|
try {
|
|
7564
|
-
const raw =
|
|
7716
|
+
const raw = readFileSync7(p, "utf-8");
|
|
7565
7717
|
return JSON.parse(raw);
|
|
7566
7718
|
} catch {
|
|
7567
7719
|
try {
|
|
@@ -7626,8 +7778,8 @@ var ThreadCache = class {
|
|
|
7626
7778
|
writeAtomic(threadId, entry) {
|
|
7627
7779
|
const p = this.pathFor(threadId);
|
|
7628
7780
|
const tmp = `${p}.tmp`;
|
|
7629
|
-
|
|
7630
|
-
|
|
7781
|
+
writeFileSync8(tmp, JSON.stringify(entry), "utf-8");
|
|
7782
|
+
renameSync3(tmp, p);
|
|
7631
7783
|
}
|
|
7632
7784
|
/**
|
|
7633
7785
|
* Best-effort LRU eviction. Runs at most every 256 writes (we
|
|
@@ -7645,7 +7797,7 @@ var ThreadCache = class {
|
|
|
7645
7797
|
}
|
|
7646
7798
|
if (files.length <= this.lruCap) return;
|
|
7647
7799
|
const stats = files.map((f) => {
|
|
7648
|
-
const p =
|
|
7800
|
+
const p = join13(this.dir, f);
|
|
7649
7801
|
try {
|
|
7650
7802
|
return { p, mtime: statSync(p).mtimeMs };
|
|
7651
7803
|
} catch {
|
|
@@ -7676,36 +7828,36 @@ function dedupAndCap(messages, k) {
|
|
|
7676
7828
|
|
|
7677
7829
|
// src/threading/agent-memory.ts
|
|
7678
7830
|
import {
|
|
7679
|
-
existsSync as
|
|
7680
|
-
mkdirSync as
|
|
7681
|
-
readFileSync as
|
|
7682
|
-
writeFileSync as
|
|
7831
|
+
existsSync as existsSync11,
|
|
7832
|
+
mkdirSync as mkdirSync10,
|
|
7833
|
+
readFileSync as readFileSync8,
|
|
7834
|
+
writeFileSync as writeFileSync9,
|
|
7683
7835
|
rmSync as rmSync2,
|
|
7684
|
-
renameSync as
|
|
7836
|
+
renameSync as renameSync4
|
|
7685
7837
|
} from "fs";
|
|
7686
|
-
import { homedir as
|
|
7687
|
-
import { join as
|
|
7688
|
-
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");
|
|
7689
7841
|
var AgentMemoryStore = class {
|
|
7690
7842
|
dir;
|
|
7691
7843
|
constructor(opts = {}) {
|
|
7692
7844
|
this.dir = opts.memoryDir ?? MEMORY_DIR_DEFAULT;
|
|
7693
7845
|
try {
|
|
7694
|
-
|
|
7846
|
+
mkdirSync10(this.dir, { recursive: true });
|
|
7695
7847
|
} catch {
|
|
7696
7848
|
}
|
|
7697
7849
|
}
|
|
7698
7850
|
dirFor(agentId) {
|
|
7699
|
-
return
|
|
7851
|
+
return join14(this.dir, sanitizeId(agentId));
|
|
7700
7852
|
}
|
|
7701
7853
|
pathFor(agentId, threadId) {
|
|
7702
|
-
return
|
|
7854
|
+
return join14(this.dirFor(agentId), `${sanitizeId(threadId)}.md`);
|
|
7703
7855
|
}
|
|
7704
7856
|
read(agentId, threadId) {
|
|
7705
7857
|
const p = this.pathFor(agentId, threadId);
|
|
7706
|
-
if (!
|
|
7858
|
+
if (!existsSync11(p)) return null;
|
|
7707
7859
|
try {
|
|
7708
|
-
const raw =
|
|
7860
|
+
const raw = readFileSync8(p, "utf-8");
|
|
7709
7861
|
const parsed = parse(raw);
|
|
7710
7862
|
return { ...parsed, raw };
|
|
7711
7863
|
} catch {
|
|
@@ -7715,14 +7867,14 @@ var AgentMemoryStore = class {
|
|
|
7715
7867
|
write(agentId, threadId, fields) {
|
|
7716
7868
|
const agentDir = this.dirFor(agentId);
|
|
7717
7869
|
try {
|
|
7718
|
-
|
|
7870
|
+
mkdirSync10(agentDir, { recursive: true });
|
|
7719
7871
|
} catch {
|
|
7720
7872
|
}
|
|
7721
7873
|
const body = render({ ...fields, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
7722
7874
|
const p = this.pathFor(agentId, threadId);
|
|
7723
7875
|
const tmp = `${p}.tmp`;
|
|
7724
|
-
|
|
7725
|
-
|
|
7876
|
+
writeFileSync9(tmp, body, "utf-8");
|
|
7877
|
+
renameSync4(tmp, p);
|
|
7726
7878
|
}
|
|
7727
7879
|
delete(agentId, threadId) {
|
|
7728
7880
|
try {
|
|
@@ -7790,6 +7942,7 @@ export {
|
|
|
7790
7942
|
CloudflareClient,
|
|
7791
7943
|
DEFAULT_AGENT_NAME,
|
|
7792
7944
|
DEFAULT_AGENT_ROLE,
|
|
7945
|
+
DEFAULT_SESSION_MAX_AGE_MS,
|
|
7793
7946
|
DNSConfigurator,
|
|
7794
7947
|
DependencyChecker,
|
|
7795
7948
|
DependencyInstaller,
|
|
@@ -7826,12 +7979,18 @@ export {
|
|
|
7826
7979
|
ensureDataDir,
|
|
7827
7980
|
extractVerificationCode,
|
|
7828
7981
|
flushTelemetry,
|
|
7982
|
+
forgetHostSession,
|
|
7829
7983
|
getDatabase,
|
|
7984
|
+
getOperatorEmail,
|
|
7985
|
+
hostSessionStoragePath,
|
|
7830
7986
|
isInternalEmail,
|
|
7987
|
+
isSessionFresh,
|
|
7831
7988
|
isValidPhoneNumber,
|
|
7989
|
+
loadHostSession,
|
|
7832
7990
|
normalizeAddress,
|
|
7833
7991
|
normalizePhoneNumber,
|
|
7834
7992
|
normalizeSubject,
|
|
7993
|
+
operatorPrefsStoragePath,
|
|
7835
7994
|
parseEmail,
|
|
7836
7995
|
parseGoogleVoiceSms,
|
|
7837
7996
|
recordToolCall,
|
|
@@ -7841,8 +8000,10 @@ export {
|
|
|
7841
8000
|
safeJoin,
|
|
7842
8001
|
sanitizeEmail,
|
|
7843
8002
|
saveConfig,
|
|
8003
|
+
saveHostSession,
|
|
7844
8004
|
scanOutboundEmail,
|
|
7845
8005
|
scoreEmail,
|
|
8006
|
+
setOperatorEmail,
|
|
7846
8007
|
setTelemetryVersion,
|
|
7847
8008
|
startRelayBridge,
|
|
7848
8009
|
threadIdFor,
|