@agenticmail/core 0.9.4 → 0.9.5
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 +270 -91
- package/dist/index.d.cts +249 -1
- package/dist/index.d.ts +249 -1
- package/dist/index.js +259 -90
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -78,7 +78,7 @@ var MailSender = class {
|
|
|
78
78
|
const code = err?.responseCode ?? err?.code;
|
|
79
79
|
const isTransient = typeof code === "number" && code >= 400 && code < 500 || code === "ECONNRESET" || code === "ETIMEDOUT" || code === "ESOCKET";
|
|
80
80
|
if (!isTransient || attempt === MAX_RETRIES) throw err;
|
|
81
|
-
await new Promise((
|
|
81
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1e3 * (attempt + 1)));
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
throw lastError;
|
|
@@ -1010,20 +1010,20 @@ var StalwartAdmin = class {
|
|
|
1010
1010
|
}
|
|
1011
1011
|
try {
|
|
1012
1012
|
const net = await import("net");
|
|
1013
|
-
return await new Promise((
|
|
1013
|
+
return await new Promise((resolve2) => {
|
|
1014
1014
|
const smtpPort = parseInt(process.env.SMTP_PORT || "25", 10);
|
|
1015
1015
|
const smtpHost = process.env.SMTP_HOST || "localhost";
|
|
1016
1016
|
const socket = net.createConnection({ host: smtpHost, port: smtpPort, timeout: 3e3 }, () => {
|
|
1017
1017
|
socket.destroy();
|
|
1018
|
-
|
|
1018
|
+
resolve2(true);
|
|
1019
1019
|
});
|
|
1020
1020
|
socket.on("error", () => {
|
|
1021
1021
|
socket.destroy();
|
|
1022
|
-
|
|
1022
|
+
resolve2(false);
|
|
1023
1023
|
});
|
|
1024
1024
|
socket.on("timeout", () => {
|
|
1025
1025
|
socket.destroy();
|
|
1026
|
-
|
|
1026
|
+
resolve2(false);
|
|
1027
1027
|
});
|
|
1028
1028
|
});
|
|
1029
1029
|
} catch {
|
|
@@ -1095,8 +1095,8 @@ var StalwartAdmin = class {
|
|
|
1095
1095
|
}
|
|
1096
1096
|
const { readFileSync: readFileSync7, writeFileSync: writeFileSync8 } = await import("fs");
|
|
1097
1097
|
const { homedir: homedir11 } = await import("os");
|
|
1098
|
-
const { join:
|
|
1099
|
-
const configPath =
|
|
1098
|
+
const { join: join13 } = await import("path");
|
|
1099
|
+
const configPath = join13(homedir11(), ".agenticmail", "stalwart.toml");
|
|
1100
1100
|
try {
|
|
1101
1101
|
let config = readFileSync7(configPath, "utf-8");
|
|
1102
1102
|
config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
|
|
@@ -1110,14 +1110,14 @@ var StalwartAdmin = class {
|
|
|
1110
1110
|
/** Path to the host-side stalwart.toml (mounted read-only into container) */
|
|
1111
1111
|
get configPath() {
|
|
1112
1112
|
const { homedir: homedir11 } = __require("os");
|
|
1113
|
-
const { join:
|
|
1114
|
-
return
|
|
1113
|
+
const { join: join13 } = __require("path");
|
|
1114
|
+
return join13(homedir11(), ".agenticmail", "stalwart.toml");
|
|
1115
1115
|
}
|
|
1116
1116
|
/** Path to host-side DKIM key directory */
|
|
1117
1117
|
get dkimDir() {
|
|
1118
1118
|
const { homedir: homedir11 } = __require("os");
|
|
1119
|
-
const { join:
|
|
1120
|
-
return
|
|
1119
|
+
const { join: join13 } = __require("path");
|
|
1120
|
+
return join13(homedir11(), ".agenticmail");
|
|
1121
1121
|
}
|
|
1122
1122
|
/**
|
|
1123
1123
|
* Create/reuse a DKIM signing key for a domain.
|
|
@@ -1220,9 +1220,9 @@ var StalwartAdmin = class {
|
|
|
1220
1220
|
async configureOutboundRelay(config) {
|
|
1221
1221
|
const { readFileSync: readFileSync7, writeFileSync: writeFileSync8 } = await import("fs");
|
|
1222
1222
|
const { homedir: homedir11 } = await import("os");
|
|
1223
|
-
const { join:
|
|
1223
|
+
const { join: join13 } = await import("path");
|
|
1224
1224
|
const routeName = config.routeName ?? "gmail";
|
|
1225
|
-
const tomlPath =
|
|
1225
|
+
const tomlPath = join13(homedir11(), ".agenticmail", "stalwart.toml");
|
|
1226
1226
|
let toml = readFileSync7(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\[|$)/, "");
|
|
@@ -2327,7 +2327,8 @@ var OUTBOUND_TEXT_RULES = [
|
|
|
2327
2327
|
var HIGH_RISK_EXTENSIONS = /* @__PURE__ */ new Set([".pem", ".key", ".p12", ".pfx", ".env", ".credentials", ".keystore", ".jks", ".p8"]);
|
|
2328
2328
|
var MEDIUM_RISK_EXTENSIONS = /* @__PURE__ */ new Set([".db", ".sqlite", ".sqlite3", ".sql", ".csv", ".tsv", ".json", ".yml", ".yaml", ".conf", ".config", ".ini"]);
|
|
2329
2329
|
function stripHtmlTags(html) {
|
|
2330
|
-
|
|
2330
|
+
const bounded = html.length > 1048576 ? html.slice(0, 1048576) : html;
|
|
2331
|
+
return bounded.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/�?39;/g, "'").replace(/ /g, " ").replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))).replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(parseInt(dec, 10)));
|
|
2331
2332
|
}
|
|
2332
2333
|
var TEXT_SCANNABLE_TYPES = /* @__PURE__ */ new Set([
|
|
2333
2334
|
"text/plain",
|
|
@@ -3859,7 +3860,7 @@ var CloudflareClient = class {
|
|
|
3859
3860
|
// --- Workers methods ---
|
|
3860
3861
|
/** Deploy an Email Worker script (ES module format) */
|
|
3861
3862
|
async deployEmailWorker(scriptName, scriptContent, envVars = {}) {
|
|
3862
|
-
const url = `${CF_API_BASE}/accounts/${this.accountId}/workers/scripts/${scriptName}`;
|
|
3863
|
+
const url = `${CF_API_BASE}/accounts/${encodeURIComponent(this.accountId)}/workers/scripts/${encodeURIComponent(scriptName)}`;
|
|
3863
3864
|
const bindings = Object.entries(envVars).map(([name, text2]) => ({
|
|
3864
3865
|
type: "plain_text",
|
|
3865
3866
|
name,
|
|
@@ -4277,7 +4278,7 @@ var TunnelManager = class {
|
|
|
4277
4278
|
detached: false,
|
|
4278
4279
|
env: { ...process.env, TUNNEL_TOKEN: tunnelToken }
|
|
4279
4280
|
});
|
|
4280
|
-
await new Promise((
|
|
4281
|
+
await new Promise((resolve2, reject) => {
|
|
4281
4282
|
let resolved = false;
|
|
4282
4283
|
const timeout = setTimeout(() => {
|
|
4283
4284
|
if (!resolved) {
|
|
@@ -4291,7 +4292,7 @@ var TunnelManager = class {
|
|
|
4291
4292
|
resolved = true;
|
|
4292
4293
|
clearTimeout(timeout);
|
|
4293
4294
|
this.running = true;
|
|
4294
|
-
|
|
4295
|
+
resolve2();
|
|
4295
4296
|
}
|
|
4296
4297
|
};
|
|
4297
4298
|
this.process.stderr?.on("data", onData);
|
|
@@ -4330,8 +4331,8 @@ var TunnelManager = class {
|
|
|
4330
4331
|
this.process = null;
|
|
4331
4332
|
p.kill("SIGTERM");
|
|
4332
4333
|
await Promise.race([
|
|
4333
|
-
new Promise((
|
|
4334
|
-
new Promise((
|
|
4334
|
+
new Promise((resolve2) => p.on("exit", () => resolve2())),
|
|
4335
|
+
new Promise((resolve2) => setTimeout(resolve2, 5e3))
|
|
4335
4336
|
]);
|
|
4336
4337
|
this.running = false;
|
|
4337
4338
|
}
|
|
@@ -4386,7 +4387,10 @@ function parseGoogleVoiceSms(emailBody, emailFrom) {
|
|
|
4386
4387
|
if (!emailBody || typeof emailBody !== "string") return null;
|
|
4387
4388
|
if (!emailFrom || typeof emailFrom !== "string") return null;
|
|
4388
4389
|
const fromLower = emailFrom.toLowerCase();
|
|
4389
|
-
const
|
|
4390
|
+
const atIdx = fromLower.lastIndexOf("@");
|
|
4391
|
+
const domain = atIdx >= 0 ? fromLower.slice(atIdx + 1).replace(/[>"'\s].*$/, "") : "";
|
|
4392
|
+
const isGoogleDomain = domain === "google.com" || domain.endsWith(".google.com");
|
|
4393
|
+
const isGoogleVoice = isGoogleDomain && (fromLower.startsWith("voice-noreply@") || domain === "txt.voice.google.com" || domain === "voice.google.com" || domain.endsWith(".voice.google.com") || fromLower.includes("voice"));
|
|
4390
4394
|
if (!isGoogleVoice) return null;
|
|
4391
4395
|
let text = emailBody.replace(/<br\s*\/?>/gi, "\n").replace(/<\/p>/gi, "\n").replace(/<\/div>/gi, "\n").replace(/<[^>]+>/g, "").replace(/ /gi, " ").replace(/&/gi, "&").replace(/</gi, "<").replace(/>/gi, ">").replace(/"/gi, '"').replace(/'/gi, "'").trim();
|
|
4392
4396
|
let from = "";
|
|
@@ -5417,6 +5421,15 @@ var GatewayManager = class {
|
|
|
5417
5421
|
bcc: mail.bcc ? Array.isArray(mail.bcc) ? mail.bcc.join(", ") : mail.bcc : void 0,
|
|
5418
5422
|
subject: mail.subject,
|
|
5419
5423
|
text: mail.text || void 0,
|
|
5424
|
+
// The `html` field is the literal HTML body of the outbound
|
|
5425
|
+
// mail — by design it is whatever the sender chose to compose.
|
|
5426
|
+
// CodeQL `js/xss` flags this because the value flows from user
|
|
5427
|
+
// input, but nodemailer is the SMTP serializer, not an HTML
|
|
5428
|
+
// renderer; XSS would only occur if the recipient's MUA
|
|
5429
|
+
// executed the body, which is outside our trust boundary.
|
|
5430
|
+
// The outbound-guard (packages/core/src/mail/outbound-guard.ts)
|
|
5431
|
+
// already scores HTML bodies for suspicious patterns at the
|
|
5432
|
+
// pre-send step. lgtm[js/xss]
|
|
5420
5433
|
html: mail.html || void 0,
|
|
5421
5434
|
replyTo: mail.replyTo || from,
|
|
5422
5435
|
inReplyTo: mail.inReplyTo || void 0,
|
|
@@ -5778,11 +5791,11 @@ var RelayBridge = class {
|
|
|
5778
5791
|
this.options = options;
|
|
5779
5792
|
}
|
|
5780
5793
|
async start() {
|
|
5781
|
-
return new Promise((
|
|
5794
|
+
return new Promise((resolve2, reject) => {
|
|
5782
5795
|
this.server = createServer((req, res) => this.handleRequest(req, res));
|
|
5783
5796
|
this.server.listen(this.options.port, "127.0.0.1", () => {
|
|
5784
5797
|
console.log(`[RelayBridge] Listening on 127.0.0.1:${this.options.port}`);
|
|
5785
|
-
|
|
5798
|
+
resolve2();
|
|
5786
5799
|
});
|
|
5787
5800
|
this.server.on("error", reject);
|
|
5788
5801
|
});
|
|
@@ -5984,16 +5997,161 @@ try {
|
|
|
5984
5997
|
} catch {
|
|
5985
5998
|
}
|
|
5986
5999
|
|
|
6000
|
+
// src/util/safe-path.ts
|
|
6001
|
+
import { isAbsolute, join as join6, resolve, sep } from "path";
|
|
6002
|
+
var PathTraversalError = class extends Error {
|
|
6003
|
+
constructor(baseDir, parts) {
|
|
6004
|
+
super(
|
|
6005
|
+
`path traversal attempt: ${JSON.stringify(parts)} resolves outside ${baseDir}`
|
|
6006
|
+
);
|
|
6007
|
+
this.baseDir = baseDir;
|
|
6008
|
+
this.parts = parts;
|
|
6009
|
+
this.name = "PathTraversalError";
|
|
6010
|
+
}
|
|
6011
|
+
};
|
|
6012
|
+
function safeJoin(baseDir, ...partsAndOpts) {
|
|
6013
|
+
let opts = {};
|
|
6014
|
+
const parts = [];
|
|
6015
|
+
for (const p of partsAndOpts) {
|
|
6016
|
+
if (typeof p === "string") {
|
|
6017
|
+
parts.push(p);
|
|
6018
|
+
} else if (p && typeof p === "object") {
|
|
6019
|
+
opts = p;
|
|
6020
|
+
}
|
|
6021
|
+
}
|
|
6022
|
+
if (!opts.allowAbsolute) {
|
|
6023
|
+
for (const part of parts) {
|
|
6024
|
+
if (isAbsolute(part)) {
|
|
6025
|
+
throw new PathTraversalError(baseDir, parts);
|
|
6026
|
+
}
|
|
6027
|
+
}
|
|
6028
|
+
}
|
|
6029
|
+
const resolvedBase = resolve(baseDir);
|
|
6030
|
+
const resolved = resolve(resolvedBase, ...parts);
|
|
6031
|
+
if (resolved !== resolvedBase && !resolved.startsWith(resolvedBase + sep)) {
|
|
6032
|
+
throw new PathTraversalError(baseDir, parts);
|
|
6033
|
+
}
|
|
6034
|
+
return resolved;
|
|
6035
|
+
}
|
|
6036
|
+
function tryJoin(baseDir, ...parts) {
|
|
6037
|
+
try {
|
|
6038
|
+
return safeJoin(baseDir, ...parts);
|
|
6039
|
+
} catch (err) {
|
|
6040
|
+
if (err instanceof PathTraversalError) return null;
|
|
6041
|
+
throw err;
|
|
6042
|
+
}
|
|
6043
|
+
}
|
|
6044
|
+
function assertWithinBase(baseDir, candidate) {
|
|
6045
|
+
const resolvedBase = resolve(baseDir);
|
|
6046
|
+
const resolvedCandidate = resolve(candidate);
|
|
6047
|
+
if (resolvedCandidate !== resolvedBase && !resolvedCandidate.startsWith(resolvedBase + sep)) {
|
|
6048
|
+
throw new PathTraversalError(baseDir, [candidate]);
|
|
6049
|
+
}
|
|
6050
|
+
return resolvedCandidate;
|
|
6051
|
+
}
|
|
6052
|
+
|
|
6053
|
+
// src/util/redact.ts
|
|
6054
|
+
var REDACTED = "***";
|
|
6055
|
+
function redactSecret(value) {
|
|
6056
|
+
if (typeof value !== "string") return REDACTED;
|
|
6057
|
+
if (value.length === 0) return REDACTED;
|
|
6058
|
+
for (const prefix of ["mk_", "ak_", "sk-", "pk_", "tok_"]) {
|
|
6059
|
+
if (value.startsWith(prefix)) return `${prefix}${REDACTED}`;
|
|
6060
|
+
}
|
|
6061
|
+
return REDACTED;
|
|
6062
|
+
}
|
|
6063
|
+
var SENSITIVE_KEY_PATTERNS = [
|
|
6064
|
+
/key$/i,
|
|
6065
|
+
/secret/i,
|
|
6066
|
+
/password/i,
|
|
6067
|
+
/passwd/i,
|
|
6068
|
+
/token/i,
|
|
6069
|
+
/authorization/i,
|
|
6070
|
+
/bearer/i,
|
|
6071
|
+
/\bapikey\b/i,
|
|
6072
|
+
/\bapi_key\b/i,
|
|
6073
|
+
/\bmasterkey\b/i,
|
|
6074
|
+
/\bmaster_key\b/i
|
|
6075
|
+
];
|
|
6076
|
+
function looksSensitive(key) {
|
|
6077
|
+
return SENSITIVE_KEY_PATTERNS.some((p) => p.test(key));
|
|
6078
|
+
}
|
|
6079
|
+
function redactObject(input, _depth = 0) {
|
|
6080
|
+
if (_depth > 12) return input;
|
|
6081
|
+
if (input === null || input === void 0) return input;
|
|
6082
|
+
if (typeof input !== "object") return input;
|
|
6083
|
+
if (Array.isArray(input)) {
|
|
6084
|
+
return input.map((v) => redactObject(v, _depth + 1));
|
|
6085
|
+
}
|
|
6086
|
+
const proto = Object.getPrototypeOf(input);
|
|
6087
|
+
if (proto !== Object.prototype && proto !== null) return input;
|
|
6088
|
+
const out = {};
|
|
6089
|
+
for (const [k, v] of Object.entries(input)) {
|
|
6090
|
+
if (looksSensitive(k)) {
|
|
6091
|
+
out[k] = typeof v === "string" ? redactSecret(v) : REDACTED;
|
|
6092
|
+
} else {
|
|
6093
|
+
out[k] = redactObject(v, _depth + 1);
|
|
6094
|
+
}
|
|
6095
|
+
}
|
|
6096
|
+
return out;
|
|
6097
|
+
}
|
|
6098
|
+
|
|
6099
|
+
// src/util/safe-url.ts
|
|
6100
|
+
var UnsafeApiUrlError = class extends Error {
|
|
6101
|
+
constructor(raw, reason) {
|
|
6102
|
+
super(`unsafe AgenticMail API URL ${JSON.stringify(raw)}: ${reason}`);
|
|
6103
|
+
this.raw = raw;
|
|
6104
|
+
this.reason = reason;
|
|
6105
|
+
this.name = "UnsafeApiUrlError";
|
|
6106
|
+
}
|
|
6107
|
+
};
|
|
6108
|
+
var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
|
|
6109
|
+
"169.254.169.254",
|
|
6110
|
+
// AWS / Azure / GCP IPv4 metadata
|
|
6111
|
+
"fd00:ec2::254",
|
|
6112
|
+
// AWS IPv6 metadata
|
|
6113
|
+
"metadata.google.internal",
|
|
6114
|
+
// GCP DNS
|
|
6115
|
+
"metadata.azure.internal"
|
|
6116
|
+
// Azure DNS
|
|
6117
|
+
]);
|
|
6118
|
+
function validateApiUrl(raw) {
|
|
6119
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
6120
|
+
throw new UnsafeApiUrlError(String(raw), "must be a non-empty string");
|
|
6121
|
+
}
|
|
6122
|
+
let parsed;
|
|
6123
|
+
try {
|
|
6124
|
+
parsed = new URL(raw);
|
|
6125
|
+
} catch (err) {
|
|
6126
|
+
throw new UnsafeApiUrlError(raw, `unparseable: ${err.message}`);
|
|
6127
|
+
}
|
|
6128
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
6129
|
+
throw new UnsafeApiUrlError(raw, `unsupported protocol: ${parsed.protocol}`);
|
|
6130
|
+
}
|
|
6131
|
+
const host = parsed.hostname.toLowerCase().replace(/\.$/, "");
|
|
6132
|
+
if (BLOCKED_HOSTS.has(host)) {
|
|
6133
|
+
throw new UnsafeApiUrlError(raw, `blocked metadata host: ${host}`);
|
|
6134
|
+
}
|
|
6135
|
+
if (parsed.username || parsed.password) {
|
|
6136
|
+
throw new UnsafeApiUrlError(raw, "embedded credentials are not supported");
|
|
6137
|
+
}
|
|
6138
|
+
return parsed.origin;
|
|
6139
|
+
}
|
|
6140
|
+
function buildApiUrl(baseOrigin, pathAndQuery) {
|
|
6141
|
+
const path = pathAndQuery.startsWith("/") ? pathAndQuery : `/${pathAndQuery}`;
|
|
6142
|
+
return new URL(path, baseOrigin + "/").toString();
|
|
6143
|
+
}
|
|
6144
|
+
|
|
5987
6145
|
// src/setup/index.ts
|
|
5988
6146
|
import { randomBytes as randomBytes3 } from "crypto";
|
|
5989
6147
|
import { existsSync as existsSync7, readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync6, chmodSync as chmodSync2 } from "fs";
|
|
5990
|
-
import { join as
|
|
6148
|
+
import { join as join10 } from "path";
|
|
5991
6149
|
import { homedir as homedir8 } from "os";
|
|
5992
6150
|
|
|
5993
6151
|
// src/setup/deps.ts
|
|
5994
6152
|
import { execFileSync } from "child_process";
|
|
5995
6153
|
import { existsSync as existsSync4 } from "fs";
|
|
5996
|
-
import { join as
|
|
6154
|
+
import { join as join7 } from "path";
|
|
5997
6155
|
import { homedir as homedir5 } from "os";
|
|
5998
6156
|
var DependencyChecker = class {
|
|
5999
6157
|
async checkAll() {
|
|
@@ -6044,7 +6202,7 @@ var DependencyChecker = class {
|
|
|
6044
6202
|
}
|
|
6045
6203
|
}
|
|
6046
6204
|
async checkCloudflared() {
|
|
6047
|
-
const binPath =
|
|
6205
|
+
const binPath = join7(homedir5(), ".agenticmail", "bin", "cloudflared");
|
|
6048
6206
|
if (existsSync4(binPath)) {
|
|
6049
6207
|
let version;
|
|
6050
6208
|
try {
|
|
@@ -6069,12 +6227,12 @@ var DependencyChecker = class {
|
|
|
6069
6227
|
import { execFileSync as execFileSync2, execSync, spawn as spawnChild } from "child_process";
|
|
6070
6228
|
import { existsSync as existsSync5, mkdirSync as mkdirSync4, symlinkSync } from "fs";
|
|
6071
6229
|
import { writeFile, rename, chmod as chmod2, mkdir as mkdir2, unlink } from "fs/promises";
|
|
6072
|
-
import { join as
|
|
6230
|
+
import { join as join8 } from "path";
|
|
6073
6231
|
import { homedir as homedir6, platform as platform3, arch as arch2 } from "os";
|
|
6074
6232
|
function runShellWithRollingOutput(cmd, opts = {}) {
|
|
6075
6233
|
const maxLines = opts.maxLines ?? 20;
|
|
6076
6234
|
const timeout = opts.timeout ?? 3e5;
|
|
6077
|
-
return new Promise((
|
|
6235
|
+
return new Promise((resolve2, reject) => {
|
|
6078
6236
|
const child = spawnChild("sh", ["-c", cmd], {
|
|
6079
6237
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6080
6238
|
timeout
|
|
@@ -6116,7 +6274,7 @@ function runShellWithRollingOutput(cmd, opts = {}) {
|
|
|
6116
6274
|
}
|
|
6117
6275
|
process.stdout.write(`\x1B[${displayedCount}A`);
|
|
6118
6276
|
}
|
|
6119
|
-
|
|
6277
|
+
resolve2({ exitCode: code ?? 1, fullOutput });
|
|
6120
6278
|
});
|
|
6121
6279
|
child.on("error", (err) => {
|
|
6122
6280
|
if (displayedCount > 0) {
|
|
@@ -6132,7 +6290,7 @@ function runShellWithRollingOutput(cmd, opts = {}) {
|
|
|
6132
6290
|
}
|
|
6133
6291
|
function runSilent(command, args, opts = {}) {
|
|
6134
6292
|
const timeout = opts.timeout ?? 3e5;
|
|
6135
|
-
return new Promise((
|
|
6293
|
+
return new Promise((resolve2, reject) => {
|
|
6136
6294
|
const child = spawnChild(command, args, {
|
|
6137
6295
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6138
6296
|
timeout
|
|
@@ -6144,7 +6302,7 @@ function runSilent(command, args, opts = {}) {
|
|
|
6144
6302
|
child.stderr?.on("data", (d) => {
|
|
6145
6303
|
fullOutput += d.toString();
|
|
6146
6304
|
});
|
|
6147
|
-
child.on("close", (code) =>
|
|
6305
|
+
child.on("close", (code) => resolve2({ exitCode: code ?? 1, fullOutput }));
|
|
6148
6306
|
child.on("error", reject);
|
|
6149
6307
|
});
|
|
6150
6308
|
}
|
|
@@ -6246,8 +6404,8 @@ var DependencyInstaller = class {
|
|
|
6246
6404
|
try {
|
|
6247
6405
|
const composeBin = execFileSync2("which", ["docker-compose"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6248
6406
|
if (!composeBin) return;
|
|
6249
|
-
const pluginDir =
|
|
6250
|
-
const pluginPath =
|
|
6407
|
+
const pluginDir = join8(homedir6(), ".docker", "cli-plugins");
|
|
6408
|
+
const pluginPath = join8(pluginDir, "docker-compose");
|
|
6251
6409
|
if (existsSync5(pluginPath)) return;
|
|
6252
6410
|
try {
|
|
6253
6411
|
mkdirSync4(pluginDir, { recursive: true });
|
|
@@ -6313,9 +6471,9 @@ var DependencyInstaller = class {
|
|
|
6313
6471
|
return;
|
|
6314
6472
|
}
|
|
6315
6473
|
this.onProgress("__progress__:5:Installing Docker Engine...");
|
|
6316
|
-
const tmpDir =
|
|
6474
|
+
const tmpDir = join8(homedir6(), ".agenticmail", "tmp");
|
|
6317
6475
|
await mkdir2(tmpDir, { recursive: true });
|
|
6318
|
-
const scriptPath =
|
|
6476
|
+
const scriptPath = join8(tmpDir, "install-docker.sh");
|
|
6319
6477
|
const dlResult = await runShellWithRollingOutput(
|
|
6320
6478
|
`curl -fsSL https://get.docker.com -o "${scriptPath}" && sudo sh "${scriptPath}"`,
|
|
6321
6479
|
{ timeout: 3e5 }
|
|
@@ -6383,7 +6541,7 @@ var DependencyInstaller = class {
|
|
|
6383
6541
|
}
|
|
6384
6542
|
try {
|
|
6385
6543
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
6386
|
-
const dockerExe =
|
|
6544
|
+
const dockerExe = join8(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
6387
6545
|
if (existsSync5(dockerExe)) {
|
|
6388
6546
|
execSync(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
6389
6547
|
}
|
|
@@ -6434,7 +6592,7 @@ var DependencyInstaller = class {
|
|
|
6434
6592
|
this.onProgress("__progress__:70:Docker Desktop installed. Starting...");
|
|
6435
6593
|
try {
|
|
6436
6594
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
6437
|
-
const dockerExe =
|
|
6595
|
+
const dockerExe = join8(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
6438
6596
|
if (existsSync5(dockerExe)) {
|
|
6439
6597
|
execSync(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
6440
6598
|
}
|
|
@@ -6531,9 +6689,9 @@ var DependencyInstaller = class {
|
|
|
6531
6689
|
*/
|
|
6532
6690
|
async installCloudflared() {
|
|
6533
6691
|
const os = platform3();
|
|
6534
|
-
const binDir =
|
|
6692
|
+
const binDir = join8(homedir6(), ".agenticmail", "bin");
|
|
6535
6693
|
const binName = os === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
6536
|
-
const binPath =
|
|
6694
|
+
const binPath = join8(binDir, binName);
|
|
6537
6695
|
if (existsSync5(binPath)) {
|
|
6538
6696
|
return binPath;
|
|
6539
6697
|
}
|
|
@@ -6563,7 +6721,7 @@ var DependencyInstaller = class {
|
|
|
6563
6721
|
}
|
|
6564
6722
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
6565
6723
|
if (os === "darwin") {
|
|
6566
|
-
const tgzPath =
|
|
6724
|
+
const tgzPath = join8(binDir, "cloudflared.tgz");
|
|
6567
6725
|
await writeFile(tgzPath, buffer);
|
|
6568
6726
|
try {
|
|
6569
6727
|
execFileSync2("tar", ["-xzf", tgzPath, "-C", binDir, "cloudflared"], { timeout: 15e3, stdio: "ignore" });
|
|
@@ -6599,7 +6757,7 @@ var DependencyInstaller = class {
|
|
|
6599
6757
|
// src/setup/service.ts
|
|
6600
6758
|
import { execFileSync as execFileSync3, execSync as execSync2 } from "child_process";
|
|
6601
6759
|
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync4, unlinkSync, mkdirSync as mkdirSync5, chmodSync } from "fs";
|
|
6602
|
-
import { join as
|
|
6760
|
+
import { join as join9 } from "path";
|
|
6603
6761
|
import { homedir as homedir7, platform as platform4 } from "os";
|
|
6604
6762
|
import { createRequire as createRequire2 } from "module";
|
|
6605
6763
|
var PLIST_LABEL = "com.agenticmail.server";
|
|
@@ -6611,9 +6769,9 @@ var ServiceManager = class {
|
|
|
6611
6769
|
*/
|
|
6612
6770
|
getServicePath() {
|
|
6613
6771
|
if (this.os === "darwin") {
|
|
6614
|
-
return
|
|
6772
|
+
return join9(homedir7(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
6615
6773
|
} else {
|
|
6616
|
-
return
|
|
6774
|
+
return join9(homedir7(), ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
6617
6775
|
}
|
|
6618
6776
|
}
|
|
6619
6777
|
/**
|
|
@@ -6651,33 +6809,33 @@ var ServiceManager = class {
|
|
|
6651
6809
|
} catch {
|
|
6652
6810
|
}
|
|
6653
6811
|
const parentPackages = [
|
|
6654
|
-
|
|
6812
|
+
join9("@agenticmail", "cli"),
|
|
6655
6813
|
// current scoped package
|
|
6656
6814
|
"agenticmail"
|
|
6657
6815
|
// legacy unscoped package
|
|
6658
6816
|
];
|
|
6659
6817
|
const baseDirs = [
|
|
6660
6818
|
// user-local install
|
|
6661
|
-
|
|
6819
|
+
join9(homedir7(), "node_modules")
|
|
6662
6820
|
];
|
|
6663
6821
|
try {
|
|
6664
6822
|
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6665
|
-
baseDirs.push(
|
|
6666
|
-
baseDirs.push(
|
|
6823
|
+
baseDirs.push(join9(prefix, "lib", "node_modules"));
|
|
6824
|
+
baseDirs.push(join9(prefix, "node_modules"));
|
|
6667
6825
|
} catch {
|
|
6668
6826
|
}
|
|
6669
6827
|
baseDirs.push("/opt/homebrew/lib/node_modules");
|
|
6670
6828
|
baseDirs.push("/usr/local/lib/node_modules");
|
|
6671
6829
|
for (const base of baseDirs) {
|
|
6672
|
-
const sibling =
|
|
6830
|
+
const sibling = join9(base, "@agenticmail", "api", "dist", "index.js");
|
|
6673
6831
|
if (existsSync6(sibling)) return sibling;
|
|
6674
6832
|
for (const parent of parentPackages) {
|
|
6675
|
-
const nested =
|
|
6833
|
+
const nested = join9(base, parent, "node_modules", "@agenticmail", "api", "dist", "index.js");
|
|
6676
6834
|
if (existsSync6(nested)) return nested;
|
|
6677
6835
|
}
|
|
6678
6836
|
}
|
|
6679
|
-
const dataDir =
|
|
6680
|
-
const entryCache =
|
|
6837
|
+
const dataDir = join9(homedir7(), ".agenticmail");
|
|
6838
|
+
const entryCache = join9(dataDir, "api-entry.path");
|
|
6681
6839
|
if (existsSync6(entryCache)) {
|
|
6682
6840
|
const cached = readFileSync3(entryCache, "utf-8").trim();
|
|
6683
6841
|
if (cached && existsSync6(cached)) return cached;
|
|
@@ -6688,9 +6846,9 @@ var ServiceManager = class {
|
|
|
6688
6846
|
* Cache the API entry path so the service can find it later.
|
|
6689
6847
|
*/
|
|
6690
6848
|
cacheApiEntryPath(entryPath) {
|
|
6691
|
-
const dataDir =
|
|
6849
|
+
const dataDir = join9(homedir7(), ".agenticmail");
|
|
6692
6850
|
if (!existsSync6(dataDir)) mkdirSync5(dataDir, { recursive: true });
|
|
6693
|
-
writeFileSync4(
|
|
6851
|
+
writeFileSync4(join9(dataDir, "api-entry.path"), entryPath);
|
|
6694
6852
|
}
|
|
6695
6853
|
/**
|
|
6696
6854
|
* Get the current package version.
|
|
@@ -6711,7 +6869,7 @@ var ServiceManager = class {
|
|
|
6711
6869
|
}
|
|
6712
6870
|
try {
|
|
6713
6871
|
const apiEntry = this.getApiEntryPath();
|
|
6714
|
-
const apiPkg =
|
|
6872
|
+
const apiPkg = join9(apiEntry, "..", "..", "package.json");
|
|
6715
6873
|
if (existsSync6(apiPkg)) {
|
|
6716
6874
|
const pkg = JSON.parse(readFileSync3(apiPkg, "utf-8"));
|
|
6717
6875
|
if (pkg.version) return pkg.version;
|
|
@@ -6719,14 +6877,14 @@ var ServiceManager = class {
|
|
|
6719
6877
|
} catch {
|
|
6720
6878
|
}
|
|
6721
6879
|
const candidates = [
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6880
|
+
join9(homedir7(), "node_modules", "@agenticmail", "cli", "package.json"),
|
|
6881
|
+
join9(homedir7(), "node_modules", "agenticmail", "package.json"),
|
|
6882
|
+
join9(homedir7(), ".agenticmail", "package-version.json")
|
|
6725
6883
|
];
|
|
6726
6884
|
try {
|
|
6727
6885
|
const prefix = execSync2("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
6728
|
-
candidates.push(
|
|
6729
|
-
candidates.push(
|
|
6886
|
+
candidates.push(join9(prefix, "lib", "node_modules", "@agenticmail", "cli", "package.json"));
|
|
6887
|
+
candidates.push(join9(prefix, "lib", "node_modules", "agenticmail", "package.json"));
|
|
6730
6888
|
} catch {
|
|
6731
6889
|
}
|
|
6732
6890
|
for (const p of candidates) {
|
|
@@ -6745,8 +6903,8 @@ var ServiceManager = class {
|
|
|
6745
6903
|
* This ensures AgenticMail doesn't fail on boot when Docker is still loading.
|
|
6746
6904
|
*/
|
|
6747
6905
|
generateStartScript(nodePath, apiEntry) {
|
|
6748
|
-
const scriptPath =
|
|
6749
|
-
const scriptDir =
|
|
6906
|
+
const scriptPath = join9(homedir7(), ".agenticmail", "bin", "start-server.sh");
|
|
6907
|
+
const scriptDir = join9(homedir7(), ".agenticmail", "bin");
|
|
6750
6908
|
if (!existsSync6(scriptDir)) mkdirSync5(scriptDir, { recursive: true });
|
|
6751
6909
|
const script = [
|
|
6752
6910
|
"#!/bin/bash",
|
|
@@ -6812,7 +6970,7 @@ var ServiceManager = class {
|
|
|
6812
6970
|
*/
|
|
6813
6971
|
generatePlist(nodePath, apiEntry, configPath) {
|
|
6814
6972
|
const config = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
6815
|
-
const logDir =
|
|
6973
|
+
const logDir = join9(homedir7(), ".agenticmail", "logs");
|
|
6816
6974
|
if (!existsSync6(logDir)) mkdirSync5(logDir, { recursive: true });
|
|
6817
6975
|
const version = this.getVersion();
|
|
6818
6976
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
@@ -6836,7 +6994,7 @@ var ServiceManager = class {
|
|
|
6836
6994
|
<key>HOME</key>
|
|
6837
6995
|
<string>${homedir7()}</string>
|
|
6838
6996
|
<key>AGENTICMAIL_DATA_DIR</key>
|
|
6839
|
-
<string>${config.dataDir ||
|
|
6997
|
+
<string>${config.dataDir || join9(homedir7(), ".agenticmail")}</string>
|
|
6840
6998
|
<key>PATH</key>
|
|
6841
6999
|
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
6842
7000
|
<key>AGENTICMAIL_SERVICE_VERSION</key>
|
|
@@ -6890,7 +7048,7 @@ var ServiceManager = class {
|
|
|
6890
7048
|
*/
|
|
6891
7049
|
generateSystemdUnit(nodePath, apiEntry, configPath) {
|
|
6892
7050
|
const config = JSON.parse(readFileSync3(configPath, "utf-8"));
|
|
6893
|
-
const dataDir = config.dataDir ||
|
|
7051
|
+
const dataDir = config.dataDir || join9(homedir7(), ".agenticmail");
|
|
6894
7052
|
const version = this.getVersion();
|
|
6895
7053
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
6896
7054
|
return `[Unit]
|
|
@@ -6920,7 +7078,7 @@ WantedBy=default.target
|
|
|
6920
7078
|
* Install the auto-start service.
|
|
6921
7079
|
*/
|
|
6922
7080
|
install() {
|
|
6923
|
-
const configPath =
|
|
7081
|
+
const configPath = join9(homedir7(), ".agenticmail", "config.json");
|
|
6924
7082
|
if (!existsSync6(configPath)) {
|
|
6925
7083
|
return { installed: false, message: "Config not found. Run agenticmail setup first." };
|
|
6926
7084
|
}
|
|
@@ -6933,7 +7091,7 @@ WantedBy=default.target
|
|
|
6933
7091
|
}
|
|
6934
7092
|
const servicePath = this.getServicePath();
|
|
6935
7093
|
if (this.os === "darwin") {
|
|
6936
|
-
const dir =
|
|
7094
|
+
const dir = join9(homedir7(), "Library", "LaunchAgents");
|
|
6937
7095
|
if (!existsSync6(dir)) mkdirSync5(dir, { recursive: true });
|
|
6938
7096
|
if (existsSync6(servicePath)) {
|
|
6939
7097
|
try {
|
|
@@ -6951,7 +7109,7 @@ WantedBy=default.target
|
|
|
6951
7109
|
}
|
|
6952
7110
|
return { installed: true, message: `Service installed at ${servicePath}` };
|
|
6953
7111
|
} else if (this.os === "linux") {
|
|
6954
|
-
const dir =
|
|
7112
|
+
const dir = join9(homedir7(), ".config", "systemd", "user");
|
|
6955
7113
|
if (!existsSync6(dir)) mkdirSync5(dir, { recursive: true });
|
|
6956
7114
|
const unit = this.generateSystemdUnit(nodePath, apiEntry, configPath);
|
|
6957
7115
|
writeFileSync4(servicePath, unit);
|
|
@@ -7077,7 +7235,7 @@ WantedBy=default.target
|
|
|
7077
7235
|
} catch {
|
|
7078
7236
|
return { reason: "Service file unreadable" };
|
|
7079
7237
|
}
|
|
7080
|
-
const startScript =
|
|
7238
|
+
const startScript = join9(homedir7(), ".agenticmail", "bin", "start-server.sh");
|
|
7081
7239
|
if (serviceContent.includes(startScript)) {
|
|
7082
7240
|
if (!existsSync6(startScript)) {
|
|
7083
7241
|
return { reason: "start-server.sh is missing" };
|
|
@@ -7110,7 +7268,7 @@ WantedBy=default.target
|
|
|
7110
7268
|
return { reason: `Service version drift (current CLI is v${currentVersion})` };
|
|
7111
7269
|
}
|
|
7112
7270
|
}
|
|
7113
|
-
const entryCache =
|
|
7271
|
+
const entryCache = join9(homedir7(), ".agenticmail", "api-entry.path");
|
|
7114
7272
|
if (existsSync6(entryCache)) {
|
|
7115
7273
|
try {
|
|
7116
7274
|
const cached = readFileSync3(entryCache, "utf-8").trim();
|
|
@@ -7165,12 +7323,12 @@ var SetupManager = class {
|
|
|
7165
7323
|
* falls back to monorepo location.
|
|
7166
7324
|
*/
|
|
7167
7325
|
getComposePath() {
|
|
7168
|
-
const standalonePath =
|
|
7326
|
+
const standalonePath = join10(homedir8(), ".agenticmail", "docker-compose.yml");
|
|
7169
7327
|
if (existsSync7(standalonePath)) return standalonePath;
|
|
7170
7328
|
const cwd = process.cwd();
|
|
7171
|
-
const candidates = [cwd,
|
|
7329
|
+
const candidates = [cwd, join10(cwd, "..")];
|
|
7172
7330
|
for (const dir of candidates) {
|
|
7173
|
-
const p =
|
|
7331
|
+
const p = join10(dir, "docker-compose.yml");
|
|
7174
7332
|
if (existsSync7(p)) return p;
|
|
7175
7333
|
}
|
|
7176
7334
|
return standalonePath;
|
|
@@ -7181,9 +7339,9 @@ var SetupManager = class {
|
|
|
7181
7339
|
* Always regenerates Docker files to keep passwords in sync.
|
|
7182
7340
|
*/
|
|
7183
7341
|
initConfig() {
|
|
7184
|
-
const dataDir =
|
|
7185
|
-
const configPath =
|
|
7186
|
-
const envPath =
|
|
7342
|
+
const dataDir = join10(homedir8(), ".agenticmail");
|
|
7343
|
+
const configPath = join10(dataDir, "config.json");
|
|
7344
|
+
const envPath = join10(dataDir, ".env");
|
|
7187
7345
|
if (existsSync7(configPath)) {
|
|
7188
7346
|
try {
|
|
7189
7347
|
const existing = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
@@ -7235,12 +7393,12 @@ IMAP_PORT=143
|
|
|
7235
7393
|
* with the correct admin password from config.
|
|
7236
7394
|
*/
|
|
7237
7395
|
generateDockerFiles(config) {
|
|
7238
|
-
const dataDir = config.dataDir ||
|
|
7396
|
+
const dataDir = config.dataDir || join10(homedir8(), ".agenticmail");
|
|
7239
7397
|
if (!existsSync7(dataDir)) {
|
|
7240
7398
|
mkdirSync6(dataDir, { recursive: true });
|
|
7241
7399
|
}
|
|
7242
7400
|
const password = config.stalwart?.adminPassword || "changeme";
|
|
7243
|
-
const composePath =
|
|
7401
|
+
const composePath = join10(dataDir, "docker-compose.yml");
|
|
7244
7402
|
writeFileSync5(composePath, `services:
|
|
7245
7403
|
stalwart:
|
|
7246
7404
|
# Pinned to v0.15.5 \u2014 Stalwart 0.16+ moved its config to JSON
|
|
@@ -7271,7 +7429,7 @@ volumes:
|
|
|
7271
7429
|
stalwart-data:
|
|
7272
7430
|
`);
|
|
7273
7431
|
chmodSync2(composePath, 384);
|
|
7274
|
-
const tomlPath =
|
|
7432
|
+
const tomlPath = join10(dataDir, "stalwart.toml");
|
|
7275
7433
|
writeFileSync5(tomlPath, `# Stalwart Mail Server \u2014 AgenticMail Configuration
|
|
7276
7434
|
|
|
7277
7435
|
[server]
|
|
@@ -7328,7 +7486,7 @@ secret = "${password}"
|
|
|
7328
7486
|
* Check if config has already been initialized.
|
|
7329
7487
|
*/
|
|
7330
7488
|
isInitialized() {
|
|
7331
|
-
const configPath =
|
|
7489
|
+
const configPath = join10(homedir8(), ".agenticmail", "config.json");
|
|
7332
7490
|
return existsSync7(configPath);
|
|
7333
7491
|
}
|
|
7334
7492
|
};
|
|
@@ -7336,7 +7494,7 @@ secret = "${password}"
|
|
|
7336
7494
|
// src/threading/thread-id.ts
|
|
7337
7495
|
import { createHash as createHash2 } from "crypto";
|
|
7338
7496
|
function stripReplyPrefixes(subject) {
|
|
7339
|
-
let s = subject;
|
|
7497
|
+
let s = subject.length > 1e3 ? subject.slice(0, 1e3) : subject;
|
|
7340
7498
|
for (; ; ) {
|
|
7341
7499
|
const next = s.replace(/^\s*(?:re|fwd?|fw)\s*(?:\[\d+\])?\s*:\s*/i, "");
|
|
7342
7500
|
if (next === s) break;
|
|
@@ -7356,8 +7514,9 @@ function normalizeSubject(subject) {
|
|
|
7356
7514
|
}
|
|
7357
7515
|
function normalizeAddress(addr) {
|
|
7358
7516
|
if (!addr) return "(unknown)";
|
|
7359
|
-
const
|
|
7360
|
-
const
|
|
7517
|
+
const bounded = addr.length > 500 ? addr.slice(0, 500) : addr;
|
|
7518
|
+
const m = bounded.match(/<([^>]+)>/);
|
|
7519
|
+
const raw = m ? m[1] : bounded;
|
|
7361
7520
|
return raw.trim().toLowerCase();
|
|
7362
7521
|
}
|
|
7363
7522
|
function threadIdFor(input) {
|
|
@@ -7377,8 +7536,8 @@ import {
|
|
|
7377
7536
|
renameSync
|
|
7378
7537
|
} from "fs";
|
|
7379
7538
|
import { homedir as homedir9 } from "os";
|
|
7380
|
-
import { join as
|
|
7381
|
-
var CACHE_DIR_DEFAULT =
|
|
7539
|
+
import { join as join11 } from "path";
|
|
7540
|
+
var CACHE_DIR_DEFAULT = join11(homedir9(), ".agenticmail", "thread-cache");
|
|
7382
7541
|
var DEFAULT_K_MESSAGES = 10;
|
|
7383
7542
|
var DEFAULT_LRU_CAP = 5e3;
|
|
7384
7543
|
var PREVIEW_MAX_CHARS = 240;
|
|
@@ -7396,7 +7555,7 @@ var ThreadCache = class {
|
|
|
7396
7555
|
}
|
|
7397
7556
|
}
|
|
7398
7557
|
pathFor(threadId) {
|
|
7399
|
-
return
|
|
7558
|
+
return join11(this.dir, `${threadId}.json`);
|
|
7400
7559
|
}
|
|
7401
7560
|
read(threadId) {
|
|
7402
7561
|
const p = this.pathFor(threadId);
|
|
@@ -7486,7 +7645,7 @@ var ThreadCache = class {
|
|
|
7486
7645
|
}
|
|
7487
7646
|
if (files.length <= this.lruCap) return;
|
|
7488
7647
|
const stats = files.map((f) => {
|
|
7489
|
-
const p =
|
|
7648
|
+
const p = join11(this.dir, f);
|
|
7490
7649
|
try {
|
|
7491
7650
|
return { p, mtime: statSync(p).mtimeMs };
|
|
7492
7651
|
} catch {
|
|
@@ -7525,8 +7684,8 @@ import {
|
|
|
7525
7684
|
renameSync as renameSync2
|
|
7526
7685
|
} from "fs";
|
|
7527
7686
|
import { homedir as homedir10 } from "os";
|
|
7528
|
-
import { join as
|
|
7529
|
-
var MEMORY_DIR_DEFAULT =
|
|
7687
|
+
import { join as join12 } from "path";
|
|
7688
|
+
var MEMORY_DIR_DEFAULT = join12(homedir10(), ".agenticmail", "agent-memory");
|
|
7530
7689
|
var AgentMemoryStore = class {
|
|
7531
7690
|
dir;
|
|
7532
7691
|
constructor(opts = {}) {
|
|
@@ -7537,10 +7696,10 @@ var AgentMemoryStore = class {
|
|
|
7537
7696
|
}
|
|
7538
7697
|
}
|
|
7539
7698
|
dirFor(agentId) {
|
|
7540
|
-
return
|
|
7699
|
+
return join12(this.dir, sanitizeId(agentId));
|
|
7541
7700
|
}
|
|
7542
7701
|
pathFor(agentId, threadId) {
|
|
7543
|
-
return
|
|
7702
|
+
return join12(this.dirFor(agentId), `${sanitizeId(threadId)}.md`);
|
|
7544
7703
|
}
|
|
7545
7704
|
read(agentId, threadId) {
|
|
7546
7705
|
const p = this.pathFor(agentId, threadId);
|
|
@@ -7641,6 +7800,8 @@ export {
|
|
|
7641
7800
|
InboxWatcher,
|
|
7642
7801
|
MailReceiver,
|
|
7643
7802
|
MailSender,
|
|
7803
|
+
PathTraversalError,
|
|
7804
|
+
REDACTED,
|
|
7644
7805
|
RELAY_PRESETS,
|
|
7645
7806
|
RelayBridge,
|
|
7646
7807
|
RelayGateway,
|
|
@@ -7652,7 +7813,10 @@ export {
|
|
|
7652
7813
|
StalwartAdmin,
|
|
7653
7814
|
ThreadCache,
|
|
7654
7815
|
TunnelManager,
|
|
7816
|
+
UnsafeApiUrlError,
|
|
7655
7817
|
WARNING_THRESHOLD,
|
|
7818
|
+
assertWithinBase,
|
|
7819
|
+
buildApiUrl,
|
|
7656
7820
|
buildInboundSecurityAdvisory,
|
|
7657
7821
|
classifyEmailRoute,
|
|
7658
7822
|
closeDatabase,
|
|
@@ -7671,12 +7835,17 @@ export {
|
|
|
7671
7835
|
parseEmail,
|
|
7672
7836
|
parseGoogleVoiceSms,
|
|
7673
7837
|
recordToolCall,
|
|
7838
|
+
redactObject,
|
|
7839
|
+
redactSecret,
|
|
7674
7840
|
resolveConfig,
|
|
7841
|
+
safeJoin,
|
|
7675
7842
|
sanitizeEmail,
|
|
7676
7843
|
saveConfig,
|
|
7677
7844
|
scanOutboundEmail,
|
|
7678
7845
|
scoreEmail,
|
|
7679
7846
|
setTelemetryVersion,
|
|
7680
7847
|
startRelayBridge,
|
|
7681
|
-
threadIdFor
|
|
7848
|
+
threadIdFor,
|
|
7849
|
+
tryJoin,
|
|
7850
|
+
validateApiUrl
|
|
7682
7851
|
};
|