@agenticmail/core 0.9.4 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +472 -211
- package/dist/index.d.cts +372 -1
- package/dist/index.d.ts +372 -1
- package/dist/index.js +445 -200
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -716,6 +716,7 @@ __export(index_exports, {
|
|
|
716
716
|
CloudflareClient: () => CloudflareClient,
|
|
717
717
|
DEFAULT_AGENT_NAME: () => DEFAULT_AGENT_NAME,
|
|
718
718
|
DEFAULT_AGENT_ROLE: () => DEFAULT_AGENT_ROLE,
|
|
719
|
+
DEFAULT_SESSION_MAX_AGE_MS: () => DEFAULT_SESSION_MAX_AGE_MS,
|
|
719
720
|
DNSConfigurator: () => DNSConfigurator,
|
|
720
721
|
DependencyChecker: () => DependencyChecker,
|
|
721
722
|
DependencyInstaller: () => DependencyInstaller,
|
|
@@ -726,6 +727,8 @@ __export(index_exports, {
|
|
|
726
727
|
InboxWatcher: () => InboxWatcher,
|
|
727
728
|
MailReceiver: () => MailReceiver,
|
|
728
729
|
MailSender: () => MailSender,
|
|
730
|
+
PathTraversalError: () => PathTraversalError,
|
|
731
|
+
REDACTED: () => REDACTED,
|
|
729
732
|
RELAY_PRESETS: () => RELAY_PRESETS,
|
|
730
733
|
RelayBridge: () => RelayBridge,
|
|
731
734
|
RelayGateway: () => RelayGateway,
|
|
@@ -737,7 +740,10 @@ __export(index_exports, {
|
|
|
737
740
|
StalwartAdmin: () => StalwartAdmin,
|
|
738
741
|
ThreadCache: () => ThreadCache,
|
|
739
742
|
TunnelManager: () => TunnelManager,
|
|
743
|
+
UnsafeApiUrlError: () => UnsafeApiUrlError,
|
|
740
744
|
WARNING_THRESHOLD: () => WARNING_THRESHOLD,
|
|
745
|
+
assertWithinBase: () => assertWithinBase,
|
|
746
|
+
buildApiUrl: () => buildApiUrl,
|
|
741
747
|
buildInboundSecurityAdvisory: () => buildInboundSecurityAdvisory,
|
|
742
748
|
classifyEmailRoute: () => classifyEmailRoute,
|
|
743
749
|
closeDatabase: () => closeDatabase,
|
|
@@ -747,23 +753,33 @@ __export(index_exports, {
|
|
|
747
753
|
ensureDataDir: () => ensureDataDir,
|
|
748
754
|
extractVerificationCode: () => extractVerificationCode,
|
|
749
755
|
flushTelemetry: () => flushTelemetry,
|
|
756
|
+
forgetHostSession: () => forgetHostSession,
|
|
750
757
|
getDatabase: () => getDatabase,
|
|
758
|
+
hostSessionStoragePath: () => hostSessionStoragePath,
|
|
751
759
|
isInternalEmail: () => isInternalEmail,
|
|
760
|
+
isSessionFresh: () => isSessionFresh,
|
|
752
761
|
isValidPhoneNumber: () => isValidPhoneNumber,
|
|
762
|
+
loadHostSession: () => loadHostSession,
|
|
753
763
|
normalizeAddress: () => normalizeAddress,
|
|
754
764
|
normalizePhoneNumber: () => normalizePhoneNumber,
|
|
755
765
|
normalizeSubject: () => normalizeSubject,
|
|
756
766
|
parseEmail: () => parseEmail,
|
|
757
767
|
parseGoogleVoiceSms: () => parseGoogleVoiceSms,
|
|
758
768
|
recordToolCall: () => recordToolCall,
|
|
769
|
+
redactObject: () => redactObject,
|
|
770
|
+
redactSecret: () => redactSecret,
|
|
759
771
|
resolveConfig: () => resolveConfig,
|
|
772
|
+
safeJoin: () => safeJoin,
|
|
760
773
|
sanitizeEmail: () => sanitizeEmail,
|
|
761
774
|
saveConfig: () => saveConfig,
|
|
775
|
+
saveHostSession: () => saveHostSession,
|
|
762
776
|
scanOutboundEmail: () => scanOutboundEmail,
|
|
763
777
|
scoreEmail: () => scoreEmail,
|
|
764
778
|
setTelemetryVersion: () => setTelemetryVersion,
|
|
765
779
|
startRelayBridge: () => startRelayBridge,
|
|
766
|
-
threadIdFor: () => threadIdFor
|
|
780
|
+
threadIdFor: () => threadIdFor,
|
|
781
|
+
tryJoin: () => tryJoin,
|
|
782
|
+
validateApiUrl: () => validateApiUrl
|
|
767
783
|
});
|
|
768
784
|
module.exports = __toCommonJS(index_exports);
|
|
769
785
|
|
|
@@ -837,7 +853,7 @@ var MailSender = class {
|
|
|
837
853
|
const code = err?.responseCode ?? err?.code;
|
|
838
854
|
const isTransient = typeof code === "number" && code >= 400 && code < 500 || code === "ECONNRESET" || code === "ETIMEDOUT" || code === "ESOCKET";
|
|
839
855
|
if (!isTransient || attempt === MAX_RETRIES) throw err;
|
|
840
|
-
await new Promise((
|
|
856
|
+
await new Promise((resolve2) => setTimeout(resolve2, 1e3 * (attempt + 1)));
|
|
841
857
|
}
|
|
842
858
|
}
|
|
843
859
|
throw lastError;
|
|
@@ -1769,20 +1785,20 @@ var StalwartAdmin = class {
|
|
|
1769
1785
|
}
|
|
1770
1786
|
try {
|
|
1771
1787
|
const net = await import("net");
|
|
1772
|
-
return await new Promise((
|
|
1788
|
+
return await new Promise((resolve2) => {
|
|
1773
1789
|
const smtpPort = parseInt(process.env.SMTP_PORT || "25", 10);
|
|
1774
1790
|
const smtpHost = process.env.SMTP_HOST || "localhost";
|
|
1775
1791
|
const socket = net.createConnection({ host: smtpHost, port: smtpPort, timeout: 3e3 }, () => {
|
|
1776
1792
|
socket.destroy();
|
|
1777
|
-
|
|
1793
|
+
resolve2(true);
|
|
1778
1794
|
});
|
|
1779
1795
|
socket.on("error", () => {
|
|
1780
1796
|
socket.destroy();
|
|
1781
|
-
|
|
1797
|
+
resolve2(false);
|
|
1782
1798
|
});
|
|
1783
1799
|
socket.on("timeout", () => {
|
|
1784
1800
|
socket.destroy();
|
|
1785
|
-
|
|
1801
|
+
resolve2(false);
|
|
1786
1802
|
});
|
|
1787
1803
|
});
|
|
1788
1804
|
} catch {
|
|
@@ -1852,14 +1868,14 @@ var StalwartAdmin = class {
|
|
|
1852
1868
|
if (!isValidDomain(domain)) {
|
|
1853
1869
|
throw new Error(`Invalid domain format: "${domain}"`);
|
|
1854
1870
|
}
|
|
1855
|
-
const { readFileSync:
|
|
1856
|
-
const { homedir:
|
|
1857
|
-
const { join:
|
|
1858
|
-
const configPath =
|
|
1871
|
+
const { readFileSync: readFileSync8, writeFileSync: writeFileSync9 } = await import("fs");
|
|
1872
|
+
const { homedir: homedir12 } = await import("os");
|
|
1873
|
+
const { join: join14 } = await import("path");
|
|
1874
|
+
const configPath = join14(homedir12(), ".agenticmail", "stalwart.toml");
|
|
1859
1875
|
try {
|
|
1860
|
-
let config =
|
|
1876
|
+
let config = readFileSync8(configPath, "utf-8");
|
|
1861
1877
|
config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
|
|
1862
|
-
|
|
1878
|
+
writeFileSync9(configPath, config);
|
|
1863
1879
|
console.log(`[Stalwart] Updated hostname to "${domain}" in stalwart.toml`);
|
|
1864
1880
|
} catch (err) {
|
|
1865
1881
|
throw new Error(`Failed to set config server.hostname=${domain}`);
|
|
@@ -1868,15 +1884,15 @@ var StalwartAdmin = class {
|
|
|
1868
1884
|
// --- DKIM ---
|
|
1869
1885
|
/** Path to the host-side stalwart.toml (mounted read-only into container) */
|
|
1870
1886
|
get configPath() {
|
|
1871
|
-
const { homedir:
|
|
1872
|
-
const { join:
|
|
1873
|
-
return
|
|
1887
|
+
const { homedir: homedir12 } = require("os");
|
|
1888
|
+
const { join: join14 } = require("path");
|
|
1889
|
+
return join14(homedir12(), ".agenticmail", "stalwart.toml");
|
|
1874
1890
|
}
|
|
1875
1891
|
/** Path to host-side DKIM key directory */
|
|
1876
1892
|
get dkimDir() {
|
|
1877
|
-
const { homedir:
|
|
1878
|
-
const { join:
|
|
1879
|
-
return
|
|
1893
|
+
const { homedir: homedir12 } = require("os");
|
|
1894
|
+
const { join: join14 } = require("path");
|
|
1895
|
+
return join14(homedir12(), ".agenticmail");
|
|
1880
1896
|
}
|
|
1881
1897
|
/**
|
|
1882
1898
|
* Create/reuse a DKIM signing key for a domain.
|
|
@@ -1977,12 +1993,12 @@ var StalwartAdmin = class {
|
|
|
1977
1993
|
* This bypasses the need for a PTR record on the sending IP.
|
|
1978
1994
|
*/
|
|
1979
1995
|
async configureOutboundRelay(config) {
|
|
1980
|
-
const { readFileSync:
|
|
1981
|
-
const { homedir:
|
|
1982
|
-
const { join:
|
|
1996
|
+
const { readFileSync: readFileSync8, writeFileSync: writeFileSync9 } = await import("fs");
|
|
1997
|
+
const { homedir: homedir12 } = await import("os");
|
|
1998
|
+
const { join: join14 } = await import("path");
|
|
1983
1999
|
const routeName = config.routeName ?? "gmail";
|
|
1984
|
-
const tomlPath =
|
|
1985
|
-
let toml =
|
|
2000
|
+
const tomlPath = join14(homedir12(), ".agenticmail", "stalwart.toml");
|
|
2001
|
+
let toml = readFileSync8(tomlPath, "utf-8");
|
|
1986
2002
|
toml = toml.replace(/\n\[queue\.route\.gmail\][\s\S]*?(?=\n\[|$)/, "");
|
|
1987
2003
|
toml = toml.replace(/\n\[queue\.strategy\][\s\S]*?(?=\n\[|$)/, "");
|
|
1988
2004
|
const safeRouteName = routeName.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
@@ -2002,7 +2018,7 @@ auth.secret = "${escapeTomlString(config.password)}"
|
|
|
2002
2018
|
route = [ { if = "is_local_domain('', rcpt_domain)", then = "'local'" },
|
|
2003
2019
|
{ else = "'${safeRouteName}'" } ]
|
|
2004
2020
|
`;
|
|
2005
|
-
|
|
2021
|
+
writeFileSync9(tomlPath, toml, "utf-8");
|
|
2006
2022
|
await this.restartContainer();
|
|
2007
2023
|
}
|
|
2008
2024
|
};
|
|
@@ -3089,7 +3105,8 @@ var OUTBOUND_TEXT_RULES = [
|
|
|
3089
3105
|
var HIGH_RISK_EXTENSIONS = /* @__PURE__ */ new Set([".pem", ".key", ".p12", ".pfx", ".env", ".credentials", ".keystore", ".jks", ".p8"]);
|
|
3090
3106
|
var MEDIUM_RISK_EXTENSIONS = /* @__PURE__ */ new Set([".db", ".sqlite", ".sqlite3", ".sql", ".csv", ".tsv", ".json", ".yml", ".yaml", ".conf", ".config", ".ini"]);
|
|
3091
3107
|
function stripHtmlTags(html) {
|
|
3092
|
-
|
|
3108
|
+
const bounded = html.length > 1048576 ? html.slice(0, 1048576) : html;
|
|
3109
|
+
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)));
|
|
3093
3110
|
}
|
|
3094
3111
|
var TEXT_SCANNABLE_TYPES = /* @__PURE__ */ new Set([
|
|
3095
3112
|
"text/plain",
|
|
@@ -4622,7 +4639,7 @@ var CloudflareClient = class {
|
|
|
4622
4639
|
// --- Workers methods ---
|
|
4623
4640
|
/** Deploy an Email Worker script (ES module format) */
|
|
4624
4641
|
async deployEmailWorker(scriptName, scriptContent, envVars = {}) {
|
|
4625
|
-
const url = `${CF_API_BASE}/accounts/${this.accountId}/workers/scripts/${scriptName}`;
|
|
4642
|
+
const url = `${CF_API_BASE}/accounts/${encodeURIComponent(this.accountId)}/workers/scripts/${encodeURIComponent(scriptName)}`;
|
|
4626
4643
|
const bindings = Object.entries(envVars).map(([name, text2]) => ({
|
|
4627
4644
|
type: "plain_text",
|
|
4628
4645
|
name,
|
|
@@ -5002,9 +5019,9 @@ var TunnelManager = class {
|
|
|
5002
5019
|
throw new Error(`Failed to download cloudflared: ${response.statusText}`);
|
|
5003
5020
|
}
|
|
5004
5021
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
5005
|
-
const { writeFile:
|
|
5022
|
+
const { writeFile: writeFile3, rename: rename2 } = await import("fs/promises");
|
|
5006
5023
|
const tmpPath = this.binPath + ".tmp";
|
|
5007
|
-
await
|
|
5024
|
+
await writeFile3(tmpPath, buffer);
|
|
5008
5025
|
await (0, import_promises.chmod)(tmpPath, 493);
|
|
5009
5026
|
await rename2(tmpPath, this.binPath);
|
|
5010
5027
|
return this.binPath;
|
|
@@ -5040,7 +5057,7 @@ var TunnelManager = class {
|
|
|
5040
5057
|
detached: false,
|
|
5041
5058
|
env: { ...process.env, TUNNEL_TOKEN: tunnelToken }
|
|
5042
5059
|
});
|
|
5043
|
-
await new Promise((
|
|
5060
|
+
await new Promise((resolve2, reject) => {
|
|
5044
5061
|
let resolved = false;
|
|
5045
5062
|
const timeout = setTimeout(() => {
|
|
5046
5063
|
if (!resolved) {
|
|
@@ -5054,7 +5071,7 @@ var TunnelManager = class {
|
|
|
5054
5071
|
resolved = true;
|
|
5055
5072
|
clearTimeout(timeout);
|
|
5056
5073
|
this.running = true;
|
|
5057
|
-
|
|
5074
|
+
resolve2();
|
|
5058
5075
|
}
|
|
5059
5076
|
};
|
|
5060
5077
|
this.process.stderr?.on("data", onData);
|
|
@@ -5093,8 +5110,8 @@ var TunnelManager = class {
|
|
|
5093
5110
|
this.process = null;
|
|
5094
5111
|
p.kill("SIGTERM");
|
|
5095
5112
|
await Promise.race([
|
|
5096
|
-
new Promise((
|
|
5097
|
-
new Promise((
|
|
5113
|
+
new Promise((resolve2) => p.on("exit", () => resolve2())),
|
|
5114
|
+
new Promise((resolve2) => setTimeout(resolve2, 5e3))
|
|
5098
5115
|
]);
|
|
5099
5116
|
this.running = false;
|
|
5100
5117
|
}
|
|
@@ -5150,7 +5167,10 @@ function parseGoogleVoiceSms(emailBody, emailFrom) {
|
|
|
5150
5167
|
if (!emailBody || typeof emailBody !== "string") return null;
|
|
5151
5168
|
if (!emailFrom || typeof emailFrom !== "string") return null;
|
|
5152
5169
|
const fromLower = emailFrom.toLowerCase();
|
|
5153
|
-
const
|
|
5170
|
+
const atIdx = fromLower.lastIndexOf("@");
|
|
5171
|
+
const domain = atIdx >= 0 ? fromLower.slice(atIdx + 1).replace(/[>"'\s].*$/, "") : "";
|
|
5172
|
+
const isGoogleDomain = domain === "google.com" || domain.endsWith(".google.com");
|
|
5173
|
+
const isGoogleVoice = isGoogleDomain && (fromLower.startsWith("voice-noreply@") || domain === "txt.voice.google.com" || domain === "voice.google.com" || domain.endsWith(".voice.google.com") || fromLower.includes("voice"));
|
|
5154
5174
|
if (!isGoogleVoice) return null;
|
|
5155
5175
|
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();
|
|
5156
5176
|
let from = "";
|
|
@@ -5909,12 +5929,12 @@ var GatewayManager = class {
|
|
|
5909
5929
|
zone = await this.cfClient.createZone(domain);
|
|
5910
5930
|
}
|
|
5911
5931
|
const existingRecords = await this.cfClient.listDnsRecords(zone.id);
|
|
5912
|
-
const { homedir:
|
|
5913
|
-
const backupDir = (0, import_node_path4.join)(
|
|
5932
|
+
const { homedir: homedir12 } = await import("os");
|
|
5933
|
+
const backupDir = (0, import_node_path4.join)(homedir12(), ".agenticmail");
|
|
5914
5934
|
const backupPath = (0, import_node_path4.join)(backupDir, `dns-backup-${domain}-${Date.now()}.json`);
|
|
5915
|
-
const { writeFileSync:
|
|
5916
|
-
|
|
5917
|
-
|
|
5935
|
+
const { writeFileSync: writeFileSync9, mkdirSync: mkdirSync10 } = await import("fs");
|
|
5936
|
+
mkdirSync10(backupDir, { recursive: true });
|
|
5937
|
+
writeFileSync9(backupPath, JSON.stringify({
|
|
5918
5938
|
domain,
|
|
5919
5939
|
zoneId: zone.id,
|
|
5920
5940
|
backedUpAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6181,6 +6201,15 @@ var GatewayManager = class {
|
|
|
6181
6201
|
bcc: mail.bcc ? Array.isArray(mail.bcc) ? mail.bcc.join(", ") : mail.bcc : void 0,
|
|
6182
6202
|
subject: mail.subject,
|
|
6183
6203
|
text: mail.text || void 0,
|
|
6204
|
+
// The `html` field is the literal HTML body of the outbound
|
|
6205
|
+
// mail — by design it is whatever the sender chose to compose.
|
|
6206
|
+
// CodeQL `js/xss` flags this because the value flows from user
|
|
6207
|
+
// input, but nodemailer is the SMTP serializer, not an HTML
|
|
6208
|
+
// renderer; XSS would only occur if the recipient's MUA
|
|
6209
|
+
// executed the body, which is outside our trust boundary.
|
|
6210
|
+
// The outbound-guard (packages/core/src/mail/outbound-guard.ts)
|
|
6211
|
+
// already scores HTML bodies for suspicious patterns at the
|
|
6212
|
+
// pre-send step. lgtm[js/xss]
|
|
6184
6213
|
html: mail.html || void 0,
|
|
6185
6214
|
replyTo: mail.replyTo || from,
|
|
6186
6215
|
inReplyTo: mail.inReplyTo || void 0,
|
|
@@ -6542,11 +6571,11 @@ var RelayBridge = class {
|
|
|
6542
6571
|
this.options = options;
|
|
6543
6572
|
}
|
|
6544
6573
|
async start() {
|
|
6545
|
-
return new Promise((
|
|
6574
|
+
return new Promise((resolve2, reject) => {
|
|
6546
6575
|
this.server = (0, import_node_http.createServer)((req, res) => this.handleRequest(req, res));
|
|
6547
6576
|
this.server.listen(this.options.port, "127.0.0.1", () => {
|
|
6548
6577
|
console.log(`[RelayBridge] Listening on 127.0.0.1:${this.options.port}`);
|
|
6549
|
-
|
|
6578
|
+
resolve2();
|
|
6550
6579
|
});
|
|
6551
6580
|
this.server.on("error", reject);
|
|
6552
6581
|
});
|
|
@@ -6748,17 +6777,232 @@ try {
|
|
|
6748
6777
|
} catch {
|
|
6749
6778
|
}
|
|
6750
6779
|
|
|
6780
|
+
// src/util/safe-path.ts
|
|
6781
|
+
var import_node_path5 = require("path");
|
|
6782
|
+
var PathTraversalError = class extends Error {
|
|
6783
|
+
constructor(baseDir, parts) {
|
|
6784
|
+
super(
|
|
6785
|
+
`path traversal attempt: ${JSON.stringify(parts)} resolves outside ${baseDir}`
|
|
6786
|
+
);
|
|
6787
|
+
this.baseDir = baseDir;
|
|
6788
|
+
this.parts = parts;
|
|
6789
|
+
this.name = "PathTraversalError";
|
|
6790
|
+
}
|
|
6791
|
+
};
|
|
6792
|
+
function safeJoin(baseDir, ...partsAndOpts) {
|
|
6793
|
+
let opts = {};
|
|
6794
|
+
const parts = [];
|
|
6795
|
+
for (const p of partsAndOpts) {
|
|
6796
|
+
if (typeof p === "string") {
|
|
6797
|
+
parts.push(p);
|
|
6798
|
+
} else if (p && typeof p === "object") {
|
|
6799
|
+
opts = p;
|
|
6800
|
+
}
|
|
6801
|
+
}
|
|
6802
|
+
if (!opts.allowAbsolute) {
|
|
6803
|
+
for (const part of parts) {
|
|
6804
|
+
if ((0, import_node_path5.isAbsolute)(part)) {
|
|
6805
|
+
throw new PathTraversalError(baseDir, parts);
|
|
6806
|
+
}
|
|
6807
|
+
}
|
|
6808
|
+
}
|
|
6809
|
+
const resolvedBase = (0, import_node_path5.resolve)(baseDir);
|
|
6810
|
+
const resolved = (0, import_node_path5.resolve)(resolvedBase, ...parts);
|
|
6811
|
+
if (resolved !== resolvedBase && !resolved.startsWith(resolvedBase + import_node_path5.sep)) {
|
|
6812
|
+
throw new PathTraversalError(baseDir, parts);
|
|
6813
|
+
}
|
|
6814
|
+
return resolved;
|
|
6815
|
+
}
|
|
6816
|
+
function tryJoin(baseDir, ...parts) {
|
|
6817
|
+
try {
|
|
6818
|
+
return safeJoin(baseDir, ...parts);
|
|
6819
|
+
} catch (err) {
|
|
6820
|
+
if (err instanceof PathTraversalError) return null;
|
|
6821
|
+
throw err;
|
|
6822
|
+
}
|
|
6823
|
+
}
|
|
6824
|
+
function assertWithinBase(baseDir, candidate) {
|
|
6825
|
+
const resolvedBase = (0, import_node_path5.resolve)(baseDir);
|
|
6826
|
+
const resolvedCandidate = (0, import_node_path5.resolve)(candidate);
|
|
6827
|
+
if (resolvedCandidate !== resolvedBase && !resolvedCandidate.startsWith(resolvedBase + import_node_path5.sep)) {
|
|
6828
|
+
throw new PathTraversalError(baseDir, [candidate]);
|
|
6829
|
+
}
|
|
6830
|
+
return resolvedCandidate;
|
|
6831
|
+
}
|
|
6832
|
+
|
|
6833
|
+
// src/util/redact.ts
|
|
6834
|
+
var REDACTED = "***";
|
|
6835
|
+
function redactSecret(value) {
|
|
6836
|
+
if (typeof value !== "string") return REDACTED;
|
|
6837
|
+
if (value.length === 0) return REDACTED;
|
|
6838
|
+
for (const prefix of ["mk_", "ak_", "sk-", "pk_", "tok_"]) {
|
|
6839
|
+
if (value.startsWith(prefix)) return `${prefix}${REDACTED}`;
|
|
6840
|
+
}
|
|
6841
|
+
return REDACTED;
|
|
6842
|
+
}
|
|
6843
|
+
var SENSITIVE_KEY_PATTERNS = [
|
|
6844
|
+
/key$/i,
|
|
6845
|
+
/secret/i,
|
|
6846
|
+
/password/i,
|
|
6847
|
+
/passwd/i,
|
|
6848
|
+
/token/i,
|
|
6849
|
+
/authorization/i,
|
|
6850
|
+
/bearer/i,
|
|
6851
|
+
/\bapikey\b/i,
|
|
6852
|
+
/\bapi_key\b/i,
|
|
6853
|
+
/\bmasterkey\b/i,
|
|
6854
|
+
/\bmaster_key\b/i
|
|
6855
|
+
];
|
|
6856
|
+
function looksSensitive(key) {
|
|
6857
|
+
return SENSITIVE_KEY_PATTERNS.some((p) => p.test(key));
|
|
6858
|
+
}
|
|
6859
|
+
function redactObject(input, _depth = 0) {
|
|
6860
|
+
if (_depth > 12) return input;
|
|
6861
|
+
if (input === null || input === void 0) return input;
|
|
6862
|
+
if (typeof input !== "object") return input;
|
|
6863
|
+
if (Array.isArray(input)) {
|
|
6864
|
+
return input.map((v) => redactObject(v, _depth + 1));
|
|
6865
|
+
}
|
|
6866
|
+
const proto = Object.getPrototypeOf(input);
|
|
6867
|
+
if (proto !== Object.prototype && proto !== null) return input;
|
|
6868
|
+
const out = {};
|
|
6869
|
+
for (const [k, v] of Object.entries(input)) {
|
|
6870
|
+
if (looksSensitive(k)) {
|
|
6871
|
+
out[k] = typeof v === "string" ? redactSecret(v) : REDACTED;
|
|
6872
|
+
} else {
|
|
6873
|
+
out[k] = redactObject(v, _depth + 1);
|
|
6874
|
+
}
|
|
6875
|
+
}
|
|
6876
|
+
return out;
|
|
6877
|
+
}
|
|
6878
|
+
|
|
6879
|
+
// src/host-sessions.ts
|
|
6880
|
+
var import_node_fs4 = require("fs");
|
|
6881
|
+
var import_node_path6 = require("path");
|
|
6882
|
+
var import_node_os4 = require("os");
|
|
6883
|
+
function storageDir() {
|
|
6884
|
+
return (0, import_node_path6.join)((0, import_node_os4.homedir)(), ".agenticmail");
|
|
6885
|
+
}
|
|
6886
|
+
function storagePath() {
|
|
6887
|
+
return (0, import_node_path6.join)(storageDir(), "host-sessions.json");
|
|
6888
|
+
}
|
|
6889
|
+
var DEFAULT_SESSION_MAX_AGE_MS = 24 * 60 * 60 * 1e3;
|
|
6890
|
+
function readFile() {
|
|
6891
|
+
const p = storagePath();
|
|
6892
|
+
if (!(0, import_node_fs4.existsSync)(p)) return { version: 1, sessions: {} };
|
|
6893
|
+
try {
|
|
6894
|
+
const raw = (0, import_node_fs4.readFileSync)(p, "utf-8");
|
|
6895
|
+
if (!raw.trim()) return { version: 1, sessions: {} };
|
|
6896
|
+
const parsed = JSON.parse(raw);
|
|
6897
|
+
return {
|
|
6898
|
+
version: 1,
|
|
6899
|
+
sessions: parsed.sessions && typeof parsed.sessions === "object" ? parsed.sessions : {}
|
|
6900
|
+
};
|
|
6901
|
+
} catch {
|
|
6902
|
+
return { version: 1, sessions: {} };
|
|
6903
|
+
}
|
|
6904
|
+
}
|
|
6905
|
+
function writeFile(shape) {
|
|
6906
|
+
const dir = storageDir();
|
|
6907
|
+
const p = storagePath();
|
|
6908
|
+
if (!(0, import_node_fs4.existsSync)(dir)) (0, import_node_fs4.mkdirSync)(dir, { recursive: true });
|
|
6909
|
+
const tmp = `${p}.agenticmail-tmp-${process.pid}`;
|
|
6910
|
+
(0, import_node_fs4.writeFileSync)(tmp, JSON.stringify(shape, null, 2), "utf-8");
|
|
6911
|
+
(0, import_node_fs4.renameSync)(tmp, p);
|
|
6912
|
+
}
|
|
6913
|
+
function saveHostSession(host, session) {
|
|
6914
|
+
if (!session.sessionId) return;
|
|
6915
|
+
try {
|
|
6916
|
+
const shape = readFile();
|
|
6917
|
+
shape.sessions[host] = {
|
|
6918
|
+
...session,
|
|
6919
|
+
lastSeenMs: Date.now()
|
|
6920
|
+
};
|
|
6921
|
+
writeFile(shape);
|
|
6922
|
+
} catch {
|
|
6923
|
+
}
|
|
6924
|
+
}
|
|
6925
|
+
function loadHostSession(host, maxAgeMs = DEFAULT_SESSION_MAX_AGE_MS) {
|
|
6926
|
+
const shape = readFile();
|
|
6927
|
+
const record = shape.sessions[host];
|
|
6928
|
+
if (!record) return null;
|
|
6929
|
+
if (!isSessionFresh(record, maxAgeMs)) return null;
|
|
6930
|
+
return record;
|
|
6931
|
+
}
|
|
6932
|
+
function isSessionFresh(session, maxAgeMs = DEFAULT_SESSION_MAX_AGE_MS) {
|
|
6933
|
+
if (!session || !Number.isFinite(session.lastSeenMs)) return false;
|
|
6934
|
+
return Date.now() - session.lastSeenMs <= maxAgeMs;
|
|
6935
|
+
}
|
|
6936
|
+
function forgetHostSession(host) {
|
|
6937
|
+
try {
|
|
6938
|
+
const shape = readFile();
|
|
6939
|
+
if (!shape.sessions[host]) return;
|
|
6940
|
+
delete shape.sessions[host];
|
|
6941
|
+
writeFile(shape);
|
|
6942
|
+
} catch {
|
|
6943
|
+
}
|
|
6944
|
+
}
|
|
6945
|
+
function hostSessionStoragePath() {
|
|
6946
|
+
return storagePath();
|
|
6947
|
+
}
|
|
6948
|
+
|
|
6949
|
+
// src/util/safe-url.ts
|
|
6950
|
+
var UnsafeApiUrlError = class extends Error {
|
|
6951
|
+
constructor(raw, reason) {
|
|
6952
|
+
super(`unsafe AgenticMail API URL ${JSON.stringify(raw)}: ${reason}`);
|
|
6953
|
+
this.raw = raw;
|
|
6954
|
+
this.reason = reason;
|
|
6955
|
+
this.name = "UnsafeApiUrlError";
|
|
6956
|
+
}
|
|
6957
|
+
};
|
|
6958
|
+
var BLOCKED_HOSTS = /* @__PURE__ */ new Set([
|
|
6959
|
+
"169.254.169.254",
|
|
6960
|
+
// AWS / Azure / GCP IPv4 metadata
|
|
6961
|
+
"fd00:ec2::254",
|
|
6962
|
+
// AWS IPv6 metadata
|
|
6963
|
+
"metadata.google.internal",
|
|
6964
|
+
// GCP DNS
|
|
6965
|
+
"metadata.azure.internal"
|
|
6966
|
+
// Azure DNS
|
|
6967
|
+
]);
|
|
6968
|
+
function validateApiUrl(raw) {
|
|
6969
|
+
if (typeof raw !== "string" || raw.length === 0) {
|
|
6970
|
+
throw new UnsafeApiUrlError(String(raw), "must be a non-empty string");
|
|
6971
|
+
}
|
|
6972
|
+
let parsed;
|
|
6973
|
+
try {
|
|
6974
|
+
parsed = new URL(raw);
|
|
6975
|
+
} catch (err) {
|
|
6976
|
+
throw new UnsafeApiUrlError(raw, `unparseable: ${err.message}`);
|
|
6977
|
+
}
|
|
6978
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
6979
|
+
throw new UnsafeApiUrlError(raw, `unsupported protocol: ${parsed.protocol}`);
|
|
6980
|
+
}
|
|
6981
|
+
const host = parsed.hostname.toLowerCase().replace(/\.$/, "");
|
|
6982
|
+
if (BLOCKED_HOSTS.has(host)) {
|
|
6983
|
+
throw new UnsafeApiUrlError(raw, `blocked metadata host: ${host}`);
|
|
6984
|
+
}
|
|
6985
|
+
if (parsed.username || parsed.password) {
|
|
6986
|
+
throw new UnsafeApiUrlError(raw, "embedded credentials are not supported");
|
|
6987
|
+
}
|
|
6988
|
+
return parsed.origin;
|
|
6989
|
+
}
|
|
6990
|
+
function buildApiUrl(baseOrigin, pathAndQuery) {
|
|
6991
|
+
const path = pathAndQuery.startsWith("/") ? pathAndQuery : `/${pathAndQuery}`;
|
|
6992
|
+
return new URL(path, baseOrigin + "/").toString();
|
|
6993
|
+
}
|
|
6994
|
+
|
|
6751
6995
|
// src/setup/index.ts
|
|
6752
6996
|
var import_node_crypto3 = require("crypto");
|
|
6753
|
-
var
|
|
6754
|
-
var
|
|
6755
|
-
var
|
|
6997
|
+
var import_node_fs8 = require("fs");
|
|
6998
|
+
var import_node_path10 = require("path");
|
|
6999
|
+
var import_node_os8 = require("os");
|
|
6756
7000
|
|
|
6757
7001
|
// src/setup/deps.ts
|
|
6758
7002
|
var import_node_child_process2 = require("child_process");
|
|
6759
|
-
var
|
|
6760
|
-
var
|
|
6761
|
-
var
|
|
7003
|
+
var import_node_fs5 = require("fs");
|
|
7004
|
+
var import_node_path7 = require("path");
|
|
7005
|
+
var import_node_os5 = require("os");
|
|
6762
7006
|
var DependencyChecker = class {
|
|
6763
7007
|
async checkAll() {
|
|
6764
7008
|
return Promise.all([
|
|
@@ -6808,8 +7052,8 @@ var DependencyChecker = class {
|
|
|
6808
7052
|
}
|
|
6809
7053
|
}
|
|
6810
7054
|
async checkCloudflared() {
|
|
6811
|
-
const binPath = (0,
|
|
6812
|
-
if ((0,
|
|
7055
|
+
const binPath = (0, import_node_path7.join)((0, import_node_os5.homedir)(), ".agenticmail", "bin", "cloudflared");
|
|
7056
|
+
if ((0, import_node_fs5.existsSync)(binPath)) {
|
|
6813
7057
|
let version;
|
|
6814
7058
|
try {
|
|
6815
7059
|
const output = (0, import_node_child_process2.execFileSync)(binPath, ["--version"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
@@ -6831,14 +7075,14 @@ var DependencyChecker = class {
|
|
|
6831
7075
|
|
|
6832
7076
|
// src/setup/installer.ts
|
|
6833
7077
|
var import_node_child_process3 = require("child_process");
|
|
6834
|
-
var
|
|
7078
|
+
var import_node_fs6 = require("fs");
|
|
6835
7079
|
var import_promises2 = require("fs/promises");
|
|
6836
|
-
var
|
|
6837
|
-
var
|
|
7080
|
+
var import_node_path8 = require("path");
|
|
7081
|
+
var import_node_os6 = require("os");
|
|
6838
7082
|
function runShellWithRollingOutput(cmd, opts = {}) {
|
|
6839
7083
|
const maxLines = opts.maxLines ?? 20;
|
|
6840
7084
|
const timeout = opts.timeout ?? 3e5;
|
|
6841
|
-
return new Promise((
|
|
7085
|
+
return new Promise((resolve2, reject) => {
|
|
6842
7086
|
const child = (0, import_node_child_process3.spawn)("sh", ["-c", cmd], {
|
|
6843
7087
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6844
7088
|
timeout
|
|
@@ -6880,7 +7124,7 @@ function runShellWithRollingOutput(cmd, opts = {}) {
|
|
|
6880
7124
|
}
|
|
6881
7125
|
process.stdout.write(`\x1B[${displayedCount}A`);
|
|
6882
7126
|
}
|
|
6883
|
-
|
|
7127
|
+
resolve2({ exitCode: code ?? 1, fullOutput });
|
|
6884
7128
|
});
|
|
6885
7129
|
child.on("error", (err) => {
|
|
6886
7130
|
if (displayedCount > 0) {
|
|
@@ -6896,7 +7140,7 @@ function runShellWithRollingOutput(cmd, opts = {}) {
|
|
|
6896
7140
|
}
|
|
6897
7141
|
function runSilent(command, args, opts = {}) {
|
|
6898
7142
|
const timeout = opts.timeout ?? 3e5;
|
|
6899
|
-
return new Promise((
|
|
7143
|
+
return new Promise((resolve2, reject) => {
|
|
6900
7144
|
const child = (0, import_node_child_process3.spawn)(command, args, {
|
|
6901
7145
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6902
7146
|
timeout
|
|
@@ -6908,7 +7152,7 @@ function runSilent(command, args, opts = {}) {
|
|
|
6908
7152
|
child.stderr?.on("data", (d) => {
|
|
6909
7153
|
fullOutput += d.toString();
|
|
6910
7154
|
});
|
|
6911
|
-
child.on("close", (code) =>
|
|
7155
|
+
child.on("close", (code) => resolve2({ exitCode: code ?? 1, fullOutput }));
|
|
6912
7156
|
child.on("error", reject);
|
|
6913
7157
|
});
|
|
6914
7158
|
}
|
|
@@ -6944,7 +7188,7 @@ var DependencyInstaller = class {
|
|
|
6944
7188
|
*/
|
|
6945
7189
|
async installDocker() {
|
|
6946
7190
|
if (this.isDockerReady()) return;
|
|
6947
|
-
const os = (0,
|
|
7191
|
+
const os = (0, import_node_os6.platform)();
|
|
6948
7192
|
if (os === "darwin") {
|
|
6949
7193
|
await this.installDockerMac();
|
|
6950
7194
|
} else if (os === "linux") {
|
|
@@ -7010,15 +7254,15 @@ var DependencyInstaller = class {
|
|
|
7010
7254
|
try {
|
|
7011
7255
|
const composeBin = (0, import_node_child_process3.execFileSync)("which", ["docker-compose"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
7012
7256
|
if (!composeBin) return;
|
|
7013
|
-
const pluginDir = (0,
|
|
7014
|
-
const pluginPath = (0,
|
|
7015
|
-
if ((0,
|
|
7257
|
+
const pluginDir = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".docker", "cli-plugins");
|
|
7258
|
+
const pluginPath = (0, import_node_path8.join)(pluginDir, "docker-compose");
|
|
7259
|
+
if ((0, import_node_fs6.existsSync)(pluginPath)) return;
|
|
7016
7260
|
try {
|
|
7017
|
-
(0,
|
|
7261
|
+
(0, import_node_fs6.mkdirSync)(pluginDir, { recursive: true });
|
|
7018
7262
|
} catch {
|
|
7019
7263
|
}
|
|
7020
7264
|
try {
|
|
7021
|
-
(0,
|
|
7265
|
+
(0, import_node_fs6.symlinkSync)(composeBin, pluginPath);
|
|
7022
7266
|
} catch {
|
|
7023
7267
|
}
|
|
7024
7268
|
} catch {
|
|
@@ -7077,9 +7321,9 @@ var DependencyInstaller = class {
|
|
|
7077
7321
|
return;
|
|
7078
7322
|
}
|
|
7079
7323
|
this.onProgress("__progress__:5:Installing Docker Engine...");
|
|
7080
|
-
const tmpDir = (0,
|
|
7324
|
+
const tmpDir = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail", "tmp");
|
|
7081
7325
|
await (0, import_promises2.mkdir)(tmpDir, { recursive: true });
|
|
7082
|
-
const scriptPath = (0,
|
|
7326
|
+
const scriptPath = (0, import_node_path8.join)(tmpDir, "install-docker.sh");
|
|
7083
7327
|
const dlResult = await runShellWithRollingOutput(
|
|
7084
7328
|
`curl -fsSL https://get.docker.com -o "${scriptPath}" && sudo sh "${scriptPath}"`,
|
|
7085
7329
|
{ timeout: 3e5 }
|
|
@@ -7147,8 +7391,8 @@ var DependencyInstaller = class {
|
|
|
7147
7391
|
}
|
|
7148
7392
|
try {
|
|
7149
7393
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
7150
|
-
const dockerExe = (0,
|
|
7151
|
-
if ((0,
|
|
7394
|
+
const dockerExe = (0, import_node_path8.join)(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
7395
|
+
if ((0, import_node_fs6.existsSync)(dockerExe)) {
|
|
7152
7396
|
(0, import_node_child_process3.execSync)(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
7153
7397
|
}
|
|
7154
7398
|
} catch {
|
|
@@ -7198,8 +7442,8 @@ var DependencyInstaller = class {
|
|
|
7198
7442
|
this.onProgress("__progress__:70:Docker Desktop installed. Starting...");
|
|
7199
7443
|
try {
|
|
7200
7444
|
const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
7201
|
-
const dockerExe = (0,
|
|
7202
|
-
if ((0,
|
|
7445
|
+
const dockerExe = (0, import_node_path8.join)(programFiles, "Docker", "Docker", "Docker Desktop.exe");
|
|
7446
|
+
if ((0, import_node_fs6.existsSync)(dockerExe)) {
|
|
7203
7447
|
(0, import_node_child_process3.execSync)(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
|
|
7204
7448
|
}
|
|
7205
7449
|
} catch {
|
|
@@ -7236,12 +7480,12 @@ var DependencyInstaller = class {
|
|
|
7236
7480
|
* Start the Stalwart mail server Docker container.
|
|
7237
7481
|
*/
|
|
7238
7482
|
async startStalwart(composePath) {
|
|
7239
|
-
if (!(0,
|
|
7483
|
+
if (!(0, import_node_fs6.existsSync)(composePath)) {
|
|
7240
7484
|
throw new Error(`docker-compose.yml not found at: ${composePath}`);
|
|
7241
7485
|
}
|
|
7242
7486
|
if (!this.isDockerReady()) {
|
|
7243
7487
|
this.onProgress("Starting Docker...");
|
|
7244
|
-
const os = (0,
|
|
7488
|
+
const os = (0, import_node_os6.platform)();
|
|
7245
7489
|
if (os === "darwin") {
|
|
7246
7490
|
await this.startColima();
|
|
7247
7491
|
} else if (os === "win32") {
|
|
@@ -7259,7 +7503,7 @@ var DependencyInstaller = class {
|
|
|
7259
7503
|
}
|
|
7260
7504
|
}
|
|
7261
7505
|
this.onProgress("__progress__:10:Pulling mail server image...");
|
|
7262
|
-
if ((0,
|
|
7506
|
+
if ((0, import_node_os6.platform)() === "darwin") this.linkComposePlugin();
|
|
7263
7507
|
let composeResult = await runSilent("docker", ["compose", "-f", composePath, "up", "-d"], { timeout: 12e4 });
|
|
7264
7508
|
if (composeResult.exitCode !== 0 && hasCommand("docker-compose")) {
|
|
7265
7509
|
composeResult = await runSilent("docker-compose", ["-f", composePath, "up", "-d"], { timeout: 12e4 });
|
|
@@ -7294,22 +7538,22 @@ var DependencyInstaller = class {
|
|
|
7294
7538
|
* Returns the path to the installed binary.
|
|
7295
7539
|
*/
|
|
7296
7540
|
async installCloudflared() {
|
|
7297
|
-
const os = (0,
|
|
7298
|
-
const binDir = (0,
|
|
7541
|
+
const os = (0, import_node_os6.platform)();
|
|
7542
|
+
const binDir = (0, import_node_path8.join)((0, import_node_os6.homedir)(), ".agenticmail", "bin");
|
|
7299
7543
|
const binName = os === "win32" ? "cloudflared.exe" : "cloudflared";
|
|
7300
|
-
const binPath = (0,
|
|
7301
|
-
if ((0,
|
|
7544
|
+
const binPath = (0, import_node_path8.join)(binDir, binName);
|
|
7545
|
+
if ((0, import_node_fs6.existsSync)(binPath)) {
|
|
7302
7546
|
return binPath;
|
|
7303
7547
|
}
|
|
7304
7548
|
try {
|
|
7305
7549
|
const whichCmd = os === "win32" ? "where" : "which";
|
|
7306
7550
|
const sysPath = (0, import_node_child_process3.execFileSync)(whichCmd, ["cloudflared"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim().split("\n")[0];
|
|
7307
|
-
if (sysPath && (0,
|
|
7551
|
+
if (sysPath && (0, import_node_fs6.existsSync)(sysPath)) return sysPath;
|
|
7308
7552
|
} catch {
|
|
7309
7553
|
}
|
|
7310
7554
|
this.onProgress("Downloading cloudflared...");
|
|
7311
7555
|
await (0, import_promises2.mkdir)(binDir, { recursive: true });
|
|
7312
|
-
const cpu = (0,
|
|
7556
|
+
const cpu = (0, import_node_os6.arch)();
|
|
7313
7557
|
const archName = cpu === "arm64" ? "arm64" : "amd64";
|
|
7314
7558
|
let downloadUrl;
|
|
7315
7559
|
if (os === "darwin") {
|
|
@@ -7327,7 +7571,7 @@ var DependencyInstaller = class {
|
|
|
7327
7571
|
}
|
|
7328
7572
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
7329
7573
|
if (os === "darwin") {
|
|
7330
|
-
const tgzPath = (0,
|
|
7574
|
+
const tgzPath = (0, import_node_path8.join)(binDir, "cloudflared.tgz");
|
|
7331
7575
|
await (0, import_promises2.writeFile)(tgzPath, buffer);
|
|
7332
7576
|
try {
|
|
7333
7577
|
(0, import_node_child_process3.execFileSync)("tar", ["-xzf", tgzPath, "-C", binDir, "cloudflared"], { timeout: 15e3, stdio: "ignore" });
|
|
@@ -7344,7 +7588,7 @@ var DependencyInstaller = class {
|
|
|
7344
7588
|
if (os !== "win32") await (0, import_promises2.chmod)(tmpPath, 493);
|
|
7345
7589
|
await (0, import_promises2.rename)(tmpPath, binPath);
|
|
7346
7590
|
}
|
|
7347
|
-
if (!(0,
|
|
7591
|
+
if (!(0, import_node_fs6.existsSync)(binPath)) {
|
|
7348
7592
|
throw new Error("cloudflared download succeeded but binary not found after extraction");
|
|
7349
7593
|
}
|
|
7350
7594
|
this.onProgress("cloudflared installed");
|
|
@@ -7362,23 +7606,23 @@ var DependencyInstaller = class {
|
|
|
7362
7606
|
|
|
7363
7607
|
// src/setup/service.ts
|
|
7364
7608
|
var import_node_child_process4 = require("child_process");
|
|
7365
|
-
var
|
|
7366
|
-
var
|
|
7367
|
-
var
|
|
7609
|
+
var import_node_fs7 = require("fs");
|
|
7610
|
+
var import_node_path9 = require("path");
|
|
7611
|
+
var import_node_os7 = require("os");
|
|
7368
7612
|
var import_node_module2 = require("module");
|
|
7369
7613
|
var import_meta2 = {};
|
|
7370
7614
|
var PLIST_LABEL = "com.agenticmail.server";
|
|
7371
7615
|
var SYSTEMD_UNIT = "agenticmail.service";
|
|
7372
7616
|
var ServiceManager = class {
|
|
7373
|
-
os = (0,
|
|
7617
|
+
os = (0, import_node_os7.platform)();
|
|
7374
7618
|
/**
|
|
7375
7619
|
* Get the path to the service file.
|
|
7376
7620
|
*/
|
|
7377
7621
|
getServicePath() {
|
|
7378
7622
|
if (this.os === "darwin") {
|
|
7379
|
-
return (0,
|
|
7623
|
+
return (0, import_node_path9.join)((0, import_node_os7.homedir)(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
|
|
7380
7624
|
} else {
|
|
7381
|
-
return (0,
|
|
7625
|
+
return (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".config", "systemd", "user", SYSTEMD_UNIT);
|
|
7382
7626
|
}
|
|
7383
7627
|
}
|
|
7384
7628
|
/**
|
|
@@ -7412,40 +7656,40 @@ var ServiceManager = class {
|
|
|
7412
7656
|
try {
|
|
7413
7657
|
const req = (0, import_node_module2.createRequire)(import_meta2.url);
|
|
7414
7658
|
const resolved = req.resolve("@agenticmail/api");
|
|
7415
|
-
if ((0,
|
|
7659
|
+
if ((0, import_node_fs7.existsSync)(resolved)) return resolved;
|
|
7416
7660
|
} catch {
|
|
7417
7661
|
}
|
|
7418
7662
|
const parentPackages = [
|
|
7419
|
-
(0,
|
|
7663
|
+
(0, import_node_path9.join)("@agenticmail", "cli"),
|
|
7420
7664
|
// current scoped package
|
|
7421
7665
|
"agenticmail"
|
|
7422
7666
|
// legacy unscoped package
|
|
7423
7667
|
];
|
|
7424
7668
|
const baseDirs = [
|
|
7425
7669
|
// user-local install
|
|
7426
|
-
(0,
|
|
7670
|
+
(0, import_node_path9.join)((0, import_node_os7.homedir)(), "node_modules")
|
|
7427
7671
|
];
|
|
7428
7672
|
try {
|
|
7429
7673
|
const prefix = (0, import_node_child_process4.execSync)("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
7430
|
-
baseDirs.push((0,
|
|
7431
|
-
baseDirs.push((0,
|
|
7674
|
+
baseDirs.push((0, import_node_path9.join)(prefix, "lib", "node_modules"));
|
|
7675
|
+
baseDirs.push((0, import_node_path9.join)(prefix, "node_modules"));
|
|
7432
7676
|
} catch {
|
|
7433
7677
|
}
|
|
7434
7678
|
baseDirs.push("/opt/homebrew/lib/node_modules");
|
|
7435
7679
|
baseDirs.push("/usr/local/lib/node_modules");
|
|
7436
7680
|
for (const base of baseDirs) {
|
|
7437
|
-
const sibling = (0,
|
|
7438
|
-
if ((0,
|
|
7681
|
+
const sibling = (0, import_node_path9.join)(base, "@agenticmail", "api", "dist", "index.js");
|
|
7682
|
+
if ((0, import_node_fs7.existsSync)(sibling)) return sibling;
|
|
7439
7683
|
for (const parent of parentPackages) {
|
|
7440
|
-
const nested = (0,
|
|
7441
|
-
if ((0,
|
|
7684
|
+
const nested = (0, import_node_path9.join)(base, parent, "node_modules", "@agenticmail", "api", "dist", "index.js");
|
|
7685
|
+
if ((0, import_node_fs7.existsSync)(nested)) return nested;
|
|
7442
7686
|
}
|
|
7443
7687
|
}
|
|
7444
|
-
const dataDir = (0,
|
|
7445
|
-
const entryCache = (0,
|
|
7446
|
-
if ((0,
|
|
7447
|
-
const cached = (0,
|
|
7448
|
-
if (cached && (0,
|
|
7688
|
+
const dataDir = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail");
|
|
7689
|
+
const entryCache = (0, import_node_path9.join)(dataDir, "api-entry.path");
|
|
7690
|
+
if ((0, import_node_fs7.existsSync)(entryCache)) {
|
|
7691
|
+
const cached = (0, import_node_fs7.readFileSync)(entryCache, "utf-8").trim();
|
|
7692
|
+
if (cached && (0, import_node_fs7.existsSync)(cached)) return cached;
|
|
7449
7693
|
}
|
|
7450
7694
|
throw new Error("Could not find @agenticmail/api entry point. Run `agenticmail start` first to populate the cache.");
|
|
7451
7695
|
}
|
|
@@ -7453,9 +7697,9 @@ var ServiceManager = class {
|
|
|
7453
7697
|
* Cache the API entry path so the service can find it later.
|
|
7454
7698
|
*/
|
|
7455
7699
|
cacheApiEntryPath(entryPath) {
|
|
7456
|
-
const dataDir = (0,
|
|
7457
|
-
if (!(0,
|
|
7458
|
-
(0,
|
|
7700
|
+
const dataDir = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail");
|
|
7701
|
+
if (!(0, import_node_fs7.existsSync)(dataDir)) (0, import_node_fs7.mkdirSync)(dataDir, { recursive: true });
|
|
7702
|
+
(0, import_node_fs7.writeFileSync)((0, import_node_path9.join)(dataDir, "api-entry.path"), entryPath);
|
|
7459
7703
|
}
|
|
7460
7704
|
/**
|
|
7461
7705
|
* Get the current package version.
|
|
@@ -7468,36 +7712,36 @@ var ServiceManager = class {
|
|
|
7468
7712
|
try {
|
|
7469
7713
|
const req = (0, import_node_module2.createRequire)(import_meta2.url);
|
|
7470
7714
|
const pkgJson = req.resolve("@agenticmail/cli/package.json");
|
|
7471
|
-
if ((0,
|
|
7472
|
-
const pkg = JSON.parse((0,
|
|
7715
|
+
if ((0, import_node_fs7.existsSync)(pkgJson)) {
|
|
7716
|
+
const pkg = JSON.parse((0, import_node_fs7.readFileSync)(pkgJson, "utf-8"));
|
|
7473
7717
|
if (pkg.version) return pkg.version;
|
|
7474
7718
|
}
|
|
7475
7719
|
} catch {
|
|
7476
7720
|
}
|
|
7477
7721
|
try {
|
|
7478
7722
|
const apiEntry = this.getApiEntryPath();
|
|
7479
|
-
const apiPkg = (0,
|
|
7480
|
-
if ((0,
|
|
7481
|
-
const pkg = JSON.parse((0,
|
|
7723
|
+
const apiPkg = (0, import_node_path9.join)(apiEntry, "..", "..", "package.json");
|
|
7724
|
+
if ((0, import_node_fs7.existsSync)(apiPkg)) {
|
|
7725
|
+
const pkg = JSON.parse((0, import_node_fs7.readFileSync)(apiPkg, "utf-8"));
|
|
7482
7726
|
if (pkg.version) return pkg.version;
|
|
7483
7727
|
}
|
|
7484
7728
|
} catch {
|
|
7485
7729
|
}
|
|
7486
7730
|
const candidates = [
|
|
7487
|
-
(0,
|
|
7488
|
-
(0,
|
|
7489
|
-
(0,
|
|
7731
|
+
(0, import_node_path9.join)((0, import_node_os7.homedir)(), "node_modules", "@agenticmail", "cli", "package.json"),
|
|
7732
|
+
(0, import_node_path9.join)((0, import_node_os7.homedir)(), "node_modules", "agenticmail", "package.json"),
|
|
7733
|
+
(0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail", "package-version.json")
|
|
7490
7734
|
];
|
|
7491
7735
|
try {
|
|
7492
7736
|
const prefix = (0, import_node_child_process4.execSync)("npm prefix -g", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
|
|
7493
|
-
candidates.push((0,
|
|
7494
|
-
candidates.push((0,
|
|
7737
|
+
candidates.push((0, import_node_path9.join)(prefix, "lib", "node_modules", "@agenticmail", "cli", "package.json"));
|
|
7738
|
+
candidates.push((0, import_node_path9.join)(prefix, "lib", "node_modules", "agenticmail", "package.json"));
|
|
7495
7739
|
} catch {
|
|
7496
7740
|
}
|
|
7497
7741
|
for (const p of candidates) {
|
|
7498
7742
|
try {
|
|
7499
|
-
if ((0,
|
|
7500
|
-
const pkg = JSON.parse((0,
|
|
7743
|
+
if ((0, import_node_fs7.existsSync)(p)) {
|
|
7744
|
+
const pkg = JSON.parse((0, import_node_fs7.readFileSync)(p, "utf-8"));
|
|
7501
7745
|
if (pkg.version) return pkg.version;
|
|
7502
7746
|
}
|
|
7503
7747
|
} catch {
|
|
@@ -7510,9 +7754,9 @@ var ServiceManager = class {
|
|
|
7510
7754
|
* This ensures AgenticMail doesn't fail on boot when Docker is still loading.
|
|
7511
7755
|
*/
|
|
7512
7756
|
generateStartScript(nodePath, apiEntry) {
|
|
7513
|
-
const scriptPath = (0,
|
|
7514
|
-
const scriptDir = (0,
|
|
7515
|
-
if (!(0,
|
|
7757
|
+
const scriptPath = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail", "bin", "start-server.sh");
|
|
7758
|
+
const scriptDir = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail", "bin");
|
|
7759
|
+
if (!(0, import_node_fs7.existsSync)(scriptDir)) (0, import_node_fs7.mkdirSync)(scriptDir, { recursive: true });
|
|
7516
7760
|
const script = [
|
|
7517
7761
|
"#!/bin/bash",
|
|
7518
7762
|
"# AgenticMail auto-start script",
|
|
@@ -7563,7 +7807,7 @@ var ServiceManager = class {
|
|
|
7563
7807
|
`log "Starting API server: ${nodePath} ${apiEntry}"`,
|
|
7564
7808
|
`exec "${nodePath}" "${apiEntry}"`
|
|
7565
7809
|
].join("\n") + "\n";
|
|
7566
|
-
(0,
|
|
7810
|
+
(0, import_node_fs7.writeFileSync)(scriptPath, script, { mode: 493 });
|
|
7567
7811
|
return scriptPath;
|
|
7568
7812
|
}
|
|
7569
7813
|
/**
|
|
@@ -7576,9 +7820,9 @@ var ServiceManager = class {
|
|
|
7576
7820
|
* - Service version tracking in env vars
|
|
7577
7821
|
*/
|
|
7578
7822
|
generatePlist(nodePath, apiEntry, configPath) {
|
|
7579
|
-
const config = JSON.parse((0,
|
|
7580
|
-
const logDir = (0,
|
|
7581
|
-
if (!(0,
|
|
7823
|
+
const config = JSON.parse((0, import_node_fs7.readFileSync)(configPath, "utf-8"));
|
|
7824
|
+
const logDir = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail", "logs");
|
|
7825
|
+
if (!(0, import_node_fs7.existsSync)(logDir)) (0, import_node_fs7.mkdirSync)(logDir, { recursive: true });
|
|
7582
7826
|
const version = this.getVersion();
|
|
7583
7827
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
7584
7828
|
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -7599,9 +7843,9 @@ var ServiceManager = class {
|
|
|
7599
7843
|
<key>EnvironmentVariables</key>
|
|
7600
7844
|
<dict>
|
|
7601
7845
|
<key>HOME</key>
|
|
7602
|
-
<string>${(0,
|
|
7846
|
+
<string>${(0, import_node_os7.homedir)()}</string>
|
|
7603
7847
|
<key>AGENTICMAIL_DATA_DIR</key>
|
|
7604
|
-
<string>${config.dataDir || (0,
|
|
7848
|
+
<string>${config.dataDir || (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail")}</string>
|
|
7605
7849
|
<key>PATH</key>
|
|
7606
7850
|
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
|
7607
7851
|
<key>AGENTICMAIL_SERVICE_VERSION</key>
|
|
@@ -7654,8 +7898,8 @@ var ServiceManager = class {
|
|
|
7654
7898
|
* - Proper dependency ordering
|
|
7655
7899
|
*/
|
|
7656
7900
|
generateSystemdUnit(nodePath, apiEntry, configPath) {
|
|
7657
|
-
const config = JSON.parse((0,
|
|
7658
|
-
const dataDir = config.dataDir || (0,
|
|
7901
|
+
const config = JSON.parse((0, import_node_fs7.readFileSync)(configPath, "utf-8"));
|
|
7902
|
+
const dataDir = config.dataDir || (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail");
|
|
7659
7903
|
const version = this.getVersion();
|
|
7660
7904
|
const startScript = this.generateStartScript(nodePath, apiEntry);
|
|
7661
7905
|
return `[Unit]
|
|
@@ -7672,7 +7916,7 @@ Restart=always
|
|
|
7672
7916
|
RestartSec=15
|
|
7673
7917
|
TimeoutStartSec=660
|
|
7674
7918
|
LimitNOFILE=8192
|
|
7675
|
-
Environment=HOME=${(0,
|
|
7919
|
+
Environment=HOME=${(0, import_node_os7.homedir)()}
|
|
7676
7920
|
Environment=AGENTICMAIL_DATA_DIR=${dataDir}
|
|
7677
7921
|
Environment=PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin
|
|
7678
7922
|
Environment=AGENTICMAIL_SERVICE_VERSION=${version}
|
|
@@ -7685,8 +7929,8 @@ WantedBy=default.target
|
|
|
7685
7929
|
* Install the auto-start service.
|
|
7686
7930
|
*/
|
|
7687
7931
|
install() {
|
|
7688
|
-
const configPath = (0,
|
|
7689
|
-
if (!(0,
|
|
7932
|
+
const configPath = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail", "config.json");
|
|
7933
|
+
if (!(0, import_node_fs7.existsSync)(configPath)) {
|
|
7690
7934
|
return { installed: false, message: "Config not found. Run agenticmail setup first." };
|
|
7691
7935
|
}
|
|
7692
7936
|
const nodePath = this.getNodePath();
|
|
@@ -7698,17 +7942,17 @@ WantedBy=default.target
|
|
|
7698
7942
|
}
|
|
7699
7943
|
const servicePath = this.getServicePath();
|
|
7700
7944
|
if (this.os === "darwin") {
|
|
7701
|
-
const dir = (0,
|
|
7702
|
-
if (!(0,
|
|
7703
|
-
if ((0,
|
|
7945
|
+
const dir = (0, import_node_path9.join)((0, import_node_os7.homedir)(), "Library", "LaunchAgents");
|
|
7946
|
+
if (!(0, import_node_fs7.existsSync)(dir)) (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
|
|
7947
|
+
if ((0, import_node_fs7.existsSync)(servicePath)) {
|
|
7704
7948
|
try {
|
|
7705
7949
|
(0, import_node_child_process4.execFileSync)("launchctl", ["unload", servicePath], { timeout: 1e4, stdio: "ignore" });
|
|
7706
7950
|
} catch {
|
|
7707
7951
|
}
|
|
7708
7952
|
}
|
|
7709
7953
|
const plist = this.generatePlist(nodePath, apiEntry, configPath);
|
|
7710
|
-
(0,
|
|
7711
|
-
(0,
|
|
7954
|
+
(0, import_node_fs7.writeFileSync)(servicePath, plist);
|
|
7955
|
+
(0, import_node_fs7.chmodSync)(servicePath, 384);
|
|
7712
7956
|
try {
|
|
7713
7957
|
(0, import_node_child_process4.execFileSync)("launchctl", ["load", servicePath], { timeout: 1e4, stdio: "ignore" });
|
|
7714
7958
|
} catch (err) {
|
|
@@ -7716,11 +7960,11 @@ WantedBy=default.target
|
|
|
7716
7960
|
}
|
|
7717
7961
|
return { installed: true, message: `Service installed at ${servicePath}` };
|
|
7718
7962
|
} else if (this.os === "linux") {
|
|
7719
|
-
const dir = (0,
|
|
7720
|
-
if (!(0,
|
|
7963
|
+
const dir = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".config", "systemd", "user");
|
|
7964
|
+
if (!(0, import_node_fs7.existsSync)(dir)) (0, import_node_fs7.mkdirSync)(dir, { recursive: true });
|
|
7721
7965
|
const unit = this.generateSystemdUnit(nodePath, apiEntry, configPath);
|
|
7722
|
-
(0,
|
|
7723
|
-
(0,
|
|
7966
|
+
(0, import_node_fs7.writeFileSync)(servicePath, unit);
|
|
7967
|
+
(0, import_node_fs7.chmodSync)(servicePath, 384);
|
|
7724
7968
|
try {
|
|
7725
7969
|
(0, import_node_child_process4.execFileSync)("systemctl", ["--user", "daemon-reload"], { timeout: 1e4, stdio: "ignore" });
|
|
7726
7970
|
(0, import_node_child_process4.execFileSync)("systemctl", ["--user", "enable", SYSTEMD_UNIT], { timeout: 1e4, stdio: "ignore" });
|
|
@@ -7742,7 +7986,7 @@ WantedBy=default.target
|
|
|
7742
7986
|
*/
|
|
7743
7987
|
uninstall() {
|
|
7744
7988
|
const servicePath = this.getServicePath();
|
|
7745
|
-
if (!(0,
|
|
7989
|
+
if (!(0, import_node_fs7.existsSync)(servicePath)) {
|
|
7746
7990
|
return { removed: false, message: "Service is not installed." };
|
|
7747
7991
|
}
|
|
7748
7992
|
if (this.os === "darwin") {
|
|
@@ -7751,7 +7995,7 @@ WantedBy=default.target
|
|
|
7751
7995
|
} catch {
|
|
7752
7996
|
}
|
|
7753
7997
|
try {
|
|
7754
|
-
(0,
|
|
7998
|
+
(0, import_node_fs7.unlinkSync)(servicePath);
|
|
7755
7999
|
} catch {
|
|
7756
8000
|
}
|
|
7757
8001
|
return { removed: true, message: "Service removed." };
|
|
@@ -7762,7 +8006,7 @@ WantedBy=default.target
|
|
|
7762
8006
|
} catch {
|
|
7763
8007
|
}
|
|
7764
8008
|
try {
|
|
7765
|
-
(0,
|
|
8009
|
+
(0, import_node_fs7.unlinkSync)(servicePath);
|
|
7766
8010
|
} catch {
|
|
7767
8011
|
}
|
|
7768
8012
|
try {
|
|
@@ -7780,7 +8024,7 @@ WantedBy=default.target
|
|
|
7780
8024
|
status() {
|
|
7781
8025
|
const servicePath = this.getServicePath();
|
|
7782
8026
|
const plat = this.os === "darwin" ? "launchd" : this.os === "linux" ? "systemd" : "unsupported";
|
|
7783
|
-
const installed = (0,
|
|
8027
|
+
const installed = (0, import_node_fs7.existsSync)(servicePath);
|
|
7784
8028
|
let running = false;
|
|
7785
8029
|
if (installed) {
|
|
7786
8030
|
if (this.os === "darwin") {
|
|
@@ -7835,34 +8079,34 @@ WantedBy=default.target
|
|
|
7835
8079
|
needsRepair() {
|
|
7836
8080
|
if (this.os !== "darwin" && this.os !== "linux") return null;
|
|
7837
8081
|
const servicePath = this.getServicePath();
|
|
7838
|
-
if (!(0,
|
|
8082
|
+
if (!(0, import_node_fs7.existsSync)(servicePath)) return null;
|
|
7839
8083
|
let serviceContent = "";
|
|
7840
8084
|
try {
|
|
7841
|
-
serviceContent = (0,
|
|
8085
|
+
serviceContent = (0, import_node_fs7.readFileSync)(servicePath, "utf-8");
|
|
7842
8086
|
} catch {
|
|
7843
8087
|
return { reason: "Service file unreadable" };
|
|
7844
8088
|
}
|
|
7845
|
-
const startScript = (0,
|
|
8089
|
+
const startScript = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail", "bin", "start-server.sh");
|
|
7846
8090
|
if (serviceContent.includes(startScript)) {
|
|
7847
|
-
if (!(0,
|
|
8091
|
+
if (!(0, import_node_fs7.existsSync)(startScript)) {
|
|
7848
8092
|
return { reason: "start-server.sh is missing" };
|
|
7849
8093
|
}
|
|
7850
8094
|
let scriptContent = "";
|
|
7851
8095
|
try {
|
|
7852
|
-
scriptContent = (0,
|
|
8096
|
+
scriptContent = (0, import_node_fs7.readFileSync)(startScript, "utf-8");
|
|
7853
8097
|
} catch {
|
|
7854
8098
|
return { reason: "start-server.sh unreadable" };
|
|
7855
8099
|
}
|
|
7856
8100
|
const apiPathMatch = scriptContent.match(/(\/[^"\s]+@agenticmail\/api\/dist\/index\.js)/);
|
|
7857
8101
|
if (apiPathMatch) {
|
|
7858
8102
|
const referenced = apiPathMatch[1];
|
|
7859
|
-
if (!(0,
|
|
8103
|
+
if (!(0, import_node_fs7.existsSync)(referenced)) {
|
|
7860
8104
|
return { reason: `start-server.sh references missing path: ${referenced}` };
|
|
7861
8105
|
}
|
|
7862
8106
|
}
|
|
7863
8107
|
if (/node_modules\/agenticmail\/(?!.*@agenticmail\/cli)/.test(scriptContent)) {
|
|
7864
8108
|
const stale = /(\S*node_modules\/agenticmail\/\S*)/.exec(scriptContent)?.[1];
|
|
7865
|
-
if (stale && !(0,
|
|
8109
|
+
if (stale && !(0, import_node_fs7.existsSync)(stale)) {
|
|
7866
8110
|
return { reason: `start-server.sh references legacy unscoped path: ${stale}` };
|
|
7867
8111
|
}
|
|
7868
8112
|
}
|
|
@@ -7875,11 +8119,11 @@ WantedBy=default.target
|
|
|
7875
8119
|
return { reason: `Service version drift (current CLI is v${currentVersion})` };
|
|
7876
8120
|
}
|
|
7877
8121
|
}
|
|
7878
|
-
const entryCache = (0,
|
|
7879
|
-
if ((0,
|
|
8122
|
+
const entryCache = (0, import_node_path9.join)((0, import_node_os7.homedir)(), ".agenticmail", "api-entry.path");
|
|
8123
|
+
if ((0, import_node_fs7.existsSync)(entryCache)) {
|
|
7880
8124
|
try {
|
|
7881
|
-
const cached = (0,
|
|
7882
|
-
if (cached && !(0,
|
|
8125
|
+
const cached = (0, import_node_fs7.readFileSync)(entryCache, "utf-8").trim();
|
|
8126
|
+
if (cached && !(0, import_node_fs7.existsSync)(cached)) {
|
|
7883
8127
|
return { reason: `Cached API entry path no longer exists: ${cached}` };
|
|
7884
8128
|
}
|
|
7885
8129
|
} catch {
|
|
@@ -7930,13 +8174,13 @@ var SetupManager = class {
|
|
|
7930
8174
|
* falls back to monorepo location.
|
|
7931
8175
|
*/
|
|
7932
8176
|
getComposePath() {
|
|
7933
|
-
const standalonePath = (0,
|
|
7934
|
-
if ((0,
|
|
8177
|
+
const standalonePath = (0, import_node_path10.join)((0, import_node_os8.homedir)(), ".agenticmail", "docker-compose.yml");
|
|
8178
|
+
if ((0, import_node_fs8.existsSync)(standalonePath)) return standalonePath;
|
|
7935
8179
|
const cwd = process.cwd();
|
|
7936
|
-
const candidates = [cwd, (0,
|
|
8180
|
+
const candidates = [cwd, (0, import_node_path10.join)(cwd, "..")];
|
|
7937
8181
|
for (const dir of candidates) {
|
|
7938
|
-
const p = (0,
|
|
7939
|
-
if ((0,
|
|
8182
|
+
const p = (0, import_node_path10.join)(dir, "docker-compose.yml");
|
|
8183
|
+
if ((0, import_node_fs8.existsSync)(p)) return p;
|
|
7940
8184
|
}
|
|
7941
8185
|
return standalonePath;
|
|
7942
8186
|
}
|
|
@@ -7946,19 +8190,19 @@ var SetupManager = class {
|
|
|
7946
8190
|
* Always regenerates Docker files to keep passwords in sync.
|
|
7947
8191
|
*/
|
|
7948
8192
|
initConfig() {
|
|
7949
|
-
const dataDir = (0,
|
|
7950
|
-
const configPath = (0,
|
|
7951
|
-
const envPath = (0,
|
|
7952
|
-
if ((0,
|
|
8193
|
+
const dataDir = (0, import_node_path10.join)((0, import_node_os8.homedir)(), ".agenticmail");
|
|
8194
|
+
const configPath = (0, import_node_path10.join)(dataDir, "config.json");
|
|
8195
|
+
const envPath = (0, import_node_path10.join)(dataDir, ".env");
|
|
8196
|
+
if ((0, import_node_fs8.existsSync)(configPath)) {
|
|
7953
8197
|
try {
|
|
7954
|
-
const existing = JSON.parse((0,
|
|
8198
|
+
const existing = JSON.parse((0, import_node_fs8.readFileSync)(configPath, "utf-8"));
|
|
7955
8199
|
this.generateDockerFiles(existing);
|
|
7956
8200
|
return { configPath, envPath, config: existing, isNew: false };
|
|
7957
8201
|
} catch {
|
|
7958
8202
|
}
|
|
7959
8203
|
}
|
|
7960
|
-
if (!(0,
|
|
7961
|
-
(0,
|
|
8204
|
+
if (!(0, import_node_fs8.existsSync)(dataDir)) {
|
|
8205
|
+
(0, import_node_fs8.mkdirSync)(dataDir, { recursive: true });
|
|
7962
8206
|
}
|
|
7963
8207
|
const masterKey = `mk_${(0, import_node_crypto3.randomBytes)(24).toString("hex")}`;
|
|
7964
8208
|
const stalwartPassword = (0, import_node_crypto3.randomBytes)(16).toString("hex");
|
|
@@ -7974,8 +8218,8 @@ var SetupManager = class {
|
|
|
7974
8218
|
api: { port: 3829, host: "127.0.0.1" },
|
|
7975
8219
|
dataDir
|
|
7976
8220
|
};
|
|
7977
|
-
(0,
|
|
7978
|
-
(0,
|
|
8221
|
+
(0, import_node_fs8.writeFileSync)(configPath, JSON.stringify(config, null, 2));
|
|
8222
|
+
(0, import_node_fs8.chmodSync)(configPath, 384);
|
|
7979
8223
|
const envContent = `# Auto-generated by agenticmail setup
|
|
7980
8224
|
STALWART_ADMIN_USER=admin
|
|
7981
8225
|
STALWART_ADMIN_PASSWORD=${stalwartPassword}
|
|
@@ -7990,8 +8234,8 @@ SMTP_PORT=587
|
|
|
7990
8234
|
IMAP_HOST=localhost
|
|
7991
8235
|
IMAP_PORT=143
|
|
7992
8236
|
`;
|
|
7993
|
-
(0,
|
|
7994
|
-
(0,
|
|
8237
|
+
(0, import_node_fs8.writeFileSync)(envPath, envContent);
|
|
8238
|
+
(0, import_node_fs8.chmodSync)(envPath, 384);
|
|
7995
8239
|
this.generateDockerFiles(config);
|
|
7996
8240
|
return { configPath, envPath, config, isNew: true };
|
|
7997
8241
|
}
|
|
@@ -8000,13 +8244,13 @@ IMAP_PORT=143
|
|
|
8000
8244
|
* with the correct admin password from config.
|
|
8001
8245
|
*/
|
|
8002
8246
|
generateDockerFiles(config) {
|
|
8003
|
-
const dataDir = config.dataDir || (0,
|
|
8004
|
-
if (!(0,
|
|
8005
|
-
(0,
|
|
8247
|
+
const dataDir = config.dataDir || (0, import_node_path10.join)((0, import_node_os8.homedir)(), ".agenticmail");
|
|
8248
|
+
if (!(0, import_node_fs8.existsSync)(dataDir)) {
|
|
8249
|
+
(0, import_node_fs8.mkdirSync)(dataDir, { recursive: true });
|
|
8006
8250
|
}
|
|
8007
8251
|
const password = config.stalwart?.adminPassword || "changeme";
|
|
8008
|
-
const composePath = (0,
|
|
8009
|
-
(0,
|
|
8252
|
+
const composePath = (0, import_node_path10.join)(dataDir, "docker-compose.yml");
|
|
8253
|
+
(0, import_node_fs8.writeFileSync)(composePath, `services:
|
|
8010
8254
|
stalwart:
|
|
8011
8255
|
# Pinned to v0.15.5 \u2014 Stalwart 0.16+ moved its config to JSON
|
|
8012
8256
|
# at /etc/stalwart/config.json (hardcoded into the container
|
|
@@ -8035,9 +8279,9 @@ IMAP_PORT=143
|
|
|
8035
8279
|
volumes:
|
|
8036
8280
|
stalwart-data:
|
|
8037
8281
|
`);
|
|
8038
|
-
(0,
|
|
8039
|
-
const tomlPath = (0,
|
|
8040
|
-
(0,
|
|
8282
|
+
(0, import_node_fs8.chmodSync)(composePath, 384);
|
|
8283
|
+
const tomlPath = (0, import_node_path10.join)(dataDir, "stalwart.toml");
|
|
8284
|
+
(0, import_node_fs8.writeFileSync)(tomlPath, `# Stalwart Mail Server \u2014 AgenticMail Configuration
|
|
8041
8285
|
|
|
8042
8286
|
[server]
|
|
8043
8287
|
hostname = "localhost"
|
|
@@ -8087,21 +8331,21 @@ enable = true
|
|
|
8087
8331
|
user = "admin"
|
|
8088
8332
|
secret = "${password}"
|
|
8089
8333
|
`);
|
|
8090
|
-
(0,
|
|
8334
|
+
(0, import_node_fs8.chmodSync)(tomlPath, 384);
|
|
8091
8335
|
}
|
|
8092
8336
|
/**
|
|
8093
8337
|
* Check if config has already been initialized.
|
|
8094
8338
|
*/
|
|
8095
8339
|
isInitialized() {
|
|
8096
|
-
const configPath = (0,
|
|
8097
|
-
return (0,
|
|
8340
|
+
const configPath = (0, import_node_path10.join)((0, import_node_os8.homedir)(), ".agenticmail", "config.json");
|
|
8341
|
+
return (0, import_node_fs8.existsSync)(configPath);
|
|
8098
8342
|
}
|
|
8099
8343
|
};
|
|
8100
8344
|
|
|
8101
8345
|
// src/threading/thread-id.ts
|
|
8102
8346
|
var import_node_crypto4 = require("crypto");
|
|
8103
8347
|
function stripReplyPrefixes(subject) {
|
|
8104
|
-
let s = subject;
|
|
8348
|
+
let s = subject.length > 1e3 ? subject.slice(0, 1e3) : subject;
|
|
8105
8349
|
for (; ; ) {
|
|
8106
8350
|
const next = s.replace(/^\s*(?:re|fwd?|fw)\s*(?:\[\d+\])?\s*:\s*/i, "");
|
|
8107
8351
|
if (next === s) break;
|
|
@@ -8121,8 +8365,9 @@ function normalizeSubject(subject) {
|
|
|
8121
8365
|
}
|
|
8122
8366
|
function normalizeAddress(addr) {
|
|
8123
8367
|
if (!addr) return "(unknown)";
|
|
8124
|
-
const
|
|
8125
|
-
const
|
|
8368
|
+
const bounded = addr.length > 500 ? addr.slice(0, 500) : addr;
|
|
8369
|
+
const m = bounded.match(/<([^>]+)>/);
|
|
8370
|
+
const raw = m ? m[1] : bounded;
|
|
8126
8371
|
return raw.trim().toLowerCase();
|
|
8127
8372
|
}
|
|
8128
8373
|
function threadIdFor(input) {
|
|
@@ -8131,10 +8376,10 @@ function threadIdFor(input) {
|
|
|
8131
8376
|
}
|
|
8132
8377
|
|
|
8133
8378
|
// src/threading/thread-cache.ts
|
|
8134
|
-
var
|
|
8135
|
-
var
|
|
8136
|
-
var
|
|
8137
|
-
var CACHE_DIR_DEFAULT = (0,
|
|
8379
|
+
var import_node_fs9 = require("fs");
|
|
8380
|
+
var import_node_os9 = require("os");
|
|
8381
|
+
var import_node_path11 = require("path");
|
|
8382
|
+
var CACHE_DIR_DEFAULT = (0, import_node_path11.join)((0, import_node_os9.homedir)(), ".agenticmail", "thread-cache");
|
|
8138
8383
|
var DEFAULT_K_MESSAGES = 10;
|
|
8139
8384
|
var DEFAULT_LRU_CAP = 5e3;
|
|
8140
8385
|
var PREVIEW_MAX_CHARS = 240;
|
|
@@ -8147,22 +8392,22 @@ var ThreadCache = class {
|
|
|
8147
8392
|
this.k = opts.k ?? DEFAULT_K_MESSAGES;
|
|
8148
8393
|
this.lruCap = opts.lruCap ?? DEFAULT_LRU_CAP;
|
|
8149
8394
|
try {
|
|
8150
|
-
(0,
|
|
8395
|
+
(0, import_node_fs9.mkdirSync)(this.dir, { recursive: true });
|
|
8151
8396
|
} catch {
|
|
8152
8397
|
}
|
|
8153
8398
|
}
|
|
8154
8399
|
pathFor(threadId) {
|
|
8155
|
-
return (0,
|
|
8400
|
+
return (0, import_node_path11.join)(this.dir, `${threadId}.json`);
|
|
8156
8401
|
}
|
|
8157
8402
|
read(threadId) {
|
|
8158
8403
|
const p = this.pathFor(threadId);
|
|
8159
|
-
if (!(0,
|
|
8404
|
+
if (!(0, import_node_fs9.existsSync)(p)) return null;
|
|
8160
8405
|
try {
|
|
8161
|
-
const raw = (0,
|
|
8406
|
+
const raw = (0, import_node_fs9.readFileSync)(p, "utf-8");
|
|
8162
8407
|
return JSON.parse(raw);
|
|
8163
8408
|
} catch {
|
|
8164
8409
|
try {
|
|
8165
|
-
(0,
|
|
8410
|
+
(0, import_node_fs9.rmSync)(p, { force: true });
|
|
8166
8411
|
} catch {
|
|
8167
8412
|
}
|
|
8168
8413
|
return null;
|
|
@@ -8203,7 +8448,7 @@ var ThreadCache = class {
|
|
|
8203
8448
|
/** Permanently remove a thread's cache (called on [FINAL] / [DONE] / [CLOSED] / [WRAP]). */
|
|
8204
8449
|
delete(threadId) {
|
|
8205
8450
|
try {
|
|
8206
|
-
(0,
|
|
8451
|
+
(0, import_node_fs9.rmSync)(this.pathFor(threadId), { force: true });
|
|
8207
8452
|
} catch {
|
|
8208
8453
|
}
|
|
8209
8454
|
}
|
|
@@ -8223,8 +8468,8 @@ var ThreadCache = class {
|
|
|
8223
8468
|
writeAtomic(threadId, entry) {
|
|
8224
8469
|
const p = this.pathFor(threadId);
|
|
8225
8470
|
const tmp = `${p}.tmp`;
|
|
8226
|
-
(0,
|
|
8227
|
-
(0,
|
|
8471
|
+
(0, import_node_fs9.writeFileSync)(tmp, JSON.stringify(entry), "utf-8");
|
|
8472
|
+
(0, import_node_fs9.renameSync)(tmp, p);
|
|
8228
8473
|
}
|
|
8229
8474
|
/**
|
|
8230
8475
|
* Best-effort LRU eviction. Runs at most every 256 writes (we
|
|
@@ -8236,15 +8481,15 @@ var ThreadCache = class {
|
|
|
8236
8481
|
if (Math.random() > 1 / 256) return;
|
|
8237
8482
|
let files;
|
|
8238
8483
|
try {
|
|
8239
|
-
files = (0,
|
|
8484
|
+
files = (0, import_node_fs9.readdirSync)(this.dir).filter((f) => f.endsWith(".json"));
|
|
8240
8485
|
} catch {
|
|
8241
8486
|
return;
|
|
8242
8487
|
}
|
|
8243
8488
|
if (files.length <= this.lruCap) return;
|
|
8244
8489
|
const stats = files.map((f) => {
|
|
8245
|
-
const p = (0,
|
|
8490
|
+
const p = (0, import_node_path11.join)(this.dir, f);
|
|
8246
8491
|
try {
|
|
8247
|
-
return { p, mtime: (0,
|
|
8492
|
+
return { p, mtime: (0, import_node_fs9.statSync)(p).mtimeMs };
|
|
8248
8493
|
} catch {
|
|
8249
8494
|
return { p, mtime: 0 };
|
|
8250
8495
|
}
|
|
@@ -8253,7 +8498,7 @@ var ThreadCache = class {
|
|
|
8253
8498
|
const dropCount = Math.max(1, Math.floor(this.lruCap * 0.1));
|
|
8254
8499
|
for (let i = 0; i < dropCount; i++) {
|
|
8255
8500
|
try {
|
|
8256
|
-
(0,
|
|
8501
|
+
(0, import_node_fs9.rmSync)(stats[i].p, { force: true });
|
|
8257
8502
|
} catch {
|
|
8258
8503
|
}
|
|
8259
8504
|
}
|
|
@@ -8272,30 +8517,30 @@ function dedupAndCap(messages, k) {
|
|
|
8272
8517
|
}
|
|
8273
8518
|
|
|
8274
8519
|
// src/threading/agent-memory.ts
|
|
8275
|
-
var
|
|
8276
|
-
var
|
|
8277
|
-
var
|
|
8278
|
-
var MEMORY_DIR_DEFAULT = (0,
|
|
8520
|
+
var import_node_fs10 = require("fs");
|
|
8521
|
+
var import_node_os10 = require("os");
|
|
8522
|
+
var import_node_path12 = require("path");
|
|
8523
|
+
var MEMORY_DIR_DEFAULT = (0, import_node_path12.join)((0, import_node_os10.homedir)(), ".agenticmail", "agent-memory");
|
|
8279
8524
|
var AgentMemoryStore = class {
|
|
8280
8525
|
dir;
|
|
8281
8526
|
constructor(opts = {}) {
|
|
8282
8527
|
this.dir = opts.memoryDir ?? MEMORY_DIR_DEFAULT;
|
|
8283
8528
|
try {
|
|
8284
|
-
(0,
|
|
8529
|
+
(0, import_node_fs10.mkdirSync)(this.dir, { recursive: true });
|
|
8285
8530
|
} catch {
|
|
8286
8531
|
}
|
|
8287
8532
|
}
|
|
8288
8533
|
dirFor(agentId) {
|
|
8289
|
-
return (0,
|
|
8534
|
+
return (0, import_node_path12.join)(this.dir, sanitizeId(agentId));
|
|
8290
8535
|
}
|
|
8291
8536
|
pathFor(agentId, threadId) {
|
|
8292
|
-
return (0,
|
|
8537
|
+
return (0, import_node_path12.join)(this.dirFor(agentId), `${sanitizeId(threadId)}.md`);
|
|
8293
8538
|
}
|
|
8294
8539
|
read(agentId, threadId) {
|
|
8295
8540
|
const p = this.pathFor(agentId, threadId);
|
|
8296
|
-
if (!(0,
|
|
8541
|
+
if (!(0, import_node_fs10.existsSync)(p)) return null;
|
|
8297
8542
|
try {
|
|
8298
|
-
const raw = (0,
|
|
8543
|
+
const raw = (0, import_node_fs10.readFileSync)(p, "utf-8");
|
|
8299
8544
|
const parsed = parse(raw);
|
|
8300
8545
|
return { ...parsed, raw };
|
|
8301
8546
|
} catch {
|
|
@@ -8305,18 +8550,18 @@ var AgentMemoryStore = class {
|
|
|
8305
8550
|
write(agentId, threadId, fields) {
|
|
8306
8551
|
const agentDir = this.dirFor(agentId);
|
|
8307
8552
|
try {
|
|
8308
|
-
(0,
|
|
8553
|
+
(0, import_node_fs10.mkdirSync)(agentDir, { recursive: true });
|
|
8309
8554
|
} catch {
|
|
8310
8555
|
}
|
|
8311
8556
|
const body = render({ ...fields, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
|
|
8312
8557
|
const p = this.pathFor(agentId, threadId);
|
|
8313
8558
|
const tmp = `${p}.tmp`;
|
|
8314
|
-
(0,
|
|
8315
|
-
(0,
|
|
8559
|
+
(0, import_node_fs10.writeFileSync)(tmp, body, "utf-8");
|
|
8560
|
+
(0, import_node_fs10.renameSync)(tmp, p);
|
|
8316
8561
|
}
|
|
8317
8562
|
delete(agentId, threadId) {
|
|
8318
8563
|
try {
|
|
8319
|
-
(0,
|
|
8564
|
+
(0, import_node_fs10.rmSync)(this.pathFor(agentId, threadId), { force: true });
|
|
8320
8565
|
} catch {
|
|
8321
8566
|
}
|
|
8322
8567
|
}
|
|
@@ -8381,6 +8626,7 @@ function parse(raw) {
|
|
|
8381
8626
|
CloudflareClient,
|
|
8382
8627
|
DEFAULT_AGENT_NAME,
|
|
8383
8628
|
DEFAULT_AGENT_ROLE,
|
|
8629
|
+
DEFAULT_SESSION_MAX_AGE_MS,
|
|
8384
8630
|
DNSConfigurator,
|
|
8385
8631
|
DependencyChecker,
|
|
8386
8632
|
DependencyInstaller,
|
|
@@ -8391,6 +8637,8 @@ function parse(raw) {
|
|
|
8391
8637
|
InboxWatcher,
|
|
8392
8638
|
MailReceiver,
|
|
8393
8639
|
MailSender,
|
|
8640
|
+
PathTraversalError,
|
|
8641
|
+
REDACTED,
|
|
8394
8642
|
RELAY_PRESETS,
|
|
8395
8643
|
RelayBridge,
|
|
8396
8644
|
RelayGateway,
|
|
@@ -8402,7 +8650,10 @@ function parse(raw) {
|
|
|
8402
8650
|
StalwartAdmin,
|
|
8403
8651
|
ThreadCache,
|
|
8404
8652
|
TunnelManager,
|
|
8653
|
+
UnsafeApiUrlError,
|
|
8405
8654
|
WARNING_THRESHOLD,
|
|
8655
|
+
assertWithinBase,
|
|
8656
|
+
buildApiUrl,
|
|
8406
8657
|
buildInboundSecurityAdvisory,
|
|
8407
8658
|
classifyEmailRoute,
|
|
8408
8659
|
closeDatabase,
|
|
@@ -8412,21 +8663,31 @@ function parse(raw) {
|
|
|
8412
8663
|
ensureDataDir,
|
|
8413
8664
|
extractVerificationCode,
|
|
8414
8665
|
flushTelemetry,
|
|
8666
|
+
forgetHostSession,
|
|
8415
8667
|
getDatabase,
|
|
8668
|
+
hostSessionStoragePath,
|
|
8416
8669
|
isInternalEmail,
|
|
8670
|
+
isSessionFresh,
|
|
8417
8671
|
isValidPhoneNumber,
|
|
8672
|
+
loadHostSession,
|
|
8418
8673
|
normalizeAddress,
|
|
8419
8674
|
normalizePhoneNumber,
|
|
8420
8675
|
normalizeSubject,
|
|
8421
8676
|
parseEmail,
|
|
8422
8677
|
parseGoogleVoiceSms,
|
|
8423
8678
|
recordToolCall,
|
|
8679
|
+
redactObject,
|
|
8680
|
+
redactSecret,
|
|
8424
8681
|
resolveConfig,
|
|
8682
|
+
safeJoin,
|
|
8425
8683
|
sanitizeEmail,
|
|
8426
8684
|
saveConfig,
|
|
8685
|
+
saveHostSession,
|
|
8427
8686
|
scanOutboundEmail,
|
|
8428
8687
|
scoreEmail,
|
|
8429
8688
|
setTelemetryVersion,
|
|
8430
8689
|
startRelayBridge,
|
|
8431
|
-
threadIdFor
|
|
8690
|
+
threadIdFor,
|
|
8691
|
+
tryJoin,
|
|
8692
|
+
validateApiUrl
|
|
8432
8693
|
});
|