@agenticmail/core 0.5.32 → 0.5.34

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.d.ts CHANGED
@@ -1068,6 +1068,8 @@ interface GatewayManagerOptions {
1068
1068
  accountManager?: AccountManager;
1069
1069
  localSmtp?: LocalSmtpConfig;
1070
1070
  onInboundMail?: (agentName: string, mail: InboundEmail) => void | Promise<void>;
1071
+ /** Master key used to encrypt credentials at rest in SQLite. */
1072
+ encryptionKey?: string;
1071
1073
  }
1072
1074
  /**
1073
1075
  * GatewayManager orchestrates relay and domain modes for sending/receiving
@@ -1087,6 +1089,7 @@ declare class GatewayManager {
1087
1089
  private domainPurchaser;
1088
1090
  private smsManager;
1089
1091
  private smsPollers;
1092
+ private encryptionKey;
1090
1093
  constructor(options: GatewayManagerOptions);
1091
1094
  /**
1092
1095
  * Check if a message has already been delivered to an agent (deduplication).
package/dist/index.js CHANGED
@@ -703,6 +703,12 @@ function saveConfig(config) {
703
703
  }
704
704
 
705
705
  // src/stalwart/admin.ts
706
+ function escapeTomlString(value) {
707
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r").replace(/\t/g, "\\t");
708
+ }
709
+ function isValidDomain(domain) {
710
+ return /^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/.test(domain);
711
+ }
706
712
  var StalwartAdmin = class {
707
713
  constructor(options) {
708
714
  this.options = options;
@@ -850,13 +856,16 @@ var StalwartAdmin = class {
850
856
  * Critical for email deliverability — must match the sending domain.
851
857
  */
