agenticmail 0.3.24 → 0.3.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 (2) hide show
  1. package/dist/cli.js +86 -126
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { createInterface as createInterface2, emitKeypressEvents as emitKeypressEvents2 } from "readline";
5
- import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync, realpathSync } from "fs";
5
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync, realpathSync, unlinkSync } from "fs";
6
6
  import { join, dirname } from "path";
7
7
  import { fileURLToPath } from "url";
8
8
  import { createRequire } from "module";
@@ -4054,22 +4054,53 @@ async function waitForApi(host, port, timeoutMs = 15e3) {
4054
4054
  }
4055
4055
  return false;
4056
4056
  }
4057
- var apiChild = null;
4058
- function cleanupChild() {
4059
- if (apiChild) {
4060
- apiChild.kill();
4061
- apiChild = null;
4057
+ var PID_FILE = join(homedir(), ".agenticmail", "server.pid");
4058
+ async function startApiServer(config) {
4059
+ const host = config.api.host;
4060
+ const port = config.api.port;
4061
+ try {
4062
+ const probe = await fetch(`http://${host}:${port}/api/agenticmail/health`, {
4063
+ signal: AbortSignal.timeout(2e3)
4064
+ });
4065
+ if (probe.ok) return true;
4066
+ } catch {
4067
+ }
4068
+ const { spawn } = await import("child_process");
4069
+ const apiEntry = resolveApiEntry();
4070
+ const env = configToEnv(config);
4071
+ const child = spawn(process.execPath, [apiEntry], {
4072
+ detached: true,
4073
+ stdio: "ignore",
4074
+ env
4075
+ });
4076
+ child.unref();
4077
+ if (child.pid) {
4078
+ try {
4079
+ writeFileSync2(PID_FILE, String(child.pid));
4080
+ } catch {
4081
+ }
4082
+ }
4083
+ return waitForApi(host, port);
4084
+ }
4085
+ function stopApiServer() {
4086
+ try {
4087
+ if (!existsSync2(PID_FILE)) return false;
4088
+ const pid = parseInt(readFileSync2(PID_FILE, "utf-8").trim(), 10);
4089
+ if (isNaN(pid)) return false;
4090
+ process.kill(pid, "SIGTERM");
4091
+ try {
4092
+ unlinkSync(PID_FILE);
4093
+ } catch {
4094
+ }
4095
+ return true;
4096
+ } catch {
4097
+ try {
4098
+ unlinkSync(PID_FILE);
4099
+ } catch {
4100
+ }
4101
+ return false;
4062
4102
  }
4063
4103
  }
4064
- process.on("exit", cleanupChild);
4065
- process.on("SIGINT", () => {
4066
- cleanupChild();
4067
- process.exit(0);
4068
- });
4069
- process.on("SIGTERM", () => {
4070
- cleanupChild();
4071
- process.exit(0);
4072
- });
4073
4104
  async function cmdSetup() {
4074
4105
  log2("");
4075
4106
  log2(` ${c2.bgCyan(" AgenticMail Setup ")}`);
@@ -4258,51 +4289,14 @@ async function cmdSetup() {
4258
4289
  serverSpinner.start();
4259
4290
  let serverReady = false;
4260
4291
  try {
4261
- const probe = await fetch(`http://${result.config.api.host}:${result.config.api.port}/api/agenticmail/health`, {
4262
- signal: AbortSignal.timeout(2e3)
4263
- });
4264
- if (probe.ok) serverReady = true;
4265
- } catch {
4266
- }
4267
- if (serverReady) {
4268
- serverSpinner.succeed(`Server already running at ${c2.cyan(`http://${result.config.api.host}:${result.config.api.port}`)}`);
4269
- } else {
4270
- try {
4271
- const { fork } = await import("child_process");
4272
- const apiEntry = resolveApiEntry();
4273
- const env = configToEnv(result.config);
4274
- apiChild = fork(apiEntry, [], { stdio: ["ignore", "ignore", "pipe", "ipc"], env });
4275
- const stderrLines = [];
4276
- apiChild.stderr?.on("data", (chunk) => {
4277
- const lines = chunk.toString().trim().split("\n");
4278
- for (const line of lines) {
4279
- stderrLines.push(line);
4280
- if (stderrLines.length > 50) stderrLines.shift();
4281
- }
4282
- });
4283
- apiChild.on("exit", (code, signal) => {
4284
- apiChild = null;
4285
- log2("");
4286
- fail2(`Server stopped unexpectedly${signal ? ` (signal: ${signal})` : code ? ` (exit code: ${code})` : ""}`);
4287
- if (stderrLines.length > 0) {
4288
- log2("");
4289
- log2(` ${c2.dim("Last server output:")}`);
4290
- for (const line of stderrLines.slice(-10)) {
4291
- log2(` ${c2.dim(line)}`);
4292
- }
4293
- }
4294
- log2("");
4295
- process.exit(code ?? 1);
4296
- });
4297
- serverReady = await waitForApi(result.config.api.host, result.config.api.port);
4298
- if (serverReady) {
4299
- serverSpinner.succeed(`Server running at ${c2.cyan(`http://${result.config.api.host}:${result.config.api.port}`)}`);
4300
- } else {
4301
- serverSpinner.fail("Server did not start in time");
4302
- }
4303
- } catch (err) {
4304
- serverSpinner.fail(`Could not start server: ${err.message}`);
4292
+ serverReady = await startApiServer(result.config);
4293
+ if (serverReady) {
4294
+ serverSpinner.succeed(`Server running at ${c2.cyan(`http://${result.config.api.host}:${result.config.api.port}`)}`);
4295
+ } else {
4296
+ serverSpinner.fail("Server did not start in time");
4305
4297
  }
4298
+ } catch (err) {
4299
+ serverSpinner.fail(`Could not start server: ${err.message}`);
4306
4300
  }
4307
4301
  let existingEmail = null;
4308
4302
  let existingProvider = null;
@@ -4363,7 +4357,6 @@ async function cmdSetup() {
4363
4357
  if (choice === "1" || choice === "2") {
4364
4358
  if (!serverReady) {
4365
4359
  info2("You can configure email later by running: agenticmail setup");
4366
- cleanupChild();
4367
4360
  printSummary(result, true);
4368
4361
  return;
4369
4362
  }
@@ -4378,7 +4371,6 @@ async function cmdSetup() {
4378
4371
  if (!emailOk) {
4379
4372
  log2("");
4380
4373
  info2("Email setup did not complete. Run " + c2.green("npx agenticmail setup") + " again to retry.");
4381
- cleanupChild();
4382
4374
  printSummary(result, true);
4383
4375
  return;
4384
4376
  }
@@ -4393,7 +4385,8 @@ async function cmdSetup() {
4393
4385
  }
4394
4386
  printSummary(result, false);
4395
4387
  if (serverReady) {
4396
- await interactiveShell({ config: result.config, onExit: cleanupChild });
4388
+ await interactiveShell({ config: result.config, onExit: () => {
4389
+ } });
4397
4390
  }
4398
4391
  }
4399
4392
  function printSummary(result, exitAfter) {
@@ -5057,17 +5050,9 @@ async function cmdOpenClaw() {
5057
5050
  const serverSpinner = new Spinner("server", "Starting the server...");
5058
5051
  serverSpinner.start();
5059
5052
  try {
5060
- const { fork } = await import("child_process");
5061
- const apiEntry = resolveApiEntry();
5062
- const env = configToEnv(config);
5063
- apiChild = fork(apiEntry, [], { stdio: ["ignore", "ignore", "pipe", "ipc"], env });
5064
- apiChild.on("exit", () => {
5065
- apiChild = null;
5066
- });
5067
- const ready = await waitForApi(apiHost, apiPort);
5053
+ const ready = await startApiServer(config);
5068
5054
  if (!ready) {
5069
5055
  serverSpinner.fail("Server did not start in time");
5070
- cleanupChild();
5071
5056
  process.exit(1);
5072
5057
  }
5073
5058
  serverSpinner.succeed(`Server running at ${c2.cyan(apiBase)}`);
@@ -5387,11 +5372,8 @@ async function cmdOpenClaw() {
5387
5372
  }
5388
5373
  log2("");
5389
5374
  if (process.stdin.isTTY) {
5390
- await interactiveShell({ config, onExit: cleanupChild });
5391
- } else {
5392
- if (!serverWasRunning) {
5393
- cleanupChild();
5394
- }
5375
+ await interactiveShell({ config, onExit: () => {
5376
+ } });
5395
5377
  }
5396
5378
  }
5397
5379
  function printPluginSnippet(apiUrl, masterKey, agentApiKey) {
@@ -5532,61 +5514,30 @@ async function cmdStart() {
5532
5514
  }
5533
5515
  const serverSpinner = new Spinner("server", "Launching your server...");
5534
5516
  serverSpinner.start();
5535
- let alreadyRunning = false;
5536
5517
  try {
5537
- const probe = await fetch(`http://${config.api.host}:${config.api.port}/api/agenticmail/health`, {
5538
- signal: AbortSignal.timeout(2e3)
5539
- });
5540
- if (probe.ok) alreadyRunning = true;
5541
- } catch {
5542
- }
5543
- if (alreadyRunning) {
5544
- serverSpinner.succeed(`Server already running at ${c2.cyan(`http://${config.api.host}:${config.api.port}`)}`);
5545
- } else {
5546
- try {
5547
- const { fork } = await import("child_process");
5548
- const apiEntry = resolveApiEntry();
5549
- if (!existsSync2(apiEntry)) {
5550
- serverSpinner.fail(`Server isn't built yet. Run: ${c2.bold("npm run build")}`);
5551
- process.exit(1);
5552
- }
5553
- const env = configToEnv(config);
5554
- apiChild = fork(apiEntry, [], { stdio: ["ignore", "ignore", "pipe", "ipc"], env });
5555
- const stderrLines = [];
5556
- apiChild.stderr?.on("data", (chunk) => {
5557
- const lines = chunk.toString().trim().split("\n");
5558
- for (const line of lines) {
5559
- stderrLines.push(line);
5560
- if (stderrLines.length > 50) stderrLines.shift();
5561
- }
5562
- });
5563
- apiChild.on("exit", (code, signal) => {
5564
- apiChild = null;
5565
- log2("");
5566
- fail2(`Server stopped unexpectedly${signal ? ` (signal: ${signal})` : code ? ` (exit code: ${code})` : ""}`);
5567
- if (stderrLines.length > 0) {
5568
- log2("");
5569
- log2(` ${c2.dim("Last server output:")}`);
5570
- for (const line of stderrLines.slice(-10)) {
5571
- log2(` ${c2.dim(line)}`);
5572
- }
5573
- }
5574
- log2("");
5575
- process.exit(code ?? 1);
5576
- });
5577
- const ready = await waitForApi(config.api.host, config.api.port, 2e4);
5578
- if (!ready) {
5579
- serverSpinner.fail("Server did not start in time");
5580
- cleanupChild();
5581
- process.exit(1);
5582
- }
5518
+ const ready = await startApiServer(config);
5519
+ if (ready) {
5583
5520
  serverSpinner.succeed(`Server running at ${c2.cyan(`http://${config.api.host}:${config.api.port}`)}`);
5584
- } catch (err) {
5585
- serverSpinner.fail(`Couldn't start the server: ${err.message}`);
5521
+ } else {
5522
+ serverSpinner.fail("Server did not start in time");
5586
5523
  process.exit(1);
5587
5524
  }
5525
+ } catch (err) {
5526
+ serverSpinner.fail(`Couldn't start the server: ${err.message}`);
5527
+ process.exit(1);
5588
5528
  }
5589
- await interactiveShell({ config, onExit: cleanupChild });
5529
+ await interactiveShell({ config, onExit: () => {
5530
+ } });
5531
+ }
5532
+ async function cmdStop() {
5533
+ log2("");
5534
+ const stopped = stopApiServer();
5535
+ if (stopped) {
5536
+ ok2("AgenticMail server stopped");
5537
+ } else {
5538
+ info2("Server is not running");
5539
+ }
5540
+ log2("");
5590
5541
  }
5591
5542
  var command = process.argv[2];
5592
5543
  switch (command) {
@@ -5602,6 +5553,14 @@ switch (command) {
5602
5553
  process.exit(1);
5603
5554
  });
5604
5555
  break;
5556
+ case "stop":
5557
+ cmdStop().then(() => {
5558
+ process.exit(0);
5559
+ }).catch((err) => {
5560
+ console.error(err);
5561
+ process.exit(1);
5562
+ });
5563
+ break;
5605
5564
  case "status":
5606
5565
  cmdStatus().then(() => {
5607
5566
  process.exit(0);
@@ -5626,6 +5585,7 @@ switch (command) {
5626
5585
  log2(` ${c2.green("agenticmail")} Get started (setup + start)`);
5627
5586
  log2(` ${c2.green("agenticmail setup")} Re-run the setup wizard`);
5628
5587
  log2(` ${c2.green("agenticmail start")} Start the server`);
5588
+ log2(` ${c2.green("agenticmail stop")} Stop the server`);
5629
5589
  log2(` ${c2.green("agenticmail status")} See what's running`);
5630
5590
  log2(` ${c2.green("agenticmail openclaw")} Set up AgenticMail for OpenClaw`);
5631
5591
  log2("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenticmail",
3
- "version": "0.3.24",
3
+ "version": "0.3.25",
4
4
  "description": "Email infrastructure for AI agents — send and receive real email programmatically",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",