@agenticmail/core 0.5.24 → 0.5.25

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.
Files changed (3) hide show
  1. package/dist/index.d.ts +19 -66
  2. package/dist/index.js +223 -516
  3. package/package.json +1 -1
package/dist/index.d.ts CHANGED
@@ -1412,97 +1412,50 @@ declare class DependencyChecker {
1412
1412
  type InstallProgress = (message: string) => void;
1413
1413
  /**
1414
1414
  * DependencyInstaller handles installing all external dependencies.
1415
- * Everything is auto-installed no optional deps, no manual steps.
1415
+ * Uses Colima + Docker CLI on macOS (no Docker Desktop GUI).
1416
+ * Uses Docker Engine directly on Linux.
1416
1417
  */
1417
1418
  declare class DependencyInstaller {
1418
1419
  private onProgress;
1419
- private _firstLaunchMode;
1420
1420
  constructor(onProgress?: InstallProgress);
1421
1421
  /**
1422
1422
  * Ensure Docker is installed AND the daemon is running.
1423
- * Fully automatic — installs, accepts license, starts daemon, waits for ready.
1424
- * Never asks the user to do anything manually.
1425
1423
  *
1426
1424
  * Flow:
1427
- * 1. docker CLI + daemon running? → done
1428
- * 2. docker CLI exists but daemon stopped? → start daemon, wait
1429
- * 3. Docker.app exists (in /Applications or Caskroom)? run installer --accept-license, start, wait
1430
- * 4. Nothing installed? → install via Homebrew or DMG download, then start, wait
1425
+ * 1. docker info works? → done
1426
+ * 2. docker CLI + colima exist but not running? → colima start
1427
+ * 3. Nothing installed? install via brew (colima + docker) or Docker Engine (Linux)
1431
1428
  */
1432
1429
  installDocker(): Promise<void>;
1433
1430
  /** Check if `docker info` succeeds (CLI + daemon both working). */
1434
1431
  private isDockerReady;
1435
- /** Check if `docker` CLI is in PATH (daemon may be stopped). */
1436
- private isDockerCliInstalled;
1437
1432
  /**
1438
- * Find Docker.app on macOS.
1439
- * Checks /Applications first, then Homebrew Caskroom.
1440
- * Validates symlinks aren't broken.
1433
+ * macOS: Install Docker via Colima (CLI-only, no GUI, no license dialogs).
1434
+ * Installs colima + docker + docker-compose via Homebrew, then starts Colima.
1441
1435
  */
1442
- private findDockerApp;
1443
- /**
1444
- * Check if Docker Desktop license has already been accepted.
1445
- * Returns true if we're confident the user has accepted before.
1446
- */
1447
- private isDockerLicenseAccepted;
1448
- /**
1449
- * Configure Docker Desktop to run headless after first-time setup.
1450
- * Call this AFTER Docker has been started and license accepted.
1451
- * Suppresses UI, dock icon, tips, analytics, and auto-updates.
1452
- */
1453
- private configureDockerHeadless;
1454
- /**
1455
- * Docker.app exists but CLI isn't available or daemon isn't running.
1456
- * Runs the built-in installer silently (--accept-license to link CLI tools),
1457
- * then starts Docker Desktop hidden (no GUI window).
1458
- */
1459
- /**
1460
- * Docker.app exists but daemon isn't running.
1461
- * Simple approach: if Docker was already running, great. If not, just
1462
- * tell the user to open Docker and accept the terms themselves.
1463
- * No GUI manipulation, no programmatic license tricks — just wait.
1464
- */
1465
- private setupExistingDockerApp;
1466
- /**
1467
- * Hide Docker Desktop completely — close all windows, hide from dock, make invisible.
1468
- * Called after starting Docker to ensure the user never sees any Docker UI.
1469
- */
1470
- private hideDockerWindow;
1471
- /**
1472
- * Full Docker Desktop install on macOS.
1473
- * Tries Homebrew first (cleaner), falls back to DMG download.
1474
- * After install, tells the user to open Docker and accept the terms.
1475
- * No GUI manipulation — the user handles Docker themselves.
1476
- */
1477
- /**
1478
- * Ensure /usr/local/bin and /usr/local/cli-plugins are writable by the
1479
- * current user. Brew needs to create symlinks there and will fail silently
1480
- * if they're root-owned (it tries sudo which fails non-interactively).
1481
- */
1482
- private fixLocalDirPermissions;
1436
+ private installDockerMac;
1483
1437
  /**
1484
- * After DMG install, manually link Docker CLI tools (brew does this
1485
- * automatically, but the DMG path doesn't).
1438
+ * Start Colima and wait for Docker to be ready.
1486
1439
  */
1487
- private linkDockerCli;
1488
- private installDockerMac;
1440
+ private startColima;
1489
1441
  /**
1490
1442
  * Install Docker Engine on Linux using Docker's official convenience script.
1491
1443
  * Also adds the current user to the docker group for rootless usage.
1492
1444
  */
1493
1445
  private installDockerLinux;
1494
1446
  /**
1495
- * Start the Docker daemon using multiple strategies.
1496
- * On macOS: tries Docker Desktop app via `open`, direct binary launch, etc.
1497
- * On Linux: tries systemctl, service, snap.
1447
+ * Wait for Docker daemon on Linux.
1448
+ */
1449
+ private waitForDockerLinux;
1450
+ /**
1451
+ * Windows: Install Docker via WSL2 + Docker Engine, or guide user to Docker Desktop.
1452
+ * Prefers WSL2 with Docker Engine (no GUI needed).
1498
1453
  */
1499
- private startDockerDaemon;
1454
+ private installDockerWindows;
1500
1455
  /**
1501
- * Wait for Docker daemon to be ready.
1502
- * Cycles through multiple start strategies automatically.
1503
- * Reports progress as a percentage (0-100).
1456
+ * Wait for Docker daemon on Windows.
1504
1457
  */
1505
- private waitForDocker;
1458
+ private waitForDockerWindows;
1506
1459
  /**
1507
1460
  * Start the Stalwart mail server Docker container.
1508
1461
  */
package/dist/index.js CHANGED
@@ -850,14 +850,14 @@ var StalwartAdmin = class {
850
850
  * Critical for email deliverability — must match the sending domain.
851
851
  */
852
852
  async setHostname(domain) {
853
- const { readFileSync: readFileSync5, writeFileSync: writeFileSync6 } = await import("fs");
853
+ const { readFileSync: readFileSync4, writeFileSync: writeFileSync5 } = await import("fs");
854
854
  const { homedir: homedir8 } = await import("os");
855
855
  const { join: join9 } = await import("path");
856
856
  const configPath = join9(homedir8(), ".agenticmail", "stalwart.toml");
857
857
  try {
858
- let config = readFileSync5(configPath, "utf-8");
858
+ let config = readFileSync4(configPath, "utf-8");
859
859
  config = config.replace(/^hostname\s*=\s*"[^"]*"/m, `hostname = "${domain}"`);
860
- writeFileSync6(configPath, config);
860
+ writeFileSync5(configPath, config);
861
861
  console.log(`[Stalwart] Updated hostname to "${domain}" in stalwart.toml`);
862
862
  } catch (err) {
863
863
  throw new Error(`Failed to set config server.hostname=${domain}`);
@@ -975,12 +975,12 @@ var StalwartAdmin = class {
975
975
  * This bypasses the need for a PTR record on the sending IP.
976
976
  */
977
977
  async configureOutboundRelay(config) {
978
- const { readFileSync: readFileSync5, writeFileSync: writeFileSync6 } = await import("fs");
978
+ const { readFileSync: readFileSync4, writeFileSync: writeFileSync5 } = await import("fs");
979
979
  const { homedir: homedir8 } = await import("os");
980
980
  const { join: join9 } = await import("path");
981
981
  const routeName = config.routeName ?? "gmail";
982
982
  const tomlPath = join9(homedir8(), ".agenticmail", "stalwart.toml");
983
- let toml = readFileSync5(tomlPath, "utf-8");
983
+ let toml = readFileSync4(tomlPath, "utf-8");
984
984
  toml = toml.replace(/\n\[queue\.route\.gmail\][\s\S]*?(?=\n\[|$)/, "");
985
985
  toml = toml.replace(/\n\[queue\.strategy\][\s\S]*?(?=\n\[|$)/, "");
986
986
  toml += `
@@ -999,7 +999,7 @@ auth.secret = "${config.password}"
999
999
  route = [ { if = "is_local_domain('', rcpt_domain)", then = "'local'" },
1000
1000
  { else = "'${routeName}'" } ]
1001
1001
  `;
1002
- writeFileSync6(tomlPath, toml, "utf-8");
1002
+ writeFileSync5(tomlPath, toml, "utf-8");
1003
1003
  await this.restartContainer();
1004
1004
  }
1005
1005
  };
@@ -5180,9 +5180,9 @@ var GatewayManager = class {
5180
5180
  const { homedir: homedir8 } = await import("os");
5181
5181
  const backupDir = join4(homedir8(), ".agenticmail");
5182
5182
  const backupPath = join4(backupDir, `dns-backup-${domain}-${Date.now()}.json`);
5183
- const { writeFileSync: writeFileSync6, mkdirSync: mkdirSync6 } = await import("fs");
5184
- mkdirSync6(backupDir, { recursive: true });
5185
- writeFileSync6(backupPath, JSON.stringify({
5183
+ const { writeFileSync: writeFileSync5, mkdirSync: mkdirSync5 } = await import("fs");
5184
+ mkdirSync5(backupDir, { recursive: true });
5185
+ writeFileSync5(backupPath, JSON.stringify({
5186
5186
  domain,
5187
5187
  zoneId: zone.id,
5188
5188
  backedUpAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -5813,7 +5813,7 @@ var RELAY_PRESETS = {
5813
5813
 
5814
5814
  // src/setup/index.ts
5815
5815
  import { randomBytes as randomBytes2 } from "crypto";
5816
- import { existsSync as existsSync6, readFileSync as readFileSync4, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
5816
+ import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4, chmodSync } from "fs";
5817
5817
  import { join as join8 } from "path";
5818
5818
  import { homedir as homedir7 } from "os";
5819
5819
 
@@ -5894,7 +5894,7 @@ var DependencyChecker = class {
5894
5894
 
5895
5895
  // src/setup/installer.ts
5896
5896
  import { execFileSync as execFileSync2, execSync, spawn as spawnChild } from "child_process";
5897
- import { existsSync as existsSync4, readdirSync, lstatSync, readlinkSync, writeFileSync as writeFileSync3, readFileSync as readFileSync2, mkdirSync as mkdirSync3 } from "fs";
5897
+ import { existsSync as existsSync4 } from "fs";
5898
5898
  import { writeFile, rename, chmod as chmod2, mkdir as mkdir2, unlink } from "fs/promises";
5899
5899
  import { join as join6 } from "path";
5900
5900
  import { homedir as homedir5, platform as platform2, arch as arch2 } from "os";
@@ -6034,26 +6034,17 @@ function runSilent(command, args, opts = {}) {
6034
6034
  child.on("error", reject);
6035
6035
  });
6036
6036
  }
6037
- function existsReal(p) {
6037
+ function hasHomebrew() {
6038
6038
  try {
6039
- const stat = lstatSync(p);
6040
- if (stat.isSymbolicLink()) {
6041
- try {
6042
- const target = readlinkSync(p);
6043
- const resolved = target.startsWith("/") ? target : join6(p, "..", target);
6044
- return existsSync4(resolved);
6045
- } catch {
6046
- return false;
6047
- }
6048
- }
6039
+ execFileSync2("brew", ["--version"], { timeout: 5e3, stdio: "ignore" });
6049
6040
  return true;
6050
6041
  } catch {
6051
6042
  return false;
6052
6043
  }
6053
6044
  }
6054
- function hasHomebrew() {
6045
+ function hasCommand(cmd) {
6055
6046
  try {
6056
- execFileSync2("brew", ["--version"], { timeout: 5e3, stdio: "ignore" });
6047
+ execFileSync2("which", [cmd], { timeout: 5e3, stdio: "ignore" });
6057
6048
  return true;
6058
6049
  } catch {
6059
6050
  return false;
@@ -6061,41 +6052,27 @@ function hasHomebrew() {
6061
6052
  }
6062
6053
  var DependencyInstaller = class {
6063
6054
  onProgress;
6064
- _firstLaunchMode = false;
6065
6055
  constructor(onProgress) {
6066
6056
  this.onProgress = onProgress ?? (() => {
6067
6057
  });
6068
6058
  }
6069
6059
  /**
6070
6060
  * Ensure Docker is installed AND the daemon is running.
6071
- * Fully automatic — installs, accepts license, starts daemon, waits for ready.
6072
- * Never asks the user to do anything manually.
6073
6061
  *
6074
6062
  * Flow:
6075
- * 1. docker CLI + daemon running? → done
6076
- * 2. docker CLI exists but daemon stopped? → start daemon, wait
6077
- * 3. Docker.app exists (in /Applications or Caskroom)? run installer --accept-license, start, wait
6078
- * 4. Nothing installed? → install via Homebrew or DMG download, then start, wait
6063
+ * 1. docker info works? → done
6064
+ * 2. docker CLI + colima exist but not running? → colima start
6065
+ * 3. Nothing installed? install via brew (colima + docker) or Docker Engine (Linux)
6079
6066
  */
6080
6067
  async installDocker() {
6081
6068
  if (this.isDockerReady()) return;
6082
- if (this.isDockerCliInstalled()) {
6083
- this.onProgress("__progress__:10:Docker installed \u2014 starting the engine...");
6084
- this.startDockerDaemon();
6085
- await this.waitForDocker();
6086
- return;
6087
- }
6088
- const dockerApp = this.findDockerApp();
6089
- if (dockerApp) {
6090
- this.onProgress("__progress__:10:Setting up mail server engine...");
6091
- await this.setupExistingDockerApp(dockerApp);
6092
- return;
6093
- }
6094
6069
  const os = platform2();
6095
6070
  if (os === "darwin") {
6096
6071
  await this.installDockerMac();
6097
6072
  } else if (os === "linux") {
6098
6073
  await this.installDockerLinux();
6074
+ } else if (os === "win32") {
6075
+ await this.installDockerWindows();
6099
6076
  } else {
6100
6077
  throw new Error(
6101
6078
  `Docker auto-install isn't supported on ${os} yet. Install it from https://docs.docker.com/get-docker/ and try again.`
@@ -6111,334 +6088,92 @@ var DependencyInstaller = class {
6111
6088
  return false;
6112
6089
  }
6113
6090
  }
6114
- /** Check if `docker` CLI is in PATH (daemon may be stopped). */
6115
- isDockerCliInstalled() {
6116
- try {
6117
- execFileSync2("docker", ["--version"], { timeout: 5e3, stdio: "ignore" });
6118
- return true;
6119
- } catch {
6120
- return false;
6121
- }
6122
- }
6123
6091
  /**
6124
- * Find Docker.app on macOS.
6125
- * Checks /Applications first, then Homebrew Caskroom.
6126
- * Validates symlinks aren't broken.
6092
+ * macOS: Install Docker via Colima (CLI-only, no GUI, no license dialogs).
6093
+ * Installs colima + docker + docker-compose via Homebrew, then starts Colima.
6127
6094
  */
6128
- findDockerApp() {
6129
- if (existsReal("/Applications/Docker.app")) {
6130
- return "/Applications/Docker.app";
6131
- }
6132
- const caskroomBases = [
6133
- "/opt/homebrew/Caskroom",
6134
- "/usr/local/Caskroom"
6135
- ];
6136
- const caskNames = ["docker-desktop", "docker"];
6137
- for (const base of caskroomBases) {
6138
- for (const name of caskNames) {
6139
- const caskroom = join6(base, name);
6140
- if (!existsSync4(caskroom)) continue;
6141
- try {
6142
- const versions = readdirSync(caskroom).filter((d) => !d.startsWith("."));
6143
- for (const ver of versions) {
6144
- const appPath = join6(caskroom, ver, "Docker.app");
6145
- if (existsReal(appPath)) return appPath;
6146
- }
6147
- } catch {
6148
- }
6149
- }
6095
+ async installDockerMac() {
6096
+ if (hasCommand("colima") && hasCommand("docker")) {
6097
+ this.onProgress("__progress__:10:Starting container engine...");
6098
+ await this.startColima();
6099
+ return;
6150
6100
  }
6151
- return null;
6152
- }
6153
- /**
6154
- * Check if Docker Desktop license has already been accepted.
6155
- * Returns true if we're confident the user has accepted before.
6156
- */
6157
- isDockerLicenseAccepted() {
6158
- try {
6159
- const systemFile = "/Library/Application Support/com.docker.docker/install-settings.json";
6160
- if (existsSync4(systemFile)) {
6161
- const raw = readFileSync2(systemFile, "utf-8");
6162
- const data = JSON.parse(raw);
6163
- if (data.acceptLicense) return true;
6164
- }
6165
- } catch {
6101
+ if (!hasHomebrew()) {
6102
+ throw new Error(
6103
+ "Homebrew is required to install Docker on macOS.\nInstall it from https://brew.sh and try again."
6104
+ );
6166
6105
  }
6167
- try {
6168
- const settingsFile = join6(process.env.HOME || "", "Library/Group Containers/group.com.docker/settings-store.json");
6169
- if (existsSync4(settingsFile)) {
6170
- return true;
6171
- }
6172
- } catch {
6106
+ this.onProgress("__progress__:5:Installing container engine...");
6107
+ const brewResult = await runSilent(
6108
+ "brew",
6109
+ ["install", "colima", "docker", "docker-compose"],
6110
+ { timeout: 6e5 }
6111
+ );
6112
+ if (brewResult.exitCode !== 0) {
6113
+ throw new Error(
6114
+ "Failed to install Docker via Homebrew.\nTry running manually: brew install colima docker docker-compose\n" + brewResult.fullOutput.slice(-500)
6115
+ );
6173
6116
  }
6174
- return false;
6175
- }
6176
- /**
6177
- * Configure Docker Desktop to run headless after first-time setup.
6178
- * Call this AFTER Docker has been started and license accepted.
6179
- * Suppresses UI, dock icon, tips, analytics, and auto-updates.
6180
- */
6181
- configureDockerHeadless() {
6182
- try {
6183
- const userDockerDir = join6(process.env.HOME || "", "Library/Group Containers/group.com.docker");
6184
- const settingsFile = join6(userDockerDir, "settings-store.json");
6185
- const headlessDefaults = {
6186
- AutoStart: false,
6187
- // Don't auto-start on login (we start it ourselves)
6188
- OpenUIOnStartupDisabled: true,
6189
- // Don't show dashboard on start
6190
- TipShown: true,
6191
- // Don't show tips/welcome
6192
- SUAutomaticallyUpdate: false,
6193
- // Don't nag about updates
6194
- AnalyticsEnabled: false
6195
- // No analytics prompts
6196
- };
6197
- if (existsSync4(settingsFile)) {
6198
- const raw = readFileSync2(settingsFile, "utf-8");
6199
- try {
6200
- const settings = JSON.parse(raw);
6201
- let changed = false;
6202
- for (const [key, val] of Object.entries(headlessDefaults)) {
6203
- if (settings[key] !== val) {
6204
- settings[key] = val;
6205
- changed = true;
6206
- }
6207
- }
6208
- if (changed) {
6209
- writeFileSync3(settingsFile, JSON.stringify(settings, null, 2));
6210
- }
6211
- } catch {
6212
- }
6213
- } else if (existsSync4(userDockerDir)) {
6214
- writeFileSync3(settingsFile, JSON.stringify(headlessDefaults, null, 2));
6215
- }
6216
- } catch {
6117
+ if (!hasCommand("colima") || !hasCommand("docker")) {
6118
+ throw new Error(
6119
+ "Installation completed but colima/docker not found in PATH.\nTry running manually: brew install colima docker docker-compose"
6120
+ );
6217
6121
  }
6122
+ this.onProgress("__progress__:50:Starting container engine...");
6123
+ await this.startColima();
6218
6124
  }
6219
6125
  /**
6220
- * Docker.app exists but CLI isn't available or daemon isn't running.
6221
- * Runs the built-in installer silently (--accept-license to link CLI tools),
6222
- * then starts Docker Desktop hidden (no GUI window).
6126
+ * Start Colima and wait for Docker to be ready.
6223
6127
  */
6224
- /**
6225
- * Docker.app exists but daemon isn't running.
6226
- * Simple approach: if Docker was already running, great. If not, just
6227
- * tell the user to open Docker and accept the terms themselves.
6228
- * No GUI manipulation, no programmatic license tricks — just wait.
6229
- */
6230
- async setupExistingDockerApp(_appPath) {
6128
+ async startColima() {
6231
6129
  if (this.isDockerReady()) {
6232
6130
  this.onProgress("__progress__:100:Engine is ready!");
6233
6131
  return;
6234
6132
  }
6235
- this.onProgress("Open Docker Desktop and accept the license agreement, then we'll continue automatically...");
6236
- const totalTime = 6e5;
6133
+ const startResult = await runSilent(
6134
+ "colima",
6135
+ ["start", "--cpu", "2", "--memory", "2", "--disk", "10"],
6136
+ { timeout: 3e5 }
6137
+ );
6138
+ if (startResult.exitCode !== 0) {
6139
+ throw new Error(
6140
+ "Failed to start Colima. Try running manually: colima start\n" + startResult.fullOutput.slice(-500)
6141
+ );
6142
+ }
6143
+ const totalTime = 6e4;
6237
6144
  const start = Date.now();
6238
6145
  while (Date.now() - start < totalTime) {
6239
- try {
6240
- execFileSync2("docker", ["info"], { timeout: 5e3, stdio: "ignore" });
6146
+ if (this.isDockerReady()) {
6241
6147
  this.onProgress("__progress__:100:Engine is ready!");
6242
- this.configureDockerHeadless();
6243
- this.hideDockerWindow();
6244
6148
  return;
6245
- } catch {
6246
6149
  }
6247
6150
  const elapsed = Date.now() - start;
6248
- const pct = Math.min(95, Math.round(elapsed / totalTime * 100));
6249
- const msgs = [
6250
- "Open Docker Desktop and accept the license to continue...",
6251
- "Waiting for Docker to be ready...",
6252
- "Accept the terms in Docker Desktop to continue...",
6253
- "Still waiting for Docker..."
6254
- ];
6255
- const msgIdx = Math.floor(elapsed / 8e3) % msgs.length;
6256
- this.onProgress(`__progress__:${pct}:${msgs[msgIdx]}`);
6257
- await new Promise((r) => setTimeout(r, 3e3));
6151
+ const pct = Math.min(95, 50 + Math.round(elapsed / totalTime * 50));
6152
+ this.onProgress(`__progress__:${pct}:Waiting for engine...`);
6153
+ await new Promise((r) => setTimeout(r, 2e3));
6258
6154
  }
6259
6155
  throw new Error(
6260
- "Docker did not start. Open Docker Desktop from your Applications folder, accept the license agreement, then run this command again."
6156
+ "Docker engine did not start in time. Try running manually: colima start"
6261
6157
  );
6262
6158
  }
6263
6159
  /**
6264
- * Hide Docker Desktop completely close all windows, hide from dock, make invisible.
6265
- * Called after starting Docker to ensure the user never sees any Docker UI.
6266
- */
6267
- hideDockerWindow() {
6268
- try {
6269
- execSync(
6270
- `osascript -e 'tell application "System Events" to tell process "Docker Desktop" to set visible to false' 2>/dev/null`,
6271
- { timeout: 5e3, stdio: "ignore" }
6272
- );
6273
- } catch {
6274
- }
6275
- try {
6276
- execSync(
6277
- `osascript -e 'tell application "Docker Desktop" to close every window' 2>/dev/null`,
6278
- { timeout: 5e3, stdio: "ignore" }
6279
- );
6280
- } catch {
6281
- }
6282
- try {
6283
- execSync(
6284
- `osascript -e 'tell application "Docker" to close every window' 2>/dev/null`,
6285
- { timeout: 5e3, stdio: "ignore" }
6286
- );
6287
- } catch {
6288
- }
6289
- const dockerApp = this.findDockerApp();
6290
- if (dockerApp) {
6291
- const plistPath = join6(dockerApp, "Contents", "Info.plist");
6292
- try {
6293
- const plistContent = readFileSync2(plistPath, "utf-8");
6294
- if (!plistContent.includes("LSUIElement")) {
6295
- execSync(
6296
- `/usr/libexec/PlistBuddy -c "Add :LSUIElement bool true" "${plistPath}" 2>/dev/null`,
6297
- { timeout: 5e3, stdio: "ignore" }
6298
- );
6299
- } else if (!plistContent.includes("<key>LSUIElement</key>\n <true/>") && !plistContent.includes("<key>LSUIElement</key><true/>")) {
6300
- execSync(
6301
- `/usr/libexec/PlistBuddy -c "Set :LSUIElement true" "${plistPath}" 2>/dev/null`,
6302
- { timeout: 5e3, stdio: "ignore" }
6303
- );
6304
- }
6305
- } catch {
6306
- }
6307
- }
6308
- }
6309
- /**
6310
- * Full Docker Desktop install on macOS.
6311
- * Tries Homebrew first (cleaner), falls back to DMG download.
6312
- * After install, tells the user to open Docker and accept the terms.
6313
- * No GUI manipulation — the user handles Docker themselves.
6314
- */
6315
- /**
6316
- * Ensure /usr/local/bin and /usr/local/cli-plugins are writable by the
6317
- * current user. Brew needs to create symlinks there and will fail silently
6318
- * if they're root-owned (it tries sudo which fails non-interactively).
6319
- */
6320
- fixLocalDirPermissions() {
6321
- const user = process.env.USER || "";
6322
- if (!user) return;
6323
- const dirs = ["/usr/local/bin", "/usr/local/cli-plugins"];
6324
- for (const dir of dirs) {
6325
- try {
6326
- if (!existsSync4(dir)) {
6327
- execSync(`mkdir -p "${dir}"`, { timeout: 5e3, stdio: "ignore" });
6328
- }
6329
- const stat = execSync(`stat -f '%Su' "${dir}"`, { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
6330
- if (stat !== user) {
6331
- try {
6332
- execSync(`find "${dir}" -maxdepth 1 -type l ! -exec test -e {} \\; -delete 2>/dev/null`, { timeout: 5e3, stdio: "ignore" });
6333
- } catch {
6334
- }
6335
- try {
6336
- execSync(`chown -R ${user} "${dir}"`, { timeout: 5e3, stdio: "ignore" });
6337
- } catch {
6338
- }
6339
- }
6340
- } catch {
6341
- }
6342
- }
6343
- }
6344
- /**
6345
- * After DMG install, manually link Docker CLI tools (brew does this
6346
- * automatically, but the DMG path doesn't).
6160
+ * Install Docker Engine on Linux using Docker's official convenience script.
6161
+ * Also adds the current user to the docker group for rootless usage.
6347
6162
  */
6348
- linkDockerCli(appPath) {
6349
- const links = [
6350
- [join6(appPath, "Contents/Resources/bin/docker"), "/usr/local/bin/docker"],
6351
- [join6(appPath, "Contents/Resources/bin/docker-credential-desktop"), "/usr/local/bin/docker-credential-desktop"],
6352
- [join6(appPath, "Contents/Resources/bin/docker-credential-ecr-login"), "/usr/local/bin/docker-credential-ecr-login"],
6353
- [join6(appPath, "Contents/Resources/bin/docker-credential-osxkeychain"), "/usr/local/bin/docker-credential-osxkeychain"],
6354
- [join6(appPath, "Contents/Resources/cli-plugins/docker-compose"), "/usr/local/cli-plugins/docker-compose"]
6355
- ];
6356
- for (const [src, dest] of links) {
6357
- try {
6358
- if (existsSync4(src)) {
6359
- const destDir = join6(dest, "..");
6360
- if (!existsSync4(destDir)) mkdirSync3(destDir, { recursive: true });
6361
- try {
6362
- execSync(`rm -f "${dest}"`, { timeout: 3e3, stdio: "ignore" });
6363
- } catch {
6364
- }
6365
- execSync(`ln -s "${src}" "${dest}"`, { timeout: 3e3, stdio: "ignore" });
6366
- }
6367
- } catch {
6368
- }
6369
- }
6370
- }
6371
- async installDockerMac() {
6372
- let installed = false;
6373
- this.fixLocalDirPermissions();
6374
- if (hasHomebrew()) {
6375
- this.onProgress("__progress__:5:Installing Docker Desktop...");
6376
- try {
6377
- const brewResult = await runSilent(
6378
- "brew",
6379
- ["install", "--cask", "docker"],
6380
- { timeout: 6e5 }
6381
- );
6382
- if (brewResult.exitCode === 0 && this.findDockerApp()) {
6383
- installed = true;
6384
- }
6385
- } catch {
6386
- }
6387
- }
6388
- if (!installed) {
6389
- const cpu = arch2();
6390
- const archName = cpu === "arm64" ? "arm64" : "amd64";
6391
- const dmgUrl = `https://desktop.docker.com/mac/main/${archName}/Docker.dmg`;
6392
- const dmgPath = "/tmp/Docker.dmg";
6393
- this.onProgress("__progress__:5:Downloading Docker Desktop...");
6394
- const dlResult = await runSilent("curl", [
6395
- "-fSL",
6396
- "-o",
6397
- dmgPath,
6398
- dmgUrl
6399
- ], { timeout: 6e5 });
6400
- if (dlResult.exitCode !== 0) {
6401
- throw new Error("Failed to download Docker Desktop. Check your internet connection and try again.");
6402
- }
6403
- this.onProgress("__progress__:40:Installing Docker Desktop...");
6163
+ async installDockerLinux() {
6164
+ if (hasCommand("docker")) {
6165
+ this.onProgress("__progress__:10:Starting Docker service...");
6404
6166
  try {
6405
- try {
6406
- execSync("hdiutil detach /Volumes/Docker 2>/dev/null", { timeout: 1e4, stdio: "ignore" });
6407
- } catch {
6408
- }
6409
- execSync(`hdiutil attach "${dmgPath}" -nobrowse -quiet`, { timeout: 3e4, stdio: "ignore" });
6167
+ execSync("sudo systemctl start docker", { timeout: 15e3, stdio: "ignore" });
6410
6168
  } catch {
6411
- throw new Error("Failed to mount Docker DMG. The download may be corrupted \u2014 try again.");
6412
- }
6413
- if (!existsSync4("/Applications/Docker.app")) {
6414
6169
  try {
6415
- execSync('cp -R "/Volumes/Docker/Docker.app" /Applications/', { timeout: 6e4, stdio: "ignore" });
6170
+ execSync("sudo service docker start", { timeout: 15e3, stdio: "ignore" });
6416
6171
  } catch {
6417
- throw new Error("Failed to install. You may need to run this with admin privileges.");
6418
6172
  }
6419
6173
  }
6420
- try {
6421
- execSync("hdiutil detach /Volumes/Docker -quiet", { timeout: 15e3, stdio: "ignore" });
6422
- } catch {
6423
- }
6424
- try {
6425
- await unlink(dmgPath);
6426
- } catch {
6427
- }
6428
- const appPath2 = this.findDockerApp();
6429
- if (appPath2) this.linkDockerCli(appPath2);
6430
- }
6431
- const appPath = this.findDockerApp();
6432
- if (!appPath) {
6433
- throw new Error("Docker Desktop was installed but could not be found. Try again.");
6174
+ await this.waitForDockerLinux();
6175
+ return;
6434
6176
  }
6435
- await this.setupExistingDockerApp(appPath);
6436
- }
6437
- /**
6438
- * Install Docker Engine on Linux using Docker's official convenience script.
6439
- * Also adds the current user to the docker group for rootless usage.
6440
- */
6441
- async installDockerLinux() {
6442
6177
  this.onProgress("__progress__:5:Installing Docker Engine...");
6443
6178
  const dlResult = await runShellWithRollingOutput(
6444
6179
  "curl -fsSL https://get.docker.com -o /tmp/install-docker.sh && sudo sh /tmp/install-docker.sh",
@@ -6470,172 +6205,126 @@ var DependencyInstaller = class {
6470
6205
  } catch {
6471
6206
  }
6472
6207
  }
6473
- await this.waitForDocker();
6208
+ await this.waitForDockerLinux();
6474
6209
  }
6475
6210
  /**
6476
- * Start the Docker daemon using multiple strategies.
6477
- * On macOS: tries Docker Desktop app via `open`, direct binary launch, etc.
6478
- * On Linux: tries systemctl, service, snap.
6211
+ * Wait for Docker daemon on Linux.
6479
6212
  */
6480
- startDockerDaemon(strategy) {
6481
- const os = platform2();
6482
- if (os === "darwin") {
6483
- const dockerApp = this.findDockerApp();
6484
- switch (strategy) {
6485
- case "cli":
6486
- try {
6487
- execSync("docker context use default 2>/dev/null; docker info", { timeout: 5e3, stdio: "ignore" });
6488
- } catch {
6489
- }
6490
- break;
6491
- case "reopen":
6492
- try {
6493
- execSync(`osascript -e 'quit app "Docker"'`, { timeout: 5e3, stdio: "ignore" });
6494
- } catch {
6495
- }
6496
- try {
6497
- execFileSync2("sleep", ["2"], { timeout: 5e3, stdio: "ignore" });
6498
- } catch {
6499
- }
6500
- if (dockerApp) {
6501
- try {
6502
- execFileSync2("open", ["-g", "-j", dockerApp], { timeout: 1e4, stdio: "ignore" });
6503
- } catch {
6504
- }
6505
- } else {
6506
- try {
6507
- execFileSync2("open", ["-g", "-j", "-a", "Docker"], { timeout: 1e4, stdio: "ignore" });
6508
- } catch {
6509
- }
6510
- }
6511
- break;
6512
- case "background": {
6513
- const appBin = dockerApp ? join6(dockerApp, "Contents", "MacOS", "Docker") : "/Applications/Docker.app/Contents/MacOS/Docker";
6514
- try {
6515
- if (existsSync4(appBin)) {
6516
- execSync(`"${appBin}" &`, { timeout: 5e3, stdio: "ignore", shell: "sh" });
6517
- }
6518
- } catch {
6519
- }
6520
- break;
6521
- }
6522
- case "install": {
6523
- const installBin = dockerApp ? join6(dockerApp, "Contents", "MacOS", "install") : "/Applications/Docker.app/Contents/MacOS/install";
6524
- if (existsSync4(installBin)) {
6525
- const user = process.env.USER || "nobody";
6526
- try {
6527
- execSync(`"${installBin}" --accept-license --user=${user}`, { timeout: 6e4, stdio: "ignore" });
6528
- } catch {
6529
- }
6530
- }
6531
- if (dockerApp) {
6532
- try {
6533
- execFileSync2("open", ["-g", "-j", dockerApp], { timeout: 1e4, stdio: "ignore" });
6534
- } catch {
6535
- }
6536
- }
6537
- break;
6213
+ async waitForDockerLinux() {
6214
+ const totalTime = 6e4;
6215
+ const start = Date.now();
6216
+ while (Date.now() - start < totalTime) {
6217
+ if (this.isDockerReady()) {
6218
+ this.onProgress("__progress__:100:Docker is ready!");
6219
+ return;
6220
+ }
6221
+ const elapsed = Date.now() - start;
6222
+ const pct = Math.min(95, Math.round(elapsed / totalTime * 100));
6223
+ this.onProgress(`__progress__:${pct}:Waiting for Docker...`);
6224
+ await new Promise((r) => setTimeout(r, 2e3));
6225
+ }
6226
+ throw new Error("Docker did not start. Try: sudo systemctl start docker");
6227
+ }
6228
+ /**
6229
+ * Windows: Install Docker via WSL2 + Docker Engine, or guide user to Docker Desktop.
6230
+ * Prefers WSL2 with Docker Engine (no GUI needed).
6231
+ */
6232
+ async installDockerWindows() {
6233
+ if (hasCommand("docker")) {
6234
+ if (this.isDockerReady()) {
6235
+ this.onProgress("__progress__:100:Engine is ready!");
6236
+ return;
6237
+ }
6238
+ this.onProgress("__progress__:10:Starting Docker...");
6239
+ try {
6240
+ execSync("net start com.docker.service", { timeout: 3e4, stdio: "ignore" });
6241
+ } catch {
6242
+ }
6243
+ try {
6244
+ const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
6245
+ const dockerExe = join6(programFiles, "Docker", "Docker", "Docker Desktop.exe");
6246
+ if (existsSync4(dockerExe)) {
6247
+ execSync(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
6538
6248
  }
6539
- default:
6540
- if (dockerApp) {
6541
- try {
6542
- execFileSync2("open", ["-g", "-j", dockerApp], { timeout: 1e4, stdio: "ignore" });
6543
- } catch {
6544
- }
6545
- } else {
6546
- try {
6547
- execFileSync2("open", ["-g", "-j", "-a", "Docker"], { timeout: 1e4, stdio: "ignore" });
6548
- } catch {
6549
- }
6550
- }
6249
+ } catch {
6551
6250
  }
6552
- } else if (os === "linux") {
6553
- switch (strategy) {
6554
- case "snap":
6555
- try {
6556
- execFileSync2("sudo", ["snap", "start", "docker"], { timeout: 15e3, stdio: "ignore" });
6557
- } catch {
6558
- }
6559
- break;
6560
- case "service":
6251
+ await this.waitForDockerWindows();
6252
+ return;
6253
+ }
6254
+ let hasWsl = false;
6255
+ try {
6256
+ const wslResult = execSync("wsl --status", { timeout: 1e4, stdio: ["ignore", "pipe", "ignore"] }).toString();
6257
+ hasWsl = wslResult.length > 0;
6258
+ } catch {
6259
+ }
6260
+ if (hasWsl) {
6261
+ this.onProgress("__progress__:5:Installing Docker Engine in WSL...");
6262
+ try {
6263
+ const wslResult = await runSilent(
6264
+ "wsl",
6265
+ ["-e", "sh", "-c", "curl -fsSL https://get.docker.com | sh"],
6266
+ { timeout: 3e5 }
6267
+ );
6268
+ if (wslResult.exitCode === 0) {
6561
6269
  try {
6562
- execFileSync2("sudo", ["service", "docker", "start"], { timeout: 15e3, stdio: "ignore" });
6270
+ execSync("wsl -e sudo service docker start", { timeout: 15e3, stdio: "ignore" });
6563
6271
  } catch {
6564
6272
  }
6565
- break;
6566
- default:
6567
- try {
6568
- execFileSync2("sudo", ["systemctl", "start", "docker"], { timeout: 15e3, stdio: "ignore" });
6569
- } catch {
6273
+ await this.waitForDockerWindows();
6274
+ return;
6275
+ }
6276
+ } catch {
6277
+ }
6278
+ }
6279
+ let hasWinget = false;
6280
+ try {
6281
+ execSync("winget --version", { timeout: 5e3, stdio: "ignore" });
6282
+ hasWinget = true;
6283
+ } catch {
6284
+ }
6285
+ if (hasWinget) {
6286
+ this.onProgress("__progress__:5:Installing Docker Desktop...");
6287
+ const wingetResult = await runSilent(
6288
+ "winget",
6289
+ ["install", "-e", "--id", "Docker.DockerDesktop", "--accept-source-agreements", "--accept-package-agreements"],
6290
+ { timeout: 6e5 }
6291
+ );
6292
+ if (wingetResult.exitCode === 0) {
6293
+ this.onProgress("__progress__:70:Docker Desktop installed. Starting...");
6294
+ try {
6295
+ const programFiles = process.env["ProgramFiles"] || "C:\\Program Files";
6296
+ const dockerExe = join6(programFiles, "Docker", "Docker", "Docker Desktop.exe");
6297
+ if (existsSync4(dockerExe)) {
6298
+ execSync(`start "" "${dockerExe}"`, { timeout: 1e4, stdio: "ignore", shell: "cmd.exe" });
6570
6299
  }
6300
+ } catch {
6301
+ }
6302
+ await this.waitForDockerWindows();
6303
+ return;
6571
6304
  }
6572
6305
  }
6306
+ throw new Error(
6307
+ 'Could not auto-install Docker on Windows.\nPlease install Docker Desktop from https://docs.docker.com/desktop/install/windows-install/\nOr install WSL2 and Docker Engine: wsl --install && wsl -e sh -c "curl -fsSL https://get.docker.com | sh"'
6308
+ );
6573
6309
  }
6574
6310
  /**
6575
- * Wait for Docker daemon to be ready.
6576
- * Cycles through multiple start strategies automatically.
6577
- * Reports progress as a percentage (0-100).
6311
+ * Wait for Docker daemon on Windows.
6578
6312
  */
6579
- async waitForDocker() {
6580
- const os = platform2();
6581
- const strategies = os === "darwin" ? ["default", "cli", "reopen", "background", "install"] : ["default", "service", "snap"];
6582
- const totalTime = 3e5;
6583
- const perStrategyTime = Math.floor(totalTime / strategies.length);
6313
+ async waitForDockerWindows() {
6314
+ const totalTime = 12e4;
6584
6315
  const start = Date.now();
6585
- let strategyIdx = 0;
6586
- this.onProgress("__progress__:0:Starting Docker...");
6587
6316
  while (Date.now() - start < totalTime) {
6588
- try {
6589
- execFileSync2("docker", ["info"], { timeout: 5e3, stdio: "ignore" });
6317
+ if (this.isDockerReady()) {
6590
6318
  this.onProgress("__progress__:100:Docker is ready!");
6591
6319
  return;
6592
- } catch {
6593
6320
  }
6594
6321
  const elapsed = Date.now() - start;
6595
6322
  const pct = Math.min(95, Math.round(elapsed / totalTime * 100));
6596
- if (this._firstLaunchMode) {
6597
- const msgs = [
6598
- "Please accept the license agreement in the Docker window...",
6599
- "Waiting for you to accept the terms...",
6600
- "Accept the agreement to continue...",
6601
- "Still waiting..."
6602
- ];
6603
- const msgIdx = Math.floor(elapsed / 8e3) % msgs.length;
6604
- this.onProgress(`__progress__:${pct}:${msgs[msgIdx]}`);
6605
- } else {
6606
- const currentStrategyElapsed = elapsed - strategyIdx * perStrategyTime;
6607
- if (currentStrategyElapsed >= perStrategyTime && strategyIdx < strategies.length - 1) {
6608
- strategyIdx++;
6609
- const strategy = strategies[strategyIdx];
6610
- const msgs = {
6611
- cli: "Checking engine...",
6612
- reopen: "Restarting engine...",
6613
- background: "Trying direct launch...",
6614
- install: "Re-running installer...",
6615
- service: "Trying service command...",
6616
- snap: "Trying snap..."
6617
- };
6618
- this.onProgress(`__progress__:${pct}:${msgs[strategy] || "Trying another approach..."}`);
6619
- this.startDockerDaemon(strategy);
6620
- } else {
6621
- const msgs = [
6622
- "Starting engine...",
6623
- "Waiting for engine...",
6624
- "Loading...",
6625
- "Almost there...",
6626
- "Still starting up...",
6627
- "First launch takes a bit longer...",
6628
- "Hang tight..."
6629
- ];
6630
- const msgIdx = Math.floor(elapsed / 1e4) % msgs.length;
6631
- this.onProgress(`__progress__:${pct}:${msgs[msgIdx]}`);
6632
- }
6633
- if (os === "darwin") this.hideDockerWindow();
6634
- }
6323
+ this.onProgress(`__progress__:${pct}:Waiting for Docker...`);
6635
6324
  await new Promise((r) => setTimeout(r, 3e3));
6636
6325
  }
6637
6326
  throw new Error(
6638
- "Engine could not be started after trying all available methods. If this is your first time, open Docker from your Applications folder, accept the license agreement, then run this command again."
6327
+ "Docker did not start in time.\nMake sure Docker Desktop is running, then try again."
6639
6328
  );
6640
6329
  }
6641
6330
  /**
@@ -6645,12 +6334,24 @@ var DependencyInstaller = class {
6645
6334
  if (!existsSync4(composePath)) {
6646
6335
  throw new Error(`docker-compose.yml not found at: ${composePath}`);
6647
6336
  }
6648
- try {
6649
- execFileSync2("docker", ["info"], { timeout: 1e4, stdio: "ignore" });
6650
- } catch {
6337
+ if (!this.isDockerReady()) {
6651
6338
  this.onProgress("Starting Docker...");
6652
- this.startDockerDaemon();
6653
- await this.waitForDocker();
6339
+ const os = platform2();
6340
+ if (os === "darwin") {
6341
+ await this.startColima();
6342
+ } else if (os === "win32") {
6343
+ await this.waitForDockerWindows();
6344
+ } else {
6345
+ try {
6346
+ execSync("sudo systemctl start docker", { timeout: 15e3, stdio: "ignore" });
6347
+ } catch {
6348
+ try {
6349
+ execSync("sudo service docker start", { timeout: 15e3, stdio: "ignore" });
6350
+ } catch {
6351
+ }
6352
+ }
6353
+ await this.waitForDockerLinux();
6354
+ }
6654
6355
  }
6655
6356
  this.onProgress("__progress__:10:Pulling mail server image...");
6656
6357
  const composeResult = await runWithRollingOutput("docker", ["compose", "-f", composePath, "up", "-d"], { timeout: 12e4 });
@@ -6684,25 +6385,31 @@ var DependencyInstaller = class {
6684
6385
  * Returns the path to the installed binary.
6685
6386
  */
6686
6387
  async installCloudflared() {
6388
+ const os = platform2();
6687
6389
  const binDir = join6(homedir5(), ".agenticmail", "bin");
6688
- const binPath = join6(binDir, "cloudflared");
6390
+ const binName = os === "win32" ? "cloudflared.exe" : "cloudflared";
6391
+ const binPath = join6(binDir, binName);
6689
6392
  if (existsSync4(binPath)) {
6690
6393
  return binPath;
6691
6394
  }
6692
6395
  try {
6693
- const sysPath = execFileSync2("which", ["cloudflared"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim();
6396
+ const whichCmd = os === "win32" ? "where" : "which";
6397
+ const sysPath = execFileSync2(whichCmd, ["cloudflared"], { timeout: 5e3, stdio: ["ignore", "pipe", "ignore"] }).toString().trim().split("\n")[0];
6694
6398
  if (sysPath && existsSync4(sysPath)) return sysPath;
6695
6399
  } catch {
6696
6400
  }
6697
6401
  this.onProgress("Downloading cloudflared...");
6698
6402
  await mkdir2(binDir, { recursive: true });
6699
- const os = platform2();
6700
6403
  const cpu = arch2();
6701
6404
  const archName = cpu === "arm64" ? "arm64" : "amd64";
6702
- const isTgz = os === "darwin";
6703
- const ext = isTgz ? ".tgz" : "";
6704
- const downloadUrl = `https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-${os}-${archName}${ext}`;
6705
- if (os !== "darwin" && os !== "linux") {
6405
+ let downloadUrl;
6406
+ if (os === "darwin") {
6407
+ downloadUrl = `https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-darwin-${archName}.tgz`;
6408
+ } else if (os === "linux") {
6409
+ downloadUrl = `https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${archName}`;
6410
+ } else if (os === "win32") {
6411
+ downloadUrl = `https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-${archName}.exe`;
6412
+ } else {
6706
6413
  throw new Error(`Unsupported platform: ${os}/${cpu}`);
6707
6414
  }
6708
6415
  const response = await fetch(downloadUrl);
@@ -6710,7 +6417,7 @@ var DependencyInstaller = class {
6710
6417
  throw new Error(`Failed to download cloudflared: ${response.statusText}`);
6711
6418
  }
6712
6419
  const buffer = Buffer.from(await response.arrayBuffer());
6713
- if (isTgz) {
6420
+ if (os === "darwin") {
6714
6421
  const tgzPath = join6(binDir, "cloudflared.tgz");
6715
6422
  await writeFile(tgzPath, buffer);
6716
6423
  try {
@@ -6725,7 +6432,7 @@ var DependencyInstaller = class {
6725
6432
  } else {
6726
6433
  const tmpPath = binPath + ".tmp";
6727
6434
  await writeFile(tmpPath, buffer);
6728
- await chmod2(tmpPath, 493);
6435
+ if (os !== "win32") await chmod2(tmpPath, 493);
6729
6436
  await rename(tmpPath, binPath);
6730
6437
  }
6731
6438
  if (!existsSync4(binPath)) {
@@ -6746,7 +6453,7 @@ var DependencyInstaller = class {
6746
6453
 
6747
6454
  // src/setup/service.ts
6748
6455
  import { execFileSync as execFileSync3, execSync as execSync2 } from "child_process";
6749
- import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync4, unlinkSync, mkdirSync as mkdirSync4 } from "fs";
6456
+ import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync3, unlinkSync, mkdirSync as mkdirSync3 } from "fs";
6750
6457
  import { join as join7 } from "path";
6751
6458
  import { homedir as homedir6, platform as platform3 } from "os";
6752
6459
  var PLIST_LABEL = "com.agenticmail.server";
@@ -6809,7 +6516,7 @@ var ServiceManager = class {
6809
6516
  const dataDir = join7(homedir6(), ".agenticmail");
6810
6517
  const entryCache = join7(dataDir, "api-entry.path");
6811
6518
  if (existsSync5(entryCache)) {
6812
- const cached = readFileSync3(entryCache, "utf-8").trim();
6519
+ const cached = readFileSync2(entryCache, "utf-8").trim();
6813
6520
  if (existsSync5(cached)) return cached;
6814
6521
  }
6815
6522
  throw new Error("Could not find @agenticmail/api entry point. Run `agenticmail start` first to populate the cache.");
@@ -6819,8 +6526,8 @@ var ServiceManager = class {
6819
6526
  */
6820
6527
  cacheApiEntryPath(entryPath) {
6821
6528
  const dataDir = join7(homedir6(), ".agenticmail");
6822
- if (!existsSync5(dataDir)) mkdirSync4(dataDir, { recursive: true });
6823
- writeFileSync4(join7(dataDir, "api-entry.path"), entryPath);
6529
+ if (!existsSync5(dataDir)) mkdirSync3(dataDir, { recursive: true });
6530
+ writeFileSync3(join7(dataDir, "api-entry.path"), entryPath);
6824
6531
  }
6825
6532
  /**
6826
6533
  * Get the current package version.
@@ -6838,7 +6545,7 @@ var ServiceManager = class {
6838
6545
  }
6839
6546
  for (const p of pkgPaths) {
6840
6547
  if (existsSync5(p)) {
6841
- const pkg = JSON.parse(readFileSync3(p, "utf-8"));
6548
+ const pkg = JSON.parse(readFileSync2(p, "utf-8"));
6842
6549
  if (pkg.version) return pkg.version;
6843
6550
  }
6844
6551
  }
@@ -6853,7 +6560,7 @@ var ServiceManager = class {
6853
6560
  generateStartScript(nodePath, apiEntry) {
6854
6561
  const scriptPath = join7(homedir6(), ".agenticmail", "bin", "start-server.sh");
6855
6562
  const scriptDir = join7(homedir6(), ".agenticmail", "bin");
6856
- if (!existsSync5(scriptDir)) mkdirSync4(scriptDir, { recursive: true });
6563
+ if (!existsSync5(scriptDir)) mkdirSync3(scriptDir, { recursive: true });
6857
6564
  const script = [
6858
6565
  "#!/bin/bash",
6859
6566
  "# AgenticMail auto-start script",
@@ -6904,7 +6611,7 @@ var ServiceManager = class {
6904
6611
  `log "Starting API server: ${nodePath} ${apiEntry}"`,
6905
6612
  `exec "${nodePath}" "${apiEntry}"`
6906
6613
  ].join("\n") + "\n";
6907
- writeFileSync4(scriptPath, script, { mode: 493 });
6614
+ writeFileSync3(scriptPath, script, { mode: 493 });
6908
6615
  return scriptPath;
6909
6616
  }
6910
6617
  /**
@@ -6917,9 +6624,9 @@ var ServiceManager = class {
6917
6624
  * - Service version tracking in env vars
6918
6625
  */
6919
6626
  generatePlist(nodePath, apiEntry, configPath) {
6920
- const config = JSON.parse(readFileSync3(configPath, "utf-8"));
6627
+ const config = JSON.parse(readFileSync2(configPath, "utf-8"));
6921
6628
  const logDir = join7(homedir6(), ".agenticmail", "logs");
6922
- if (!existsSync5(logDir)) mkdirSync4(logDir, { recursive: true });
6629
+ if (!existsSync5(logDir)) mkdirSync3(logDir, { recursive: true });
6923
6630
  const version = this.getVersion();
6924
6631
  const startScript = this.generateStartScript(nodePath, apiEntry);
6925
6632
  return `<?xml version="1.0" encoding="UTF-8"?>
@@ -7015,7 +6722,7 @@ var ServiceManager = class {
7015
6722
  * - Proper dependency ordering
7016
6723
  */
7017
6724
  generateSystemdUnit(nodePath, apiEntry, configPath) {
7018
- const config = JSON.parse(readFileSync3(configPath, "utf-8"));
6725
+ const config = JSON.parse(readFileSync2(configPath, "utf-8"));
7019
6726
  const dataDir = config.dataDir || join7(homedir6(), ".agenticmail");
7020
6727
  const version = this.getVersion();
7021
6728
  const startScript = this.generateStartScript(nodePath, apiEntry);
@@ -7070,7 +6777,7 @@ WantedBy=default.target
7070
6777
  const servicePath = this.getServicePath();
7071
6778
  if (this.os === "darwin") {
7072
6779
  const dir = join7(homedir6(), "Library", "LaunchAgents");
7073
- if (!existsSync5(dir)) mkdirSync4(dir, { recursive: true });
6780
+ if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
7074
6781
  if (existsSync5(servicePath)) {
7075
6782
  try {
7076
6783
  execFileSync3("launchctl", ["unload", servicePath], { timeout: 1e4, stdio: "ignore" });
@@ -7078,7 +6785,7 @@ WantedBy=default.target
7078
6785
  }
7079
6786
  }
7080
6787
  const plist = this.generatePlist(nodePath, apiEntry, configPath);
7081
- writeFileSync4(servicePath, plist);
6788
+ writeFileSync3(servicePath, plist);
7082
6789
  try {
7083
6790
  execFileSync3("launchctl", ["load", servicePath], { timeout: 1e4, stdio: "ignore" });
7084
6791
  } catch (err) {
@@ -7087,9 +6794,9 @@ WantedBy=default.target
7087
6794
  return { installed: true, message: `Service installed at ${servicePath}` };
7088
6795
  } else if (this.os === "linux") {
7089
6796
  const dir = join7(homedir6(), ".config", "systemd", "user");
7090
- if (!existsSync5(dir)) mkdirSync4(dir, { recursive: true });
6797
+ if (!existsSync5(dir)) mkdirSync3(dir, { recursive: true });
7091
6798
  const unit = this.generateSystemdUnit(nodePath, apiEntry, configPath);
7092
- writeFileSync4(servicePath, unit);
6799
+ writeFileSync3(servicePath, unit);
7093
6800
  try {
7094
6801
  execFileSync3("systemctl", ["--user", "daemon-reload"], { timeout: 1e4, stdio: "ignore" });
7095
6802
  execFileSync3("systemctl", ["--user", "enable", SYSTEMD_UNIT], { timeout: 1e4, stdio: "ignore" });
@@ -7240,14 +6947,14 @@ var SetupManager = class {
7240
6947
  const envPath = join8(dataDir, ".env");
7241
6948
  if (existsSync6(configPath)) {
7242
6949
  try {
7243
- const existing = JSON.parse(readFileSync4(configPath, "utf-8"));
6950
+ const existing = JSON.parse(readFileSync3(configPath, "utf-8"));
7244
6951
  this.generateDockerFiles(existing);
7245
6952
  return { configPath, envPath, config: existing, isNew: false };
7246
6953
  } catch {
7247
6954
  }
7248
6955
  }
7249
6956
  if (!existsSync6(dataDir)) {
7250
- mkdirSync5(dataDir, { recursive: true });
6957
+ mkdirSync4(dataDir, { recursive: true });
7251
6958
  }
7252
6959
  const masterKey = `mk_${randomBytes2(24).toString("hex")}`;
7253
6960
  const stalwartPassword = randomBytes2(16).toString("hex");
@@ -7263,7 +6970,7 @@ var SetupManager = class {
7263
6970
  api: { port: 3100, host: "127.0.0.1" },
7264
6971
  dataDir
7265
6972
  };
7266
- writeFileSync5(configPath, JSON.stringify(config, null, 2));
6973
+ writeFileSync4(configPath, JSON.stringify(config, null, 2));
7267
6974
  chmodSync(configPath, 384);
7268
6975
  const envContent = `# Auto-generated by agenticmail setup
7269
6976
  STALWART_ADMIN_USER=admin
@@ -7279,7 +6986,7 @@ SMTP_PORT=587
7279
6986
  IMAP_HOST=localhost
7280
6987
  IMAP_PORT=143
7281
6988
  `;
7282
- writeFileSync5(envPath, envContent);
6989
+ writeFileSync4(envPath, envContent);
7283
6990
  chmodSync(envPath, 384);
7284
6991
  this.generateDockerFiles(config);
7285
6992
  return { configPath, envPath, config, isNew: true };
@@ -7291,11 +6998,11 @@ IMAP_PORT=143
7291
6998
  generateDockerFiles(config) {
7292
6999
  const dataDir = config.dataDir || join8(homedir7(), ".agenticmail");
7293
7000
  if (!existsSync6(dataDir)) {
7294
- mkdirSync5(dataDir, { recursive: true });
7001
+ mkdirSync4(dataDir, { recursive: true });
7295
7002
  }
7296
7003
  const password = config.stalwart?.adminPassword || "changeme";
7297
7004
  const composePath = join8(dataDir, "docker-compose.yml");
7298
- writeFileSync5(composePath, `services:
7005
+ writeFileSync4(composePath, `services:
7299
7006
  stalwart:
7300
7007
  image: stalwartlabs/stalwart:latest
7301
7008
  container_name: agenticmail-stalwart
@@ -7318,7 +7025,7 @@ volumes:
7318
7025
  stalwart-data:
7319
7026
  `);
7320
7027
  const tomlPath = join8(dataDir, "stalwart.toml");
7321
- writeFileSync5(tomlPath, `# Stalwart Mail Server \u2014 AgenticMail Configuration
7028
+ writeFileSync4(tomlPath, `# Stalwart Mail Server \u2014 AgenticMail Configuration
7322
7029
 
7323
7030
  [server]
7324
7031
  hostname = "localhost"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/core",
3
- "version": "0.5.24",
3
+ "version": "0.5.25",
4
4
  "description": "Core SDK for AgenticMail \u2014 email, SMS, and phone number access for AI agents",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",