@jait/gateway 0.1.500 → 0.1.501

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/bin/jait.mjs +148 -14
  2. package/package.json +1 -1
package/bin/jait.mjs CHANGED
@@ -368,29 +368,163 @@ async function cmdStart(cliFlags) {
368
368
  console.log("");
369
369
  }
370
370
 
371
- function cmdStop() {
371
+ async function cmdStop() {
372
372
  printBanner();
373
+
374
+ const port = process.env.PORT || flags.port || "8000";
375
+
376
+ // Case 1: Process tracked via PID file
373
377
  const tracked = getTrackedProcess();
374
- if (!tracked) {
375
- console.log(" Jait is not running (no PID file found).");
378
+ if (tracked) {
379
+ const { pid, pidPath } = tracked;
380
+ try {
381
+ if (platform() === "win32") {
382
+ execSync(`taskkill /PID ${pid} /T /F`, { stdio: "ignore" });
383
+ } else {
384
+ process.kill(Number(pid), "SIGTERM");
385
+ }
386
+ console.log(` Sent stop signal to PID ${pid}.`);
387
+ } catch (err) {
388
+ console.error(` Failed to stop process ${pid}: ${err.message}`);
389
+ }
390
+ cleanupPidFile(pidPath);
376
391
  console.log("");
377
- process.exit(1);
392
+ return;
378
393
  }
379
394
 
380
- const { pid, pidPath } = tracked;
395
+ // Case 2: systemd unit is active
396
+ if (platform() === "linux") {
397
+ let unitActive = false;
398
+ try {
399
+ const status = runSilent(`systemctl --user is-active ${SERVICE_NAME} 2>/dev/null`);
400
+ if (status.trim() === "active") {
401
+ console.log(` No PID file found. Stopping via systemd...`);
402
+ try {
403
+ run(`systemctl --user stop ${SERVICE_NAME}`, { silent: true });
404
+ console.log(` ${SERVICE_NAME} stopped.`);
405
+ console.log("");
406
+ return;
407
+ } catch (err) {
408
+ console.error(` Failed to stop via systemd: ${err.message}`);
409
+ }
410
+ unitActive = true;
411
+ }
412
+ } catch {}
413
+
414
+ if (!unitActive) {
415
+ // Case 3: check if port is actually answering /health — kill that process
416
+ const health = await healthCheck(port);
417
+ if (health) {
418
+ console.log(` No PID file found, but gateway is healthy on port ${port}.`);
419
+
420
+ // Find the PID listening on the port
421
+ let targetPids = [];
422
+ try {
423
+ const lsofOut = runSilent(
424
+ `lsof -ti:${port} 2>/dev/null`
425
+ );
426
+ targetPids = lsofOut
427
+ ? lsofOut.split("\n").filter(Boolean).map(Number)
428
+ : [];
429
+ } catch {}
430
+
431
+ // Also try ss/fuser as fallback
432
+ if (targetPids.length === 0) {
433
+ try {
434
+ const fuserOut = runSilent(
435
+ `fuser ${port}/tcp 2>/dev/null`
436
+ );
437
+ targetPids = fuserOut
438
+ ? fuserOut.split(/\s+/).map(Number)
439
+ : [];
440
+ } catch {}
441
+ }
381
442
 
382
- try {
383
- if (platform() === "win32") {
384
- execSync(`taskkill /PID ${pid} /T /F`, { stdio: "ignore" });
385
- } else {
386
- process.kill(Number(pid), "SIGTERM");
443
+ let stopped = false;
444
+ for (const pid of targetPids) {
445
+ const exePath = "/proc/" + pid + "/exe";
446
+ try {
447
+ if (!existsSync(exePath)) continue;
448
+ const exeTarget = readFileSync(exePath, "utf8");
449
+ if (exeTarget.includes("node") || exeTarget.includes("jait")) {
450
+ try {
451
+ process.kill(pid, "SIGTERM");
452
+ console.log(` Sent stop signal to PID ${pid}.`);
453
+ stopped = true;
454
+ } catch (err) {
455
+ // already dead
456
+ }
457
+ }
458
+ } catch {}
459
+ }
460
+
461
+ if (!stopped) {
462
+ // Last resort: kill all processes on the port that are owned by this user
463
+ for (const pid of targetPids) {
464
+ try {
465
+ process.kill(pid, "SIGTERM");
466
+ console.log(` Sent stop signal to PID ${pid}.`);
467
+ stopped = true;
468
+ } catch {}
469
+ }
470
+ }
471
+
472
+ if (!stopped) {
473
+ console.log(` Could not find process to stop. Try 'sudo lsof -i :${port}' and kill manually.`);
474
+ } else {
475
+ // Clean up stale PID file if one exists (from a prior crash)
476
+ for (const p of [PID_PATH, LEGACY_PID_PATH]) {
477
+ if (existsSync(p)) cleanupPidFile(p);
478
+ }
479
+ }
480
+
481
+ console.log("");
482
+ return;
483
+ }
484
+
485
+ // Nothing found: no PID file, no systemd unit, port not in use
486
+ const reachable = await isPortReachable(port);
487
+ if (reachable) {
488
+ console.log(` Port ${port} is in use, but health check timed out.`);
489
+ console.log(` Another process may be listening. Run 'lsof -i :${port}' to inspect.`);
490
+ } else {
491
+ console.log(" Jait is not running (no PID file found).");
492
+ }
493
+ console.log("");
494
+ return;
387
495
  }
388
- console.log(` Sent stop signal to PID ${pid}.`);
389
- } catch (err) {
390
- console.error(` Failed to stop process ${pid}: ${err.message}`);
496
+ return;
391
497
  }
392
498
 
393
- cleanupPidFile(pidPath);
499
+ // Non-Linux: port check fallback
500
+ const reachable = await isPortReachable(port);
501
+ if (reachable) {
502
+ const health = await healthCheck(port);
503
+ if (health) {
504
+ console.log(` No PID file found, but gateway is healthy on port ${port}.`);
505
+ let targetPids = [];
506
+ try {
507
+ targetPids = runSilent(
508
+ platform() === "darwin"
509
+ ? `lsof -ti:${port} 2>/dev/null`
510
+ : `netstat -tlnp | grep :${port} | awk '{print $NF}' | cut -d/ -f1 | uniq'`
511
+ ).split("\n").filter(Boolean).map(Number);
512
+ } catch {}
513
+
514
+ for (const pid of targetPids) {
515
+ try {
516
+ process.kill(pid, "SIGTERM");
517
+ console.log(` Sent stop signal to PID ${pid}.`);
518
+ } catch {}
519
+ }
520
+ } else {
521
+ console.log(` Port ${port} is in use but not a Jait health endpoint.`);
522
+ }
523
+ } else {
524
+ console.log(" Jait is not running (no PID file found).");
525
+ console.log("");
526
+ process.exit(1);
527
+ }
394
528
  console.log("");
395
529
  }
396
530
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jait/gateway",
3
- "version": "0.1.500",
3
+ "version": "0.1.501",
4
4
  "description": "Jait AI gateway — local-first AI coding agent with terminal, filesystem, and browser control",
5
5
  "author": "JakobWl",
6
6
  "type": "module",