md4ai 0.7.7 → 0.7.8

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.
@@ -1232,7 +1232,7 @@ var CURRENT_VERSION;
1232
1232
  var init_check_update = __esm({
1233
1233
  "dist/check-update.js"() {
1234
1234
  "use strict";
1235
- CURRENT_VERSION = true ? "0.7.7" : "0.0.0-dev";
1235
+ CURRENT_VERSION = true ? "0.7.8" : "0.0.0-dev";
1236
1236
  }
1237
1237
  });
1238
1238
 
@@ -1591,7 +1591,15 @@ function findProcessesForConfig(config, processes) {
1591
1591
  });
1592
1592
  }
1593
1593
  }
1594
- return matches;
1594
+ const byTty = /* @__PURE__ */ new Map();
1595
+ for (const m of matches) {
1596
+ const key = m.tty || `pid-${m.pid}`;
1597
+ const existing = byTty.get(key);
1598
+ if (!existing || m.memoryMb > existing.memoryMb) {
1599
+ byTty.set(key, m);
1600
+ }
1601
+ }
1602
+ return Array.from(byTty.values());
1595
1603
  }
1596
1604
  function getProcessTable() {
1597
1605
  try {
@@ -1747,11 +1755,12 @@ function printTable(rows, deviceName) {
1747
1755
  MCP Monitor \u2014 ${deviceName}`));
1748
1756
  console.log(chalk18.dim(` ${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \xB7 refreshes every 30s \xB7 Ctrl+C to stop
1749
1757
  `));
1750
- const running = rows.filter((r) => r.status === "running");
1758
+ const runningLocal = rows.filter((r) => r.status === "running" && r.server_type !== "http");
1759
+ const runningHttp = rows.filter((r) => r.status === "running" && r.server_type === "http");
1751
1760
  const stopped = rows.filter((r) => r.status === "stopped");
1752
1761
  const errored = rows.filter((r) => r.status === "error");
1753
1762
  const byTty = /* @__PURE__ */ new Map();
1754
- for (const r of running) {
1763
+ for (const r of runningLocal) {
1755
1764
  const key = r.session_tty ?? "unknown";
1756
1765
  const list = byTty.get(key) ?? [];
1757
1766
  list.push(r);
@@ -1768,6 +1777,13 @@ function printTable(rows, deviceName) {
1768
1777
  console.log("");
1769
1778
  }
1770
1779
  }
1780
+ if (runningHttp.length > 0) {
1781
+ console.log(chalk18.blue(` Remote Services (${runningHttp.length})`) + chalk18.dim(" \u2014 HTTP endpoints reachable"));
1782
+ for (const s of runningHttp) {
1783
+ console.log(` ${chalk18.blue("\u25CF")} ${s.server_name.padEnd(20)} ${chalk18.dim((s.http_url ?? "").padEnd(30))}`);
1784
+ }
1785
+ console.log("");
1786
+ }
1771
1787
  if (stopped.length > 0 || errored.length > 0) {
1772
1788
  const notRunning = [...stopped, ...errored];
1773
1789
  console.log(chalk18.yellow(` Not Running (${notRunning.length})`));
@@ -1783,6 +1799,14 @@ function printTable(rows, deviceName) {
1783
1799
  console.log(chalk18.dim(" Configure servers in ~/.claude/mcp.json or .mcp.json\n"));
1784
1800
  }
1785
1801
  }
1802
+ function timeAgo(dateStr) {
1803
+ const diff = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1e3);
1804
+ if (diff < 60)
1805
+ return `${diff}s ago`;
1806
+ if (diff < 3600)
1807
+ return `${Math.floor(diff / 60)}m ago`;
1808
+ return `${Math.floor(diff / 3600)}h ago`;
1809
+ }
1786
1810
  function formatUptime(seconds) {
1787
1811
  if (seconds < 60)
1788
1812
  return `${seconds}s`;
@@ -1798,6 +1822,35 @@ async function mcpWatchCommand() {
1798
1822
  const deviceName = detectDeviceName();
1799
1823
  const myPid = process.pid;
1800
1824
  const myTty = detectTty();
1825
+ const staleThreshold = new Date(Date.now() - 12e4).toISOString();
1826
+ await supabase.from("mcp_watchers").delete().eq("device_id", deviceId).lt("last_heartbeat", staleThreshold);
1827
+ const { data: existingWatchers } = await supabase.from("mcp_watchers").select("pid, tty, cli_version, started_at").eq("device_id", deviceId);
1828
+ if (existingWatchers && existingWatchers.length > 0) {
1829
+ const { confirm: confirm2 } = await import("@inquirer/prompts");
1830
+ console.log("");
1831
+ console.log(chalk18.yellow(" Another watcher is already running on this device:"));
1832
+ for (const w of existingWatchers) {
1833
+ console.log(chalk18.dim(` PID ${w.pid} \xB7 ${w.tty ?? "unknown tty"} \xB7 v${w.cli_version} \xB7 started ${timeAgo(w.started_at)}`));
1834
+ }
1835
+ console.log("");
1836
+ const takeOver = await confirm2({
1837
+ message: "Stop the existing watcher and start a new one?",
1838
+ default: true
1839
+ });
1840
+ if (!takeOver) {
1841
+ console.log(chalk18.dim("\nKeeping existing watcher. Exiting.\n"));
1842
+ return;
1843
+ }
1844
+ for (const w of existingWatchers) {
1845
+ try {
1846
+ process.kill(w.pid, "SIGTERM");
1847
+ } catch {
1848
+ }
1849
+ }
1850
+ await supabase.from("mcp_watchers").delete().eq("device_id", deviceId);
1851
+ await new Promise((r) => setTimeout(r, 1e3));
1852
+ console.log(chalk18.dim(" Previous watcher stopped.\n"));
1853
+ }
1801
1854
  process.stdout.write(`\x1B]0;${deviceName} MCP Monitor\x07`);
1802
1855
  console.log(chalk18.blue(`Starting MCP monitor for ${deviceName}...`));
1803
1856
  console.log("");
@@ -1814,8 +1867,6 @@ async function mcpWatchCommand() {
1814
1867
  started_at: (/* @__PURE__ */ new Date()).toISOString(),
1815
1868
  last_heartbeat: (/* @__PURE__ */ new Date()).toISOString()
1816
1869
  }, { onConflict: "device_id,pid" });
1817
- const staleThreshold = new Date(Date.now() - 12e4).toISOString();
1818
- await supabase.from("mcp_watchers").delete().eq("device_id", deviceId).lt("last_heartbeat", staleThreshold);
1819
1870
  async function cycle() {
1820
1871
  const configs = await readAllMcpConfigs();
1821
1872
  const httpConfigs = configs.filter((c) => c.type === "http" && c.url);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "md4ai",
3
- "version": "0.7.7",
3
+ "version": "0.7.8",
4
4
  "description": "CLI for MD4AI — scan Claude projects and sync to your dashboard",
5
5
  "type": "module",
6
6
  "bin": {