agenticmail 0.4.0 → 0.5.1

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 (2) hide show
  1. package/dist/cli.js +195 -11
  2. package/package.json +3 -3
package/dist/cli.js CHANGED
@@ -9,7 +9,8 @@ import { createRequire } from "module";
9
9
  import { homedir } from "os";
10
10
  import JSON5 from "json5";
11
11
  import {
12
- SetupManager
12
+ SetupManager,
13
+ ServiceManager
13
14
  } from "@agenticmail/core";
14
15
 
15
16
  // src/shell.ts
@@ -4215,6 +4216,8 @@ var c2 = {
4215
4216
  yellow: (s) => `\x1B[33m${s}\x1B[0m`,
4216
4217
  cyan: (s) => `\x1B[36m${s}\x1B[0m`,
4217
4218
  magenta: (s) => `\x1B[35m${s}\x1B[0m`,
4219
+ pink: (s) => `\x1B[38;5;205m${s}\x1B[0m`,
4220
+ pinkBg: (s) => `\x1B[48;5;205m\x1B[97m${s}\x1B[0m`,
4218
4221
  dim: (s) => `\x1B[90m${s}\x1B[0m`,
4219
4222
  bold: (s) => `\x1B[1m${s}\x1B[0m`,
4220
4223
  bgGreen: (s) => `\x1B[42m\x1B[30m${s}\x1B[0m`,
@@ -4299,6 +4302,8 @@ var Spinner = class {
4299
4302
  msgChangeCounter = 0;
4300
4303
  category;
4301
4304
  currentMsg;
4305
+ progressPct = -1;
4306
+ // -1 = no progress bar
4302
4307
  constructor(category, initialMsg) {
4303
4308
  this.category = category;
4304
4309
  const msgs = LOADING_MESSAGES[category] ?? LOADING_MESSAGES.general;
@@ -4308,13 +4313,23 @@ var Spinner = class {
4308
4313
  this.frameIdx = 0;
4309
4314
  this.msgIdx = 0;
4310
4315
  this.msgChangeCounter = 0;
4316
+ this.progressPct = -1;
4311
4317
  const msgs = LOADING_MESSAGES[this.category] ?? LOADING_MESSAGES.general;
4312
4318
  this.interval = setInterval(() => {
4313
4319
  const frame = SPINNER_FRAMES[this.frameIdx % SPINNER_FRAMES.length];
4314
- process.stdout.write(`\r ${c2.cyan(frame)} ${c2.yellow(this.currentMsg)}\x1B[K`);
4320
+ if (this.progressPct >= 0) {
4321
+ const barWidth = 20;
4322
+ const filled = Math.round(this.progressPct / 100 * barWidth);
4323
+ const empty = barWidth - filled;
4324
+ const bar = c2.pink("\u2588".repeat(filled)) + c2.dim("\u2591".repeat(empty));
4325
+ const pctStr = c2.pink(`${this.progressPct}%`);
4326
+ process.stdout.write(`\r ${c2.pink(frame)} ${bar} ${pctStr} ${c2.dim(this.currentMsg)}\x1B[K`);
4327
+ } else {
4328
+ process.stdout.write(`\r ${c2.cyan(frame)} ${c2.yellow(this.currentMsg)}\x1B[K`);
4329
+ }
4315
4330
  this.frameIdx++;
4316
4331
  this.msgChangeCounter++;
4317
- if (this.msgChangeCounter >= 30) {
4332
+ if (this.msgChangeCounter >= 30 && this.progressPct < 0) {
4318
4333
  this.msgChangeCounter = 0;
4319
4334
  this.msgIdx = (this.msgIdx + 1) % msgs.length;
4320
4335
  this.currentMsg = msgs[this.msgIdx];
@@ -4322,7 +4337,13 @@ var Spinner = class {
4322
4337
  }, 100);
4323
4338
  }
4324
4339
  update(msg) {
4325
- this.currentMsg = msg;
4340
+ const match = msg.match(/^__progress__:(\d+):(.*)$/);
4341
+ if (match) {
4342
+ this.progressPct = Math.min(100, parseInt(match[1], 10));
4343
+ this.currentMsg = match[2];
4344
+ } else {
4345
+ this.currentMsg = msg;
4346
+ }
4326
4347
  this.msgChangeCounter = 0;
4327
4348
  }
4328
4349
  succeed(msg) {
@@ -4408,6 +4429,10 @@ async function startApiServer(config) {
4408
4429
  const { spawn } = await import("child_process");
4409
4430
  const apiEntry = resolveApiEntry();
4410
4431
  const env = configToEnv(config);
4432
+ try {
4433
+ new ServiceManager().cacheApiEntryPath(apiEntry);
4434
+ } catch {
4435
+ }
4411
4436
  const child = spawn(process.execPath, [apiEntry], {
4412
4437
  detached: true,
4413
4438
  stdio: "ignore",
@@ -4542,14 +4567,41 @@ async function cmdSetup() {
4542
4567
  {
4543
4568
  const spinner = new Spinner("docker");
4544
4569
  spinner.start();
4570
+ const dockerSetup = new SetupManager((msg) => spinner.update(msg));
4545
4571
  try {
4546
- await setup.ensureDocker();
4572
+ await dockerSetup.ensureDocker();
4547
4573
  spinner.succeed(`${c2.bold("Docker")} \u2014 engine running`);
4548
4574
  } catch (err) {
4549
- spinner.fail(`Couldn't start Docker: ${err.message}`);
4550
- log2("");
4551
- log2(` ${c2.yellow("Tip:")} Install Docker manually from ${c2.cyan("https://docker.com/get-docker")}`);
4552
- log2(` ${c2.dim("Then run")} ${c2.green("agenticmail setup")} ${c2.dim("again.")}`);
4575
+ const msg = err.message;
4576
+ if (msg === "DOCKER_MANUAL_START") {
4577
+ spinner.fail(`Docker installed but couldn't start automatically`);
4578
+ log2("");
4579
+ log2(` ${c2.pink(c2.bold("Don't worry! Here's how to fix this:"))}`);
4580
+ log2("");
4581
+ log2(` ${c2.pink("Step 1:")} Open a ${c2.bold("new terminal window")}`);
4582
+ log2(` ${c2.dim("(Command + T on Mac, or open Terminal from your dock)")}`);
4583
+ log2("");
4584
+ log2(` ${c2.pink("Step 2:")} Run this command:`);
4585
+ log2(` ${c2.cyan("open -a Docker")}`);
4586
+ log2("");
4587
+ log2(` ${c2.pink("Step 3:")} Wait for Docker Desktop to fully load`);
4588
+ log2(` ${c2.dim("You'll see a whale icon in your menu bar (top of screen).")}`);
4589
+ log2(` ${c2.dim("Wait until it stops animating \u2014 that means it's ready.")}`);
4590
+ log2("");
4591
+ log2(` ${c2.pink("Step 4:")} Close that terminal window`);
4592
+ log2(` ${c2.dim("Just close the window normally (Command + W). Don't press Ctrl+C.")}`);
4593
+ log2("");
4594
+ log2(` ${c2.pink("Step 5:")} Come back here and run:`);
4595
+ log2(` ${c2.green("npx agenticmail@latest")}`);
4596
+ log2("");
4597
+ log2(` ${c2.dim("That's it! Docker only needs this manual start the first time.")}`);
4598
+ log2(` ${c2.dim("After that, it starts automatically.")}`);
4599
+ } else {
4600
+ spinner.fail(`Couldn't start Docker: ${msg}`);
4601
+ log2("");
4602
+ log2(` ${c2.yellow("Tip:")} Install Docker manually from ${c2.cyan("https://docker.com/get-docker")}`);
4603
+ log2(` ${c2.dim("Then run")} ${c2.green("agenticmail setup")} ${c2.dim("again.")}`);
4604
+ }
4553
4605
  process.exit(1);
4554
4606
  }
4555
4607
  await new Promise((r) => setTimeout(r, 300));
@@ -4558,8 +4610,9 @@ async function cmdSetup() {
4558
4610
  if (!stalwart?.installed) {
4559
4611
  const spinner = new Spinner("stalwart");
4560
4612
  spinner.start();
4613
+ const stalwartSetup = new SetupManager((msg) => spinner.update(msg));
4561
4614
  try {
4562
- await setup.ensureStalwart();
4615
+ await stalwartSetup.ensureStalwart();
4563
4616
  spinner.succeed(`${c2.bold("Mail Server")} \u2014 up and running!`);
4564
4617
  } catch (err) {
4565
4618
  spinner.fail(`Couldn't start the mail server: ${err.message}`);
@@ -4831,6 +4884,23 @@ async function cmdSetup() {
4831
4884
  log2("");
4832
4885
  await registerWithOpenClaw(result.config);
4833
4886
  }
4887
+ if (serverReady) {
4888
+ const svcSpinner = new Spinner("general", "Setting up auto-start...");
4889
+ svcSpinner.start();
4890
+ try {
4891
+ const svc = new ServiceManager();
4892
+ const svcResult = svc.install();
4893
+ if (svcResult.installed) {
4894
+ svcSpinner.succeed(`${c2.bold("Auto-start")} \u2014 AgenticMail will start on boot`);
4895
+ } else {
4896
+ svcSpinner.fail(`Auto-start: ${svcResult.message}`);
4897
+ info2("You can set this up later with: agenticmail service install");
4898
+ }
4899
+ } catch (err) {
4900
+ svcSpinner.fail(`Auto-start: ${err.message}`);
4901
+ }
4902
+ await new Promise((r) => setTimeout(r, 300));
4903
+ }
4834
4904
  printSummary(result, false);
4835
4905
  if (serverReady) {
4836
4906
  await interactiveShell({ config: result.config, onExit: () => {
@@ -6175,6 +6245,23 @@ async function cmdStatus() {
6175
6245
  info2("Can't check email status \u2014 server isn't running");
6176
6246
  }
6177
6247
  log2("");
6248
+ log2(` ${c2.bold("Auto-Start:")}`);
6249
+ try {
6250
+ const svc = new ServiceManager();
6251
+ const svcStatus = svc.status();
6252
+ if (svcStatus.installed) {
6253
+ if (svcStatus.running) {
6254
+ ok2(`Enabled ${c2.dim(`(${svcStatus.platform}) \u2014 starts on boot`)}`);
6255
+ } else {
6256
+ ok2(`Installed ${c2.dim(`(${svcStatus.platform})`)} \u2014 ${c2.yellow("not currently running")}`);
6257
+ }
6258
+ } else {
6259
+ fail2(`Not installed ${c2.dim("\u2014 run: agenticmail service install")}`);
6260
+ }
6261
+ } catch {
6262
+ info2("Could not check auto-start status");
6263
+ }
6264
+ log2("");
6178
6265
  }
6179
6266
  async function cmdStart() {
6180
6267
  const setup = new SetupManager();
@@ -6225,19 +6312,107 @@ async function cmdStart() {
6225
6312
  serverSpinner.fail(`Couldn't start the server: ${err.message}`);
6226
6313
  process.exit(1);
6227
6314
  }
6315
+ try {
6316
+ const svc = new ServiceManager();
6317
+ const svcStatus = svc.status();
6318
+ if (!svcStatus.installed) {
6319
+ const svcResult = svc.install();
6320
+ if (svcResult.installed) {
6321
+ ok2(`${c2.bold("Auto-start")} enabled \u2014 survives reboots`);
6322
+ }
6323
+ }
6324
+ } catch {
6325
+ }
6228
6326
  await interactiveShell({ config, onExit: () => {
6229
6327
  } });
6230
6328
  }
6231
6329
  async function cmdStop() {
6232
6330
  log2("");
6233
6331
  const stopped = stopApiServer();
6234
- if (stopped) {
6332
+ const svc = new ServiceManager();
6333
+ const svcStatus = svc.status();
6334
+ if (svcStatus.installed && svcStatus.running) {
6335
+ try {
6336
+ if (svcStatus.platform === "launchd") {
6337
+ const { execFileSync } = await import("child_process");
6338
+ execFileSync("launchctl", ["unload", svcStatus.servicePath], { timeout: 1e4, stdio: "ignore" });
6339
+ } else if (svcStatus.platform === "systemd") {
6340
+ const { execFileSync } = await import("child_process");
6341
+ execFileSync("systemctl", ["--user", "stop", "agenticmail.service"], { timeout: 1e4, stdio: "ignore" });
6342
+ }
6343
+ } catch {
6344
+ }
6345
+ }
6346
+ if (stopped || svcStatus.installed && svcStatus.running) {
6235
6347
  ok2("AgenticMail server stopped");
6348
+ if (svcStatus.installed) {
6349
+ info2("Auto-start is still enabled. It will restart on next boot.");
6350
+ info2(`To disable: ${c2.green("agenticmail service uninstall")}`);
6351
+ }
6236
6352
  } else {
6237
6353
  info2("Server is not running");
6238
6354
  }
6239
6355
  log2("");
6240
6356
  }
6357
+ async function cmdService() {
6358
+ const subCmd = process.argv[3] || "status";
6359
+ const svc = new ServiceManager();
6360
+ log2("");
6361
+ switch (subCmd) {
6362
+ case "install": {
6363
+ const result = svc.install();
6364
+ if (result.installed) {
6365
+ ok2(`Auto-start service installed`);
6366
+ info2(result.message);
6367
+ info2("AgenticMail will now start automatically when your computer boots.");
6368
+ } else {
6369
+ fail2(result.message);
6370
+ }
6371
+ break;
6372
+ }
6373
+ case "uninstall":
6374
+ case "remove": {
6375
+ const result = svc.uninstall();
6376
+ if (result.removed) {
6377
+ ok2("Auto-start service removed");
6378
+ info2("AgenticMail will no longer start on boot.");
6379
+ } else {
6380
+ fail2(result.message);
6381
+ }
6382
+ break;
6383
+ }
6384
+ case "reinstall": {
6385
+ const result = svc.reinstall();
6386
+ if (result.installed) {
6387
+ ok2("Auto-start service reinstalled");
6388
+ info2(result.message);
6389
+ } else {
6390
+ fail2(result.message);
6391
+ }
6392
+ break;
6393
+ }
6394
+ case "status":
6395
+ default: {
6396
+ const status = svc.status();
6397
+ log2(` ${c2.bold("Auto-Start Service")}`);
6398
+ log2("");
6399
+ if (status.installed) {
6400
+ ok2(`Installed ${c2.dim(`(${status.platform})`)}`);
6401
+ if (status.running) {
6402
+ ok2(`Running`);
6403
+ } else {
6404
+ fail2(`Not running ${c2.dim("\u2014 will start on next boot or: agenticmail service reinstall")}`);
6405
+ }
6406
+ info2(`Service file: ${status.servicePath}`);
6407
+ } else {
6408
+ fail2("Not installed");
6409
+ info2(`Install with: ${c2.green("agenticmail service install")}`);
6410
+ }
6411
+ break;
6412
+ }
6413
+ }
6414
+ log2("");
6415
+ }
6241
6416
  async function cmdUpdate() {
6242
6417
  const { execSync } = await import("child_process");
6243
6418
  log2("");
@@ -6362,6 +6537,14 @@ switch (command) {
6362
6537
  process.exit(1);
6363
6538
  });
6364
6539
  break;
6540
+ case "service":
6541
+ cmdService().then(() => {
6542
+ process.exit(0);
6543
+ }).catch((err) => {
6544
+ console.error(err);
6545
+ process.exit(1);
6546
+ });
6547
+ break;
6365
6548
  case "update":
6366
6549
  cmdUpdate().catch((err) => {
6367
6550
  console.error(err);
@@ -6381,6 +6564,7 @@ switch (command) {
6381
6564
  log2(` ${c2.green("agenticmail stop")} Stop the server`);
6382
6565
  log2(` ${c2.green("agenticmail status")} See what's running`);
6383
6566
  log2(` ${c2.green("agenticmail openclaw")} Set up AgenticMail for OpenClaw`);
6567
+ log2(` ${c2.green("agenticmail service")} Manage auto-start (install/uninstall/status)`);
6384
6568
  log2(` ${c2.green("agenticmail update")} Update to the latest version`);
6385
6569
  log2("");
6386
6570
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenticmail",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Email infrastructure for AI agents — send and receive real email programmatically",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,8 +26,8 @@
26
26
  "prepublishOnly": "npm run build"
27
27
  },
28
28
  "dependencies": {
29
- "@agenticmail/api": "^0.3.0",
30
- "@agenticmail/core": "^0.3.0",
29
+ "@agenticmail/api": "^0.5.0",
30
+ "@agenticmail/core": "^0.5.0",
31
31
  "json5": "^2.2.3"
32
32
  },
33
33
  "devDependencies": {