@agenticmail/core 0.3.3 → 0.3.4

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
@@ -230,6 +230,7 @@ declare class StalwartAdmin {
230
230
  * Note: stalwart-cli may return a 500 error even when the operation succeeds.
231
231
  * We verify by listing the config afterwards.
232
232
  */
233
+ private cliArgs;
233
234
  updateSetting(key: string, value: string): Promise<void>;
234
235
  /**
235
236
  * Set the server hostname used in SMTP EHLO greetings.
package/dist/index.js CHANGED
@@ -813,24 +813,31 @@ var StalwartAdmin = class {
813
813
  * Note: stalwart-cli may return a 500 error even when the operation succeeds.
814
814
  * We verify by listing the config afterwards.
815
815
  */
816
- async updateSetting(key, value) {
817
- const { execSync: execSync3 } = await import("child_process");
816
+ cliArgs() {
818
817
  const creds = `${this.options.adminUser}:${this.options.adminPassword}`;
818
+ return ["exec", "agenticmail-stalwart", "stalwart-cli", "-u", "http://localhost:8080", "-c", creds];
819
+ }
820
+ async updateSetting(key, value) {
821
+ const { execFileSync: execFileSync3 } = await import("child_process");
822
+ const cli = this.cliArgs();
819
823
  try {
820
- execSync3(
821
- `docker exec agenticmail-stalwart stalwart-cli -u http://localhost:8080 -c ${creds} server delete-config ${key}`,
824
+ execFileSync3(
825
+ "docker",
826
+ [...cli, "server", "delete-config", key],
822
827
  { timeout: 15e3, stdio: ["ignore", "pipe", "pipe"] }
823
828
  );
824
829
  } catch {
825
830
  }
826
831
  try {
827
- execSync3(
828
- `docker exec agenticmail-stalwart stalwart-cli -u http://localhost:8080 -c ${creds} server add-config ${key} ${value}`,
832
+ execFileSync3(
833
+ "docker",
834
+ [...cli, "server", "add-config", key, value],
829
835
  { timeout: 15e3, stdio: ["ignore", "pipe", "pipe"] }
830
836
  );
831
837
  } catch {
832
- const output = execSync3(
833
- `docker exec agenticmail-stalwart stalwart-cli -u http://localhost:8080 -c ${creds} server list-config ${key}`,
838
+ const output = execFileSync3(
839
+ "docker",
840
+ [...cli, "server", "list-config", key],
834
841
  { timeout: 15e3, stdio: ["ignore", "pipe", "pipe"] }
835
842
  ).toString();
836
843
  if (!output.includes(value)) {
@@ -875,16 +882,15 @@ var StalwartAdmin = class {
875
882
  * Returns the public key (base64, no headers) for DNS TXT record.
876
883
  */
877
884
  async createDkimSignature(domain, selector = "agenticmail") {
878
- const { execSync: execSync3 } = await import("child_process");
885
+ const { execFileSync: execFileSync3 } = await import("child_process");
879
886
  const signatureId = `agenticmail-${domain.replace(/\./g, "-")}`;
880
- const creds = `${this.options.adminUser}:${this.options.adminPassword}`;
881
- const cli = `docker exec agenticmail-stalwart stalwart-cli -u http://localhost:8080 -c ${creds}`;
887
+ const cli = this.cliArgs();
882
888
  const existing = await this.getSettings(`signature.${signatureId}`);
883
889
  if (existing["private-key"] && existing["domain"]) {
884
890
  console.log(`[DKIM] Reusing existing signature "${signatureId}" from Stalwart DB`);
885
891
  } else {
886
892
  try {
887
- execSync3(`${cli} server delete-config "signature.${signatureId}"`, {
893
+ execFileSync3("docker", [...cli, "server", "delete-config", `signature.${signatureId}`], {
888
894
  timeout: 1e4,
889
895
  stdio: ["ignore", "pipe", "pipe"]
890
896
  });
@@ -892,7 +898,7 @@ var StalwartAdmin = class {
892
898
  }
893
899
  console.log(`[DKIM] Creating RSA signature for ${domain} via stalwart-cli`);
894
900
  try {
895
- execSync3(`${cli} dkim create rsa ${domain} ${signatureId} ${selector}`, {
901
+ execFileSync3("docker", [...cli, "dkim", "create", "rsa", domain, signatureId, selector], {
896
902
  timeout: 15e3,
897
903
  stdio: ["ignore", "pipe", "pipe"]
898
904
  });
@@ -904,12 +910,12 @@ var StalwartAdmin = class {
904
910
  if (!Object.keys(signRule).length) {
905
911
  console.log(`[DKIM] Configuring DKIM signing rule`);
906
912
  const rules = [
907
- [`auth.dkim.sign.0000.if`, `listener != 'smtp'`],
908
- [`auth.dkim.sign.0000.then`, `['${signatureId}']`],
909
- [`auth.dkim.sign.0001.else`, `false`]
913
+ ["auth.dkim.sign.0000.if", `listener != 'smtp'`],
914
+ ["auth.dkim.sign.0000.then", `['${signatureId}']`],
915
+ ["auth.dkim.sign.0001.else", "false"]
910
916
  ];
911
917
  for (const [key, value] of rules) {
912
- execSync3(`${cli} server add-config "${key}" "${value}"`, {
918
+ execFileSync3("docker", [...cli, "server", "add-config", key, value], {
913
919
  timeout: 1e4,
914
920
  stdio: ["ignore", "pipe", "pipe"]
915
921
  });
@@ -917,7 +923,7 @@ var StalwartAdmin = class {
917
923
  }
918
924
  let publicKey;
919
925
  try {
920
- const output = execSync3(`${cli} dkim get-public-key ${signatureId}`, {
926
+ const output = execFileSync3("docker", [...cli, "dkim", "get-public-key", signatureId], {
921
927
  timeout: 1e4,
922
928
  stdio: ["ignore", "pipe", "pipe"]
923
929
  }).toString();
@@ -928,7 +934,7 @@ var StalwartAdmin = class {
928
934
  throw new Error(`Failed to get DKIM public key: ${err.message}`);
929
935
  }
930
936
  try {
931
- execSync3(`${cli} server reload-config`, {
937
+ execFileSync3("docker", [...cli, "server", "reload-config"], {
932
938
  timeout: 1e4,
933
939
  stdio: ["ignore", "pipe", "pipe"]
934
940
  });
@@ -941,9 +947,9 @@ var StalwartAdmin = class {
941
947
  * Restart the Stalwart Docker container and wait for it to be ready.
942
948
  */
943
949
  async restartContainer() {
944
- const { execSync: execSync3 } = await import("child_process");
950
+ const { execFileSync: execFileSync3 } = await import("child_process");
945
951
  try {
946
- execSync3("docker restart agenticmail-stalwart", { timeout: 3e4, stdio: ["ignore", "pipe", "pipe"] });
952
+ execFileSync3("docker", ["restart", "agenticmail-stalwart"], { timeout: 3e4, stdio: ["ignore", "pipe", "pipe"] });
947
953
  for (let i = 0; i < 15; i++) {
948
954
  try {
949
955
  const res = await fetch(`${this.baseUrl}/health`, { signal: AbortSignal.timeout(2e3) });
@@ -3813,8 +3819,8 @@ var CloudflareClient = class {
3813
3819
  let available = false;
3814
3820
  if (result.supported_tld && !hasRegistration) {
3815
3821
  try {
3816
- const { execSync: execSync3 } = await import("child_process");
3817
- const whoisOutput = execSync3(`whois ${domain}`, { timeout: 1e4, stdio: ["ignore", "pipe", "pipe"] }).toString().toLowerCase();
3822
+ const { execFileSync: execFileSync3 } = await import("child_process");
3823
+ const whoisOutput = execFileSync3("whois", [domain], { timeout: 1e4, stdio: ["ignore", "pipe", "pipe"] }).toString().toLowerCase();
3818
3824
  available = whoisOutput.includes("domain not found") || whoisOutput.includes("no match") || whoisOutput.includes("not found") || whoisOutput.includes("no data found") || whoisOutput.includes("status: free") || whoisOutput.includes("no entries found");
3819
3825
  } catch {
3820
3826
  available = false;
@@ -4278,8 +4284,8 @@ var TunnelManager = class {
4278
4284
  return this.binPath;
4279
4285
  }
4280
4286
  try {
4281
- const { execSync: execSync3 } = await import("child_process");
4282
- const sysPath = execSync3("which cloudflared", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
4287
+ const { execFileSync: execFileSync3 } = await import("child_process");
4288
+ const sysPath = execFileSync3("which", ["cloudflared"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
4283
4289
  if (sysPath && existsSync2(sysPath)) {
4284
4290
  this.binPath = sysPath;
4285
4291
  return sysPath;
@@ -5351,7 +5357,7 @@ import { join as join7 } from "path";
5351
5357
  import { homedir as homedir6 } from "os";
5352
5358
 
5353
5359
  // src/setup/deps.ts
5354
- import { execSync } from "child_process";
5360
+ import { execFileSync } from "child_process";
5355
5361
  import { existsSync as existsSync3 } from "fs";
5356
5362
  import { join as join5 } from "path";
5357
5363
  import { homedir as homedir4 } from "os";
@@ -5365,7 +5371,7 @@ var DependencyChecker = class {
5365
5371
  }
5366
5372
  async checkDocker() {
5367
5373
  try {
5368
- const output = execSync("docker --version", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
5374
+ const output = execFileSync("docker", ["--version"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
5369
5375
  const match = output.match(/Docker version ([\d.]+)/);
5370
5376
  return {
5371
5377
  name: "docker",
@@ -5383,8 +5389,9 @@ var DependencyChecker = class {
5383
5389
  }
5384
5390
  async checkStalwart() {
5385
5391
  try {
5386
- const output = execSync(
5387
- 'docker ps --filter name=agenticmail-stalwart --format "{{.Status}}"',
5392
+ const output = execFileSync(
5393
+ "docker",
5394
+ ["ps", "--filter", "name=agenticmail-stalwart", "--format", "{{.Status}}"],
5388
5395
  { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }
5389
5396
  ).toString().trim();
5390
5397
  const running = output.length > 0 && output.toLowerCase().includes("up");
@@ -5407,7 +5414,7 @@ var DependencyChecker = class {
5407
5414
  if (existsSync3(binPath)) {
5408
5415
  let version;
5409
5416
  try {
5410
- const output = execSync(`"${binPath}" --version`, { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
5417
+ const output = execFileSync(binPath, ["--version"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
5411
5418
  const match = output.match(/cloudflared version ([\d.]+)/);
5412
5419
  version = match?.[1];
5413
5420
  } catch {
@@ -5415,7 +5422,7 @@ var DependencyChecker = class {
5415
5422
  return { name: "cloudflared", installed: true, version, description: "Cloudflare Tunnel for custom domain email" };
5416
5423
  }
5417
5424
  try {
5418
- const output = execSync("cloudflared --version", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
5425
+ const output = execFileSync("cloudflared", ["--version"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
5419
5426
  const match = output.match(/cloudflared version ([\d.]+)/);
5420
5427
  return { name: "cloudflared", installed: true, version: match?.[1], description: "Cloudflare Tunnel for custom domain email" };
5421
5428
  } catch {
@@ -5425,9 +5432,9 @@ var DependencyChecker = class {
5425
5432
  };
5426
5433
 
5427
5434
  // src/setup/installer.ts
5428
- import { execSync as execSync2 } from "child_process";
5435
+ import { execFileSync as execFileSync2, execSync } from "child_process";
5429
5436
  import { existsSync as existsSync4 } from "fs";
5430
- import { writeFile, rename, chmod as chmod2, mkdir as mkdir2 } from "fs/promises";
5437
+ import { writeFile, rename, chmod as chmod2, mkdir as mkdir2, unlink } from "fs/promises";
5431
5438
  import { join as join6 } from "path";
5432
5439
  import { homedir as homedir5, platform as platform2, arch as arch2 } from "os";
5433
5440
  var DependencyInstaller = class {
@@ -5444,13 +5451,13 @@ var DependencyInstaller = class {
5444
5451
  async installDocker() {
5445
5452
  let cliInstalled = false;
5446
5453
  try {
5447
- execSync2("docker --version", { timeout: 5e3, stdio: "ignore" });
5454
+ execFileSync2("docker", ["--version"], { timeout: 5e3, stdio: "ignore" });
5448
5455
  cliInstalled = true;
5449
5456
  } catch {
5450
5457
  }
5451
5458
  if (cliInstalled) {
5452
5459
  try {
5453
- execSync2("docker info", { timeout: 1e4, stdio: "ignore" });
5460
+ execFileSync2("docker", ["info"], { timeout: 1e4, stdio: "ignore" });
5454
5461
  return;
5455
5462
  } catch {
5456
5463
  this.onProgress("Docker found but not running \u2014 starting it now...");
@@ -5463,18 +5470,18 @@ var DependencyInstaller = class {
5463
5470
  if (os === "darwin") {
5464
5471
  this.onProgress("Installing Docker via Homebrew...");
5465
5472
  try {
5466
- execSync2("brew --version", { timeout: 5e3, stdio: "ignore" });
5473
+ execFileSync2("brew", ["--version"], { timeout: 5e3, stdio: "ignore" });
5467
5474
  } catch {
5468
5475
  throw new Error("Homebrew is required to install Docker on macOS. Install it from https://brew.sh then try again.");
5469
5476
  }
5470
- execSync2("brew install --cask docker", { timeout: 3e5, stdio: "inherit" });
5477
+ execFileSync2("brew", ["install", "--cask", "docker"], { timeout: 3e5, stdio: "inherit" });
5471
5478
  this.onProgress("Docker installed. Starting Docker Desktop...");
5472
5479
  this.startDockerDaemon();
5473
5480
  await this.waitForDocker();
5474
5481
  } else if (os === "linux") {
5475
5482
  this.onProgress("Installing Docker...");
5476
5483
  try {
5477
- execSync2("curl -fsSL https://get.docker.com | sh", { timeout: 3e5, stdio: "inherit" });
5484
+ execSync("curl -fsSL https://get.docker.com | sh", { timeout: 3e5, stdio: "inherit" });
5478
5485
  } catch {
5479
5486
  throw new Error("Failed to install Docker. Install it manually: https://docs.docker.com/get-docker/");
5480
5487
  }
@@ -5492,12 +5499,12 @@ var DependencyInstaller = class {
5492
5499
  const os = platform2();
5493
5500
  if (os === "darwin") {
5494
5501
  try {
5495
- execSync2("open -a Docker", { timeout: 1e4, stdio: "ignore" });
5502
+ execFileSync2("open", ["-a", "Docker"], { timeout: 1e4, stdio: "ignore" });
5496
5503
  } catch {
5497
5504
  }
5498
5505
  } else if (os === "linux") {
5499
5506
  try {
5500
- execSync2("sudo systemctl start docker", { timeout: 15e3, stdio: "ignore" });
5507
+ execFileSync2("sudo", ["systemctl", "start", "docker"], { timeout: 15e3, stdio: "ignore" });
5501
5508
  } catch {
5502
5509
  }
5503
5510
  }
@@ -5513,7 +5520,7 @@ var DependencyInstaller = class {
5513
5520
  let attempts = 0;
5514
5521
  while (Date.now() - start < maxWait) {
5515
5522
  try {
5516
- execSync2("docker info", { timeout: 5e3, stdio: "ignore" });
5523
+ execFileSync2("docker", ["info"], { timeout: 5e3, stdio: "ignore" });
5517
5524
  return;
5518
5525
  } catch {
5519
5526
  }
@@ -5536,14 +5543,14 @@ var DependencyInstaller = class {
5536
5543
  throw new Error(`docker-compose.yml not found at: ${composePath}`);
5537
5544
  }
5538
5545
  try {
5539
- execSync2("docker info", { timeout: 1e4, stdio: "ignore" });
5546
+ execFileSync2("docker", ["info"], { timeout: 1e4, stdio: "ignore" });
5540
5547
  } catch {
5541
5548
  this.onProgress("Starting Docker...");
5542
5549
  this.startDockerDaemon();
5543
5550
  await this.waitForDocker();
5544
5551
  }
5545
5552
  this.onProgress("Starting Stalwart mail server...");
5546
- execSync2(`docker compose -f "${composePath}" up -d`, {
5553
+ execFileSync2("docker", ["compose", "-f", composePath, "up", "-d"], {
5547
5554
  timeout: 12e4,
5548
5555
  stdio: ["ignore", "pipe", "pipe"]
5549
5556
  });
@@ -5551,8 +5558,9 @@ var DependencyInstaller = class {
5551
5558
  const start = Date.now();
5552
5559
  while (Date.now() - start < maxWait) {
5553
5560
  try {
5554
- const output = execSync2(
5555
- 'docker ps --filter name=agenticmail-stalwart --format "{{.Status}}"',
5561
+ const output = execFileSync2(
5562
+ "docker",
5563
+ ["ps", "--filter", "name=agenticmail-stalwart", "--format", "{{.Status}}"],
5556
5564
  { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }
5557
5565
  ).toString().trim();
5558
5566
  if (output.toLowerCase().includes("up")) {
@@ -5575,7 +5583,7 @@ var DependencyInstaller = class {
5575
5583
  return binPath;
5576
5584
  }
5577
5585
  try {
5578
- const sysPath = execSync2("which cloudflared", { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
5586
+ const sysPath = execFileSync2("which", ["cloudflared"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
5579
5587
  if (sysPath && existsSync4(sysPath)) return sysPath;
5580
5588
  } catch {
5581
5589
  }
@@ -5599,11 +5607,11 @@ var DependencyInstaller = class {
5599
5607
  const tgzPath = join6(binDir, "cloudflared.tgz");
5600
5608
  await writeFile(tgzPath, buffer);
5601
5609
  try {
5602
- execSync2(`tar -xzf "${tgzPath}" -C "${binDir}" cloudflared`, { timeout: 15e3, stdio: "ignore" });
5610
+ execFileSync2("tar", ["-xzf", tgzPath, "-C", binDir, "cloudflared"], { timeout: 15e3, stdio: "ignore" });
5603
5611
  await chmod2(binPath, 493);
5604
5612
  } finally {
5605
5613
  try {
5606
- execSync2(`rm -f "${tgzPath}"`, { stdio: "ignore" });
5614
+ await unlink(tgzPath);
5607
5615
  } catch {
5608
5616
  }
5609
5617
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/core",
3
- "version": "0.3.3",
3
+ "version": "0.3.4",
4
4
  "description": "Core SDK for AgenticMail — programmatic email for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",