852
858
  async setHostname(domain) {
859
+ if (!isValidDomain(domain)) {
860
+ throw new Error(`Invalid domain format: "${domain}"`);
861
+ }
853
862
  const { readFileSync: readFileSync4, writeFileSync: writeFileSync5 } = await import("fs");
854
863
  const { homedir: homedir8 } = await import("os");
855
864
  const { join: join9 } = await import("path");
856
865
  const configPath = join9(homedir8(), ".agenticmail", "stalwart.toml");
857
866
  try {
858
867
  let config = readFileSync4(configPath, "utf-8");
859
- config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${domain}"`);
868
+ config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${escapeTomlString(domain)}"`);
860
869
  writeFileSync5(configPath, config);
861
870
  console.log(`[Stalwart] Updated hostname to "${domain}" in stalwart.toml`);
862
871
  } catch (err) {
@@ -983,21 +992,22 @@ var StalwartAdmin = class {
983
992
  let toml = readFileSync4(tomlPath, "utf-8");
984
993
  toml = toml.replace(/\n\[queue\.route\.gmail\][\s\S]*?(?=\n\[|$)/, "");
985
994
  toml = toml.replace(/\n\[queue\.strategy\][\s\S]*?(?=\n\[|$)/, "");
995
+ const safeRouteName = routeName.replace(/[^a-zA-Z0-9_-]/g, "");
986
996
  toml += `
987
997
 
988
- [queue.route.${routeName}]
998
+ [queue.route.${safeRouteName}]
989
999
  description = "Gmail SMTP relay for outbound delivery"
990
1000
  type = "relay"
991
- address = "${config.smtpHost}"
992
- port = ${config.smtpPort}
1001
+ address = "${escapeTomlString(config.smtpHost)}"
1002
+ port = ${Number(config.smtpPort) || 465}
993
1003
  protocol = "smtp"
994
1004
  tls.implicit = true
995
- auth.username = "${config.username}"
996
- auth.secret = "${config.password}"
1005
+ auth.username = "${escapeTomlString(config.username)}"
1006
+ auth.secret = "${escapeTomlString(config.password)}"
997
1007
 
998
1008
  [queue.strategy]
999
1009
  route = [ { if = "is_local_domain('', rcpt_domain)", then = "'local'" },
1000
- { else = "'${routeName}'" } ]
1010
+ { else = "'${safeRouteName}'" } ]
1001
1011
  `;
1002
1012
  writeFileSync5(tomlPath, toml, "utf-8");
1003
1013
  await this.restartContainer();
@@ -3193,6 +3203,7 @@ var DomainManager = class {
3193
3203
 
3194
3204
  // src/gateway/manager.ts
3195
3205
  import { join as join4 } from "path";
3206
+ import { createCipheriv, createDecipheriv, randomBytes as randomBytes2, createHash } from "crypto";
3196
3207
  import nodemailer3 from "nodemailer";
3197
3208
 
3198
3209
  // src/debug.ts
@@ -4833,12 +4844,31 @@ var SmsPoller = class {
4833
4844
  };
4834
4845
 
4835
4846
  // src/gateway/manager.ts
4847
+ function encryptSecret(plaintext, key) {
4848
+ const keyHash = createHash("sha256").update(key).digest();
4849
+ const iv = randomBytes2(12);
4850
+ const cipher = createCipheriv("aes-256-gcm", keyHash, iv);
4851
+ const encrypted = Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
4852
+ const authTag = cipher.getAuthTag();
4853
+ return `enc:${iv.toString("hex")}:${authTag.toString("hex")}:${encrypted.toString("hex")}`;
4854
+ }
4855
+ function decryptSecret(value, key) {
4856
+ if (!value.startsWith("enc:")) return value;
4857
+ const parts = value.split(":");
4858
+ if (parts.length !== 4) return value;
4859
+ const [, ivHex, authTagHex, ciphertextHex] = parts;
4860
+ const keyHash = createHash("sha256").update(key).digest();
4861
+ const decipher = createDecipheriv("aes-256-gcm", keyHash, Buffer.from(ivHex, "hex"));
4862
+ decipher.setAuthTag(Buffer.from(authTagHex, "hex"));
4863
+ return Buffer.concat([decipher.update(Buffer.from(ciphertextHex, "hex")), decipher.final()]).toString("utf8");
4864
+ }
4836
4865
  var GatewayManager = class {
4837
4866
  constructor(options) {
4838
4867
  this.options = options;
4839
4868
  this.db = options.db;
4840
4869
  this.stalwart = options.stalwart;
4841
4870
  this.accountManager = options.accountManager ?? null;
4871
+ this.encryptionKey = options.encryptionKey ?? process.env.AGENTICMAIL_MASTER_KEY ?? null;
4842
4872
  const inboundHandler = options.onInboundMail ?? (this.accountManager && options.localSmtp ? this.deliverInboundLocally.bind(this) : void 0);
4843
4873
  this.relay = new RelayGateway({
4844
4874
  onInboundMail: inboundHandler,
@@ -4865,6 +4895,7 @@ var GatewayManager = class {
4865
4895
  domainPurchaser = null;
4866
4896
  smsManager = null;
4867
4897
  smsPollers = /* @__PURE__ */ new Map();
4898
+ encryptionKey = null;
4868
4899
  /**
4869
4900
  * Check if a message has already been delivered to an agent (deduplication).
4870
4901
  */
@@ -5180,8 +5211,8 @@ var GatewayManager = class {
5180
5211
  const { homedir: homedir8 } = await import("os");
5181
5212
  const backupDir = join4(homedir8(), ".agenticmail");
5182
5213
  const backupPath = join4(backupDir, `dns-backup-${domain}-${Date.now()}.json`);
5183
- const { writeFileSync: writeFileSync5, mkdirSync: mkdirSync5 } = await import("fs");
5184
- mkdirSync5(backupDir, { recursive: true });
5214
+ const { writeFileSync: writeFileSync5, mkdirSync: mkdirSync6 } = await import("fs");
5215
+ mkdirSync6(backupDir, { recursive: true });
5185
5216
  writeFileSync5(backupPath, JSON.stringify({
5186
5217
  domain,
5187
5218
  zoneId: zone.id,
@@ -5646,6 +5677,44 @@ var GatewayManager = class {
5646
5677
  if (row) {
5647
5678
  try {
5648
5679
  const parsed = JSON.parse(row.config);
5680
+ if (this.encryptionKey) {
5681
+ if (parsed.relay?.password) {
5682
+ try {
5683
+ parsed.relay.password = decryptSecret(parsed.relay.password, this.encryptionKey);
5684
+ } catch {
5685
+ }
5686
+ }
5687
+ if (parsed.relay?.appPassword) {
5688
+ try {
5689
+ parsed.relay.appPassword = decryptSecret(parsed.relay.appPassword, this.encryptionKey);
5690
+ } catch {
5691
+ }
5692
+ }
5693
+ if (parsed.domain?.cloudflareApiToken) {
5694
+ try {
5695
+ parsed.domain.cloudflareApiToken = decryptSecret(parsed.domain.cloudflareApiToken, this.encryptionKey);
5696
+ } catch {
5697
+ }
5698
+ }
5699
+ if (parsed.domain?.tunnelToken) {
5700
+ try {
5701
+ parsed.domain.tunnelToken = decryptSecret(parsed.domain.tunnelToken, this.encryptionKey);
5702
+ } catch {
5703
+ }
5704
+ }
5705
+ if (parsed.domain?.inboundSecret) {
5706
+ try {
5707
+ parsed.domain.inboundSecret = decryptSecret(parsed.domain.inboundSecret, this.encryptionKey);
5708
+ } catch {
5709
+ }
5710
+ }
5711
+ if (parsed.domain?.outboundSecret) {
5712
+ try {
5713
+ parsed.domain.outboundSecret = decryptSecret(parsed.domain.outboundSecret, this.encryptionKey);
5714
+ } catch {
5715
+ }
5716
+ }
5717
+ }
5649
5718
  this.config = {
5650
5719
  mode: row.mode,
5651
5720
  ...parsed
@@ -5657,10 +5726,31 @@ var GatewayManager = class {
5657
5726
  }
5658
5727
  saveConfig() {
5659
5728
  const { mode, ...rest } = this.config;
5729
+ const toStore = JSON.parse(JSON.stringify(rest));
5730
+ if (this.encryptionKey) {
5731
+ if (toStore.relay?.password) {
5732
+ toStore.relay.password = encryptSecret(toStore.relay.password, this.encryptionKey);
5733
+ }
5734
+ if (toStore.relay?.appPassword) {
5735
+ toStore.relay.appPassword = encryptSecret(toStore.relay.appPassword, this.encryptionKey);
5736
+ }
5737
+ if (toStore.domain?.cloudflareApiToken) {
5738
+ toStore.domain.cloudflareApiToken = encryptSecret(toStore.domain.cloudflareApiToken, this.encryptionKey);
5739
+ }
5740
+ if (toStore.domain?.tunnelToken) {
5741
+ toStore.domain.tunnelToken = encryptSecret(toStore.domain.tunnelToken, this.encryptionKey);
5742
+ }
5743
+ if (toStore.domain?.inboundSecret) {
5744
+ toStore.domain.inboundSecret = encryptSecret(toStore.domain.inboundSecret, this.encryptionKey);
5745
+ }
5746
+ if (toStore.domain?.outboundSecret) {
5747
+ toStore.domain.outboundSecret = encryptSecret(toStore.domain.outboundSecret, this.encryptionKey);
5748
+ }
5749
+ }
5660
5750
  this.db.prepare(`
5661
5751
  INSERT OR REPLACE INTO gateway_config (id, mode, config)
5662
5752
  VALUES ('default', ?, ?)
5663
- `).run(mode, JSON.stringify(rest));
5753
+ `).run(mode, JSON.stringify(toStore));
5664
5754
  }
5665
5755
  saveLastSeenUid(uid) {
5666
5756
  this.db.prepare(`
@@ -5812,8 +5902,8 @@ var RELAY_PRESETS = {
5812
5902
  };
5813
5903
 
5814
5904
  // src/setup/index.ts
5815
- import { randomBytes as randomBytes2 } from "crypto";
5816
- import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, chmodSync } from "fs";
5905
+ import { randomBytes as randomBytes3 } from "crypto";
5906
+ import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync5, chmodSync as chmodSync2 } from "fs";
5817
5907
  import { join as join8 } from "path";
5818
5908
  import { homedir as homedir7 } from "os";
5819
5909
 
@@ -5894,7 +5984,7 @@ var DependencyChecker = class {
5894
5984
 
5895
5985
  // src/setup/installer.ts
5896
5986
  import { execFileSync as execFileSync2, execSync, spawn as spawnChild } from "child_process";
5897
- import { existsSync as existsSync4 } from "fs";
5987
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, symlinkSync } from "fs";
5898
5988
  import { writeFile, rename, chmod as chmod2, mkdir as mkdir2, unlink } from "fs/promises";
5899
5989
  import { join as join6 } from "path";
5900
5990
  import { homedir as homedir5, platform as platform2, arch as arch2 } from "os";
@@ -6077,11 +6167,11 @@ var DependencyInstaller = class {
6077
6167
  const pluginPath = join6(pluginDir, "docker-compose");
6078
6168
  if (existsSync4(pluginPath)) return;
6079
6169
  try {
6080
- execSync(`mkdir -p "${pluginDir}"`, { timeout: 5e3, stdio: "ignore" });
6170
+ mkdirSync3(pluginDir, { recursive: true });
6081
6171
  } catch {
6082
6172
  }
6083
6173
  try {
6084
- execSync(`ln -sf "${composeBin}" "${pluginPath}"`, { timeout: 5e3, stdio: "ignore" });
6174
+ symlinkSync(composeBin, pluginPath);
6085
6175
  } catch {
6086
6176
  }
6087
6177
  } catch {
@@ -6140,8 +6230,11 @@ var DependencyInstaller = class {
6140
6230
  return;
6141
6231
  }
6142
6232
  this.onProgress("__progress__:5:Installing Docker Engine...");
6233
+ const tmpDir = join6(homedir5(), ".agenticmail", "tmp");
6234
+ await mkdir2(tmpDir, { recursive: true });
6235
+ const scriptPath = join6(tmpDir, "install-docker.sh");
6143
6236
  const dlResult = await runShellWithRollingOutput(
6144
- "curl -fsSL https://get.docker.com -o /tmp/install-docker.sh && sudo sh /tmp/install-docker.sh",
6237
+ `curl -fsSL https://get.docker.com -o "${scriptPath}" && sudo sh "${scriptPath}"`,
6145
6238
  { timeout: 3e5 }
6146
6239
  );
6147
6240
  if (dlResult.exitCode !== 0) {
@@ -6150,15 +6243,15 @@ var DependencyInstaller = class {
6150
6243
  );
6151
6244
  }
6152
6245
  const user = process.env.USER || process.env.LOGNAME || "";
6153
- if (user && user !== "root") {
6246
+ if (user && user !== "root" && /^[a-zA-Z0-9._-]+$/.test(user)) {
6154
6247
  this.onProgress("__progress__:80:Adding user to docker group...");
6155
6248
  try {
6156
- execSync(`sudo usermod -aG docker ${user}`, { timeout: 1e4, stdio: "ignore" });
6249
+ execFileSync2("sudo", ["usermod", "-aG", "docker", user], { timeout: 1e4, stdio: "ignore" });
6157
6250
  } catch {
6158
6251
  }
6159
6252
  }
6160
6253
  try {
6161
- await unlink("/tmp/install-docker.sh");
6254
+ await unlink(scriptPath);
6162
6255
  } catch {
6163
6256
  }
6164
6257
  this.onProgress("__progress__:85:Starting Docker service...");
@@ -6422,7 +6515,7 @@ var DependencyInstaller = class {
6422
6515
 
6423
6516
  // src/setup/service.ts
6424
6517
  import { execFileSync as execFileSync3, execSync as execSync2 } from "child_process";
6425
- import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync3, unlinkSync, mkdirSync as mkdirSync3 } from "fs";
6518
+ import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync3, unlinkSync, mkdirSync as mkdirSync4, chmodSync } from "fs";
6426
6519
  import { join as join7 } from "path";
6427
6520
  import { homedir as homedir6, platform as platform3 } from "os";
6428
6521
  var PLIST_LABEL = "com.agenticmail.server";
@@ -6495,7 +6588,7 @@ var ServiceManager = class {
6495
6588
  */
6496
6589
  cacheApiEntryPath(entryPath) {
6497
6590
  const dataDir = join7(homedir6(), ".agenticmail");
6498
- if (!existsSync5(dataDir)) mkdirSync3(dataDir, { recursive: true });
6591
+ if (!existsSync5(dataDir)) mkdirSync4(dataDir, { recursive: true });
6499
6592
  writeFileSync3(join7(dataDir, "api-entry.path"), entryPath);
6500
6593
  }
6501
6594
  /**
@@ -6529,7 +6622,7 @@ var ServiceManager = class {
6529
6622
  generateStartScript(nodePath, apiEntry) {
6530
6623
  const scriptPath = join7(homedir6(), ".agenticmail", "bin", "start-server.sh");
6531
6624
  const scriptDir = join7(homedir6(), ".agenticmail", "bin");
6532
- if (!existsSync5(scriptDir)) mkdirSync3(scriptDir, { recursive: true });
6625
+ if (!existsSync5(scriptDir)) mkdirSync4(scriptDir, { recursive: true });
6533
6626
  const script = [
6534
6627
  "#!/bin/bash",
6535
6628
  "# AgenticMail auto-start script",
@@ -6595,7 +6688,7 @@ var ServiceManager = class {
6595
6688
  generatePlist(nodePath, apiEntry, configPath) {
6596
6689
  const config = JSON.parse(readFileSync2(configPath, "utf-8"));
6597
6690
  const logDir = join7(homedir6(), ".agenticmail", "logs");
6598
- if (!existsSync5(logDir)) mkdirSync3(logDir, { recursive: true });
6691
+ if (!existsSync5(logDir)) mkdirSync4(logDir, { recursive: true });
6599
6692
  const version = this.getVersion();
6600
6693
  const startScript = this.generateStartScript(nodePath, apiEntry);
6601
6694
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -6619,26 +6712,6 @@ var ServiceManager = class {
6619
6712
  <string>${homedir6()}</string>
6620
6713
  <key>AGENTICMAIL_DATA_DIR</key>
6621
6714
  <string>${config.dataDir || join7(homedir6(), ".agenticmail")}</string>
6622
- <key>AGENTICMAIL_MASTER_KEY</key>
6623
- <string>${config.masterKey}</string>
6624
- <key>STALWART_ADMIN_USER</key>
6625
- <string>${config.stalwart.adminUser}</string>
6626
- <key>STALWART_ADMIN_PASSWORD</key>
6627
- <string>${config.stalwart.adminPassword}</string>
6628
- <key>STALWART_URL</key>
6629
- <string>${config.stalwart.url}</string>
6630
- <key>AGENTICMAIL_API_PORT</key>
6631
- <string>${String(config.api.port)}</string>
6632
- <key>AGENTICMAIL_API_HOST</key>
6633
- <string>${config.api.host}</string>
6634
- <key>SMTP_HOST</key>
6635
- <string>${config.smtp.host}</string>
6636
- <key>SMTP_PORT</key>
6637
- <string>${String(config.smtp.port)}</string>
6638
- <key>IMAP_HOST</key>
6639
- <string>${config.imap.host}</string>
6640
- <key>IMAP_PORT</key>
6641
- <string>${String(config.imap.port)}</string>
6642
6715
  <key>PATH</key>
6643
6716
  <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
6644
6717
  <key>AGENTICMAIL_SERVICE_VERSION</key>
@@ -6711,16 +6784,6 @@ TimeoutStartSec=660
6711
6784
  LimitNOFILE=8192
6712
6785
  Environment=HOME=${homedir6()}
6713
6786
  Environment=AGENTICMAIL_DATA_DIR=${dataDir}
6714
- Environment=AGENTICMAIL_MASTER_KEY=${config.masterKey}
6715
- Environment=STALWART_ADMIN_USER=${config.stalwart.adminUser}
6716
- Environment=STALWART_ADMIN_PASSWORD=${config.stalwart.adminPassword}
6717
- Environment=STALWART_URL=${config.stalwart.url}
6718
- Environment=AGENTICMAIL_API_PORT=${config.api.port}
6719
- Environment=AGENTICMAIL_API_HOST=${config.api.host}
6720
- Environment=SMTP_HOST=${config.smtp.host}
6721
- Environment=SMTP_PORT=${config.smtp.port}
6722
- Environment=IMAP_HOST=${config.imap.host}
6723
- Environment=IMAP_PORT=${config.imap.port}
6724
6787
  Environment=PATH=/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin
6725
6788
  Environment=AGENTICMAIL_SERVICE_VERSION=${version}
6726
6789
 
@@ -6746,7 +6809,7 @@ WantedBy=default.target
6746
6809
  const servicePath = this.getServicePath();
6747
6810
  if (this.os === "darwin") {
6748
6811
  const dir = join7(homedir6(), "Library", "LaunchAgents");
6749
- if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
6812
+ if (!existsSync5(dir)) mkdirSync4(dir, { recursive: true });
6750
6813
  if (existsSync5(servicePath)) {
6751
6814
  try {
6752
6815
  execFileSync3("launchctl", ["unload", servicePath], { timeout: 1e4, stdio: "ignore" });
@@ -6755,6 +6818,7 @@ WantedBy=default.target
6755
6818
  }
6756
6819
  const plist = this.generatePlist(nodePath, apiEntry, configPath);
6757
6820
  writeFileSync3(servicePath, plist);
6821
+ chmodSync(servicePath, 384);
6758
6822
  try {
6759
6823
  execFileSync3("launchctl", ["load", servicePath], { timeout: 1e4, stdio: "ignore" });
6760
6824
  } catch (err) {
@@ -6763,9 +6827,10 @@ WantedBy=default.target
6763
6827
  return { installed: true, message: `Service installed at ${servicePath}` };
6764
6828
  } else if (this.os === "linux") {
6765
6829
  const dir = join7(homedir6(), ".config", "systemd", "user");
6766
- if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
6830
+ if (!existsSync5(dir)) mkdirSync4(dir, { recursive: true });
6767
6831
  const unit = this.generateSystemdUnit(nodePath, apiEntry, configPath);
6768
6832
  writeFileSync3(servicePath, unit);
6833
+ chmodSync(servicePath, 384);
6769
6834
  try {
6770
6835
  execFileSync3("systemctl", ["--user", "daemon-reload"], { timeout: 1e4, stdio: "ignore" });
6771
6836
  execFileSync3("systemctl", ["--user", "enable", SYSTEMD_UNIT], { timeout: 1e4, stdio: "ignore" });
@@ -6923,10 +6988,10 @@ var SetupManager = class {
6923
6988
  }
6924
6989
  }
6925
6990
  if (!existsSync6(dataDir)) {
6926
- mkdirSync4(dataDir, { recursive: true });
6991
+ mkdirSync5(dataDir, { recursive: true });
6927
6992
  }
6928
- const masterKey = `mk_${randomBytes2(24).toString("hex")}`;
6929
- const stalwartPassword = randomBytes2(16).toString("hex");
6993
+ const masterKey = `mk_${randomBytes3(24).toString("hex")}`;
6994
+ const stalwartPassword = randomBytes3(16).toString("hex");
6930
6995
  const config = {
6931
6996
  masterKey,
6932
6997
  stalwart: {
@@ -6940,7 +7005,7 @@ var SetupManager = class {
6940
7005
  dataDir
6941
7006
  };
6942
7007
  writeFileSync4(configPath, JSON.stringify(config, null, 2));
6943
- chmodSync(configPath, 384);
7008
+ chmodSync2(configPath, 384);
6944
7009
  const envContent = `# Auto-generated by agenticmail setup
6945
7010
  STALWART_ADMIN_USER=admin
6946
7011
  STALWART_ADMIN_PASSWORD=${stalwartPassword}
@@ -6956,7 +7021,7 @@ IMAP_HOST=localhost
6956
7021
  IMAP_PORT=143
6957
7022
  `;
6958
7023
  writeFileSync4(envPath, envContent);
6959
- chmodSync(envPath, 384);
7024
+ chmodSync2(envPath, 384);
6960
7025
  this.generateDockerFiles(config);
6961
7026
  return { configPath, envPath, config, isNew: true };
6962
7027
  }
@@ -6967,7 +7032,7 @@ IMAP_PORT=143
6967
7032
  generateDockerFiles(config) {
6968
7033
  const dataDir = config.dataDir || join8(homedir7(), ".agenticmail");
6969
7034
  if (!existsSync6(dataDir)) {
6970
- mkdirSync4(dataDir, { recursive: true });
7035
+ mkdirSync5(dataDir, { recursive: true });
6971
7036
  }
6972
7037
  const password = config.stalwart?.adminPassword || "changeme";
6973
7038
  const composePath = join8(dataDir, "docker-compose.yml");
@@ -6976,16 +7041,16 @@ IMAP_PORT=143
6976
7041
  image: stalwartlabs/stalwart:latest
6977
7042
  container_name: agenticmail-stalwart
6978
7043
  ports:
6979
- - "8080:8080" # HTTP Admin + JMAP
6980
- - "587:587" # SMTP Submission
6981
- - "143:143" # IMAP
6982
- - "25:25" # SMTP
7044
+ - "127.0.0.1:8080:8080" # HTTP Admin + JMAP (localhost only)
7045
+ - "127.0.0.1:587:587" # SMTP Submission (localhost only)
7046
+ - "127.0.0.1:143:143" # IMAP (localhost only)
7047
+ - "127.0.0.1:25:25" # SMTP (localhost only)
6983
7048
  volumes:
6984
7049
  - stalwart-data:/opt/stalwart
6985
7050
  - ./stalwart.toml:/opt/stalwart/etc/config.toml:ro
6986
7051
  restart: unless-stopped
6987
7052
  healthcheck:
6988
- test: ["CMD-SHELL", "stalwart-cli -u http://localhost:8080 -c admin:${password} server list-domains 2>/dev/null || true"]
7053
+ test: ["CMD-SHELL", "curl -sf http://localhost:8080/health || true"]
6989
7054
  interval: 10s
6990
7055
  timeout: 5s
6991
7056
  retries: 5
@@ -6993,6 +7058,7 @@ IMAP_PORT=143
6993
7058
  volumes:
6994
7059
  stalwart-data:
6995
7060
  `);
7061
+ chmodSync2(composePath, 384);
6996
7062
  const tomlPath = join8(dataDir, "stalwart.toml");
6997
7063
  writeFileSync4(tomlPath, `# Stalwart Mail Server \u2014 AgenticMail Configuration
6998
7064
 
@@ -7044,6 +7110,7 @@ enable = true
7044
7110
  user = "admin"
7045
7111
  secret = "${password}"
7046
7112
  `);
7113
+ chmodSync2(tomlPath, 384);
7047
7114
  }
7048
7115
  /**
7049
7116
  * Check if config has already been initialized.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agenticmail/core",
3
- "version": "0.5.32",
4
- "description": "Core SDK for AgenticMail \u2014 email, SMS, and phone number access for AI agents",
3
+ "version": "0.5.34",
4
+ "description": "Core SDK for AgenticMail email, SMS, and phone number access for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
@@ -28,7 +28,7 @@
28
28
  "better-sqlite3": "^11.8.0",
29
29
  "imapflow": "^1.0.170",
30
30
  "mailparser": "^3.7.2",
31
- "nodemailer": "^6.10.0",
31
+ "nodemailer": "^8.0.1",
32
32
  "uuid": "^11.1.0"
33
33
  },
34
34
  "devDependencies": {