@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 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((resolve) => setTimeout(resolve, 1e3 * (attempt + 1)));
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((resolve) => {
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
- resolve(true);
1793
+ resolve2(true);
1778
1794
  });
1779
1795
  socket.on("error", () => {
1780
1796
  socket.destroy();
1781
- resolve(false);
1797
+ resolve2(false);
1782
1798
  });
1783
1799
  socket.on("timeout", () => {
1784
1800
  socket.destroy();
1785
- resolve(false);
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: readFileSync7, writeFileSync: writeFileSync8 } = await import("fs");
1856
- const { homedir: homedir11 } = await import("os");
1857
- const { join: join12 } = await import("path");
1858
- const configPath = join12(homedir11(), ".agenticmail", "stalwart.toml");
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 = readFileSync7(configPath, "utf-8");
1876
+ let config = readFileSync8(configPath, "utf-8");
1861
1877
  config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
1862
- writeFileSync8(configPath, config);
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: homedir11 } = require("os");
1872
- const { join: join12 } = require("path");
1873
- return join12(homedir11(), ".agenticmail", "stalwart.toml");
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: homedir11 } = require("os");
1878
- const { join: join12 } = require("path");
1879
- return join12(homedir11(), ".agenticmail");
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: readFileSync7, writeFileSync: writeFileSync8 } = await import("fs");
1981
- const { homedir: homedir11 } = await import("os");
1982
- const { join: join12 } = await import("path");
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 = join12(homedir11(), ".agenticmail", "stalwart.toml");
1985
- let toml = readFileSync7(tomlPath, "utf-8");
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
- writeFileSync8(tomlPath, toml, "utf-8");
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
- return html.replace(/<style[^>]*>[\s\S]*?<\/style>/gi, "").replace(/<script[^>]*>[\s\S]*?<\/script>/gi, "").replace(/<[^>]+>/g, "").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#0?39;/g, "'").replace(/&nbsp;/g, " ").replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCharCode(parseInt(hex, 16))).replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(parseInt(dec, 10)));
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(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#0?39;/g, "'").replace(/&nbsp;/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: writeFile2, rename: rename2 } = await import("fs/promises");
5022
+ const { writeFile: writeFile3, rename: rename2 } = await import("fs/promises");
5006
5023
  const tmpPath = this.binPath + ".tmp";
5007
- await writeFile2(tmpPath, buffer);
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((resolve, reject) => {
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
- resolve();
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((resolve) => p.on("exit", () => resolve())),
5097
- new Promise((resolve) => setTimeout(resolve, 5e3))
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 isGoogleVoice = fromLower.includes("voice-noreply@google.com") || fromLower.includes("@txt.voice.google.com") || fromLower.includes("voice.google.com") || fromLower.includes("google.com/voice") || fromLower.includes("google") && fromLower.includes("voice");
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(/&nbsp;/gi, " ").replace(/&amp;/gi, "&").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&#39;/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: homedir11 } = await import("os");
5913
- const backupDir = (0, import_node_path4.join)(homedir11(), ".agenticmail");
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: writeFileSync8, mkdirSync: mkdirSync9 } = await import("fs");
5916
- mkdirSync9(backupDir, { recursive: true });
5917
- writeFileSync8(backupPath, JSON.stringify({
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((resolve, reject) => {
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
- resolve();
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 import_node_fs7 = require("fs");
6754
- var import_node_path8 = require("path");
6755
- var import_node_os7 = require("os");
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 import_node_fs4 = require("fs");
6760
- var import_node_path5 = require("path");
6761
- var import_node_os4 = require("os");
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, import_node_path5.join)((0, import_node_os4.homedir)(), ".agenticmail", "bin", "cloudflared");
6812
- if ((0, import_node_fs4.existsSync)(binPath)) {
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 import_node_fs5 = require("fs");
7078
+ var import_node_fs6 = require("fs");
6835
7079
  var import_promises2 = require("fs/promises");
6836
- var import_node_path6 = require("path");
6837
- var import_node_os5 = require("os");
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((resolve, reject) => {
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
- resolve({ exitCode: code ?? 1, fullOutput });
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((resolve, reject) => {
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) => resolve({ exitCode: code ?? 1, fullOutput }));
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, import_node_os5.platform)();
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, import_node_path6.join)((0, import_node_os5.homedir)(), ".docker", "cli-plugins");
7014
- const pluginPath = (0, import_node_path6.join)(pluginDir, "docker-compose");
7015
- if ((0, import_node_fs5.existsSync)(pluginPath)) return;
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, import_node_fs5.mkdirSync)(pluginDir, { recursive: true });
7261
+ (0, import_node_fs6.mkdirSync)(pluginDir, { recursive: true });
7018
7262
  } catch {
7019
7263
  }
7020
7264
  try {
7021
- (0, import_node_fs5.symlinkSync)(composeBin, pluginPath);
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, import_node_path6.join)((0, import_node_os5.homedir)(), ".agenticmail", "tmp");
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, import_node_path6.join)(tmpDir, "install-docker.sh");
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, import_node_path6.join)(programFiles, "Docker", "Docker", "Docker Desktop.exe");
7151
- if ((0, import_node_fs5.existsSync)(dockerExe)) {
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, import_node_path6.join)(programFiles, "Docker", "Docker", "Docker Desktop.exe");
7202
- if ((0, import_node_fs5.existsSync)(dockerExe)) {
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, import_node_fs5.existsSync)(composePath)) {
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, import_node_os5.platform)();
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, import_node_os5.platform)() === "darwin") this.linkComposePlugin();
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, import_node_os5.platform)();
7298
- const binDir = (0, import_node_path6.join)((0, import_node_os5.homedir)(), ".agenticmail", "bin");
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, import_node_path6.join)(binDir, binName);
7301
- if ((0, import_node_fs5.existsSync)(binPath)) {
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, import_node_fs5.existsSync)(sysPath)) return sysPath;
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, import_node_os5.arch)();
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, import_node_path6.join)(binDir, "cloudflared.tgz");
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, import_node_fs5.existsSync)(binPath)) {
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 import_node_fs6 = require("fs");
7366
- var import_node_path7 = require("path");
7367
- var import_node_os6 = require("os");
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, import_node_os6.platform)();
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, import_node_path7.join)((0, import_node_os6.homedir)(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
7623
+ return (0, import_node_path9.join)((0, import_node_os7.homedir)(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
7380
7624
  } else {
7381
- return (0, import_node_path7.join)((0, import_node_os6.homedir)(), ".config", "systemd", "user", SYSTEMD_UNIT);
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, import_node_fs6.existsSync)(resolved)) return resolved;
7659
+ if ((0, import_node_fs7.existsSync)(resolved)) return resolved;
7416
7660
  } catch {
7417
7661
  }
7418
7662
  const parentPackages = [
7419
- (0, import_node_path7.join)("@agenticmail", "cli"),
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, import_node_path7.join)((0, import_node_os6.homedir)(), "node_modules")
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, import_node_path7.join)(prefix, "lib", "node_modules"));
7431
- baseDirs.push((0, import_node_path7.join)(prefix, "node_modules"));
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, import_node_path7.join)(base, "@agenticmail", "api", "dist", "index.js");
7438
- if ((0, import_node_fs6.existsSync)(sibling)) return sibling;
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, import_node_path7.join)(base, parent, "node_modules", "@agenticmail", "api", "dist", "index.js");
7441
- if ((0, import_node_fs6.existsSync)(nested)) return nested;
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, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail");
7445
- const entryCache = (0, import_node_path7.join)(dataDir, "api-entry.path");
7446
- if ((0, import_node_fs6.existsSync)(entryCache)) {
7447
- const cached = (0, import_node_fs6.readFileSync)(entryCache, "utf-8").trim();
7448
- if (cached && (0, import_node_fs6.existsSync)(cached)) return cached;
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, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail");
7457
- if (!(0, import_node_fs6.existsSync)(dataDir)) (0, import_node_fs6.mkdirSync)(dataDir, { recursive: true });
7458
- (0, import_node_fs6.writeFileSync)((0, import_node_path7.join)(dataDir, "api-entry.path"), entryPath);
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, import_node_fs6.existsSync)(pkgJson)) {
7472
- const pkg = JSON.parse((0, import_node_fs6.readFileSync)(pkgJson, "utf-8"));
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, import_node_path7.join)(apiEntry, "..", "..", "package.json");
7480
- if ((0, import_node_fs6.existsSync)(apiPkg)) {
7481
- const pkg = JSON.parse((0, import_node_fs6.readFileSync)(apiPkg, "utf-8"));
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, import_node_path7.join)((0, import_node_os6.homedir)(), "node_modules", "@agenticmail", "cli", "package.json"),
7488
- (0, import_node_path7.join)((0, import_node_os6.homedir)(), "node_modules", "agenticmail", "package.json"),
7489
- (0, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail", "package-version.json")
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, import_node_path7.join)(prefix, "lib", "node_modules", "@agenticmail", "cli", "package.json"));
7494
- candidates.push((0, import_node_path7.join)(prefix, "lib", "node_modules", "agenticmail", "package.json"));
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, import_node_fs6.existsSync)(p)) {
7500
- const pkg = JSON.parse((0, import_node_fs6.readFileSync)(p, "utf-8"));
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, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail", "bin", "start-server.sh");
7514
- const scriptDir = (0, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail", "bin");
7515
- if (!(0, import_node_fs6.existsSync)(scriptDir)) (0, import_node_fs6.mkdirSync)(scriptDir, { recursive: true });
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, import_node_fs6.writeFileSync)(scriptPath, script, { mode: 493 });
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, import_node_fs6.readFileSync)(configPath, "utf-8"));
7580
- const logDir = (0, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail", "logs");
7581
- if (!(0, import_node_fs6.existsSync)(logDir)) (0, import_node_fs6.mkdirSync)(logDir, { recursive: true });
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, import_node_os6.homedir)()}</string>
7846
+ <string>${(0, import_node_os7.homedir)()}</string>
7603
7847
  <key>AGENTICMAIL_DATA_DIR</key>
7604
- <string>${config.dataDir || (0, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail")}</string>
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, import_node_fs6.readFileSync)(configPath, "utf-8"));
7658
- const dataDir = config.dataDir || (0, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail");
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, import_node_os6.homedir)()}
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, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail", "config.json");
7689
- if (!(0, import_node_fs6.existsSync)(configPath)) {
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, import_node_path7.join)((0, import_node_os6.homedir)(), "Library", "LaunchAgents");
7702
- if (!(0, import_node_fs6.existsSync)(dir)) (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
7703
- if ((0, import_node_fs6.existsSync)(servicePath)) {
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, import_node_fs6.writeFileSync)(servicePath, plist);
7711
- (0, import_node_fs6.chmodSync)(servicePath, 384);
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, import_node_path7.join)((0, import_node_os6.homedir)(), ".config", "systemd", "user");
7720
- if (!(0, import_node_fs6.existsSync)(dir)) (0, import_node_fs6.mkdirSync)(dir, { recursive: true });
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, import_node_fs6.writeFileSync)(servicePath, unit);
7723
- (0, import_node_fs6.chmodSync)(servicePath, 384);
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, import_node_fs6.existsSync)(servicePath)) {
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, import_node_fs6.unlinkSync)(servicePath);
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, import_node_fs6.unlinkSync)(servicePath);
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, import_node_fs6.existsSync)(servicePath);
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, import_node_fs6.existsSync)(servicePath)) return null;
8082
+ if (!(0, import_node_fs7.existsSync)(servicePath)) return null;
7839
8083
  let serviceContent = "";
7840
8084
  try {
7841
- serviceContent = (0, import_node_fs6.readFileSync)(servicePath, "utf-8");
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, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail", "bin", "start-server.sh");
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, import_node_fs6.existsSync)(startScript)) {
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, import_node_fs6.readFileSync)(startScript, "utf-8");
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, import_node_fs6.existsSync)(referenced)) {
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, import_node_fs6.existsSync)(stale)) {
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, import_node_path7.join)((0, import_node_os6.homedir)(), ".agenticmail", "api-entry.path");
7879
- if ((0, import_node_fs6.existsSync)(entryCache)) {
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, import_node_fs6.readFileSync)(entryCache, "utf-8").trim();
7882
- if (cached && !(0, import_node_fs6.existsSync)(cached)) {
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, import_node_path8.join)((0, import_node_os7.homedir)(), ".agenticmail", "docker-compose.yml");
7934
- if ((0, import_node_fs7.existsSync)(standalonePath)) return standalonePath;
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, import_node_path8.join)(cwd, "..")];
8180
+ const candidates = [cwd, (0, import_node_path10.join)(cwd, "..")];
7937
8181
  for (const dir of candidates) {
7938
- const p = (0, import_node_path8.join)(dir, "docker-compose.yml");
7939
- if ((0, import_node_fs7.existsSync)(p)) return p;
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, import_node_path8.join)((0, import_node_os7.homedir)(), ".agenticmail");
7950
- const configPath = (0, import_node_path8.join)(dataDir, "config.json");
7951
- const envPath = (0, import_node_path8.join)(dataDir, ".env");
7952
- if ((0, import_node_fs7.existsSync)(configPath)) {
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, import_node_fs7.readFileSync)(configPath, "utf-8"));
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, import_node_fs7.existsSync)(dataDir)) {
7961
- (0, import_node_fs7.mkdirSync)(dataDir, { recursive: true });
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, import_node_fs7.writeFileSync)(configPath, JSON.stringify(config, null, 2));
7978
- (0, import_node_fs7.chmodSync)(configPath, 384);
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, import_node_fs7.writeFileSync)(envPath, envContent);
7994
- (0, import_node_fs7.chmodSync)(envPath, 384);
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, import_node_path8.join)((0, import_node_os7.homedir)(), ".agenticmail");
8004
- if (!(0, import_node_fs7.existsSync)(dataDir)) {
8005
- (0, import_node_fs7.mkdirSync)(dataDir, { recursive: true });
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, import_node_path8.join)(dataDir, "docker-compose.yml");
8009
- (0, import_node_fs7.writeFileSync)(composePath, `services:
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, import_node_fs7.chmodSync)(composePath, 384);
8039
- const tomlPath = (0, import_node_path8.join)(dataDir, "stalwart.toml");
8040
- (0, import_node_fs7.writeFileSync)(tomlPath, `# Stalwart Mail Server \u2014 AgenticMail Configuration
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, import_node_fs7.chmodSync)(tomlPath, 384);
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, import_node_path8.join)((0, import_node_os7.homedir)(), ".agenticmail", "config.json");
8097
- return (0, import_node_fs7.existsSync)(configPath);
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 m = addr.match(/<([^>]+)>/);
8125
- const raw = m ? m[1] : addr;
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 import_node_fs8 = require("fs");
8135
- var import_node_os8 = require("os");
8136
- var import_node_path9 = require("path");
8137
- var CACHE_DIR_DEFAULT = (0, import_node_path9.join)((0, import_node_os8.homedir)(), ".agenticmail", "thread-cache");
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, import_node_fs8.mkdirSync)(this.dir, { recursive: true });
8395
+ (0, import_node_fs9.mkdirSync)(this.dir, { recursive: true });
8151
8396
  } catch {
8152
8397
  }
8153
8398
  }
8154
8399
  pathFor(threadId) {
8155
- return (0, import_node_path9.join)(this.dir, `${threadId}.json`);
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, import_node_fs8.existsSync)(p)) return null;
8404
+ if (!(0, import_node_fs9.existsSync)(p)) return null;
8160
8405
  try {
8161
- const raw = (0, import_node_fs8.readFileSync)(p, "utf-8");
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, import_node_fs8.rmSync)(p, { force: true });
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, import_node_fs8.rmSync)(this.pathFor(threadId), { force: true });
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, import_node_fs8.writeFileSync)(tmp, JSON.stringify(entry), "utf-8");
8227
- (0, import_node_fs8.renameSync)(tmp, p);
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, import_node_fs8.readdirSync)(this.dir).filter((f) => f.endsWith(".json"));
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, import_node_path9.join)(this.dir, f);
8490
+ const p = (0, import_node_path11.join)(this.dir, f);
8246
8491
  try {
8247
- return { p, mtime: (0, import_node_fs8.statSync)(p).mtimeMs };
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, import_node_fs8.rmSync)(stats[i].p, { force: true });
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 import_node_fs9 = require("fs");
8276
- var import_node_os9 = require("os");
8277
- var import_node_path10 = require("path");
8278
- var MEMORY_DIR_DEFAULT = (0, import_node_path10.join)((0, import_node_os9.homedir)(), ".agenticmail", "agent-memory");
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, import_node_fs9.mkdirSync)(this.dir, { recursive: true });
8529
+ (0, import_node_fs10.mkdirSync)(this.dir, { recursive: true });
8285
8530
  } catch {
8286
8531
  }
8287
8532
  }
8288
8533
  dirFor(agentId) {
8289
- return (0, import_node_path10.join)(this.dir, sanitizeId(agentId));
8534
+ return (0, import_node_path12.join)(this.dir, sanitizeId(agentId));
8290
8535
  }
8291
8536
  pathFor(agentId, threadId) {
8292
- return (0, import_node_path10.join)(this.dirFor(agentId), `${sanitizeId(threadId)}.md`);
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, import_node_fs9.existsSync)(p)) return null;
8541
+ if (!(0, import_node_fs10.existsSync)(p)) return null;
8297
8542
  try {
8298
- const raw = (0, import_node_fs9.readFileSync)(p, "utf-8");
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, import_node_fs9.mkdirSync)(agentDir, { recursive: true });
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, import_node_fs9.writeFileSync)(tmp, body, "utf-8");
8315
- (0, import_node_fs9.renameSync)(tmp, p);
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, import_node_fs9.rmSync)(this.pathFor(agentId, threadId), { force: true });
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
  });