md4ai 0.7.6 → 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.
- package/dist/index.bundled.js +106 -19
- package/package.json +1 -1
package/dist/index.bundled.js
CHANGED
|
@@ -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.
|
|
1235
|
+
CURRENT_VERSION = true ? "0.7.8" : "0.0.0-dev";
|
|
1236
1236
|
}
|
|
1237
1237
|
});
|
|
1238
1238
|
|
|
@@ -1563,15 +1563,24 @@ function findProcessesForConfig(config, processes) {
|
|
|
1563
1563
|
return [];
|
|
1564
1564
|
const packageName = config.args ? extractPackageName(config.args) : null;
|
|
1565
1565
|
const matches = [];
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1566
|
+
const searchTerms = [];
|
|
1567
|
+
if (packageName)
|
|
1568
|
+
searchTerms.push(packageName);
|
|
1569
|
+
if (config.command === "node" && config.args?.[0]) {
|
|
1570
|
+
searchTerms.push(config.args[0]);
|
|
1571
|
+
}
|
|
1572
|
+
if (config.name) {
|
|
1573
|
+
searchTerms.push(`mcp-server-${config.name}`);
|
|
1574
|
+
searchTerms.push(`${config.name}-mcp`);
|
|
1575
|
+
}
|
|
1576
|
+
if ((config.command === "uvx" || config.command === "pipx") && config.args) {
|
|
1577
|
+
for (const arg of config.args) {
|
|
1578
|
+
if (!arg.startsWith("-") && arg !== "run")
|
|
1579
|
+
searchTerms.push(arg);
|
|
1574
1580
|
}
|
|
1581
|
+
}
|
|
1582
|
+
for (const proc of processes) {
|
|
1583
|
+
const matched = searchTerms.some((term) => proc.args.includes(term));
|
|
1575
1584
|
if (matched) {
|
|
1576
1585
|
matches.push({
|
|
1577
1586
|
pid: proc.pid,
|
|
@@ -1582,7 +1591,15 @@ function findProcessesForConfig(config, processes) {
|
|
|
1582
1591
|
});
|
|
1583
1592
|
}
|
|
1584
1593
|
}
|
|
1585
|
-
|
|
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());
|
|
1586
1603
|
}
|
|
1587
1604
|
function getProcessTable() {
|
|
1588
1605
|
try {
|
|
@@ -1623,16 +1640,37 @@ function detectTty() {
|
|
|
1623
1640
|
}
|
|
1624
1641
|
function checkEnvVars(config) {
|
|
1625
1642
|
const required = config.env ? Object.keys(config.env) : [];
|
|
1626
|
-
const missing = required.filter((key) =>
|
|
1643
|
+
const missing = required.filter((key) => {
|
|
1644
|
+
const configValue = config.env?.[key];
|
|
1645
|
+
const hasConfigValue = configValue && !configValue.startsWith("${");
|
|
1646
|
+
return !hasConfigValue && !process.env[key];
|
|
1647
|
+
});
|
|
1627
1648
|
return { required, missing };
|
|
1628
1649
|
}
|
|
1629
|
-
function
|
|
1650
|
+
async function checkHttpServer(url) {
|
|
1651
|
+
try {
|
|
1652
|
+
const controller = new AbortController();
|
|
1653
|
+
const timeout = setTimeout(() => controller.abort(), 3e3);
|
|
1654
|
+
const res = await fetch(url, {
|
|
1655
|
+
method: "HEAD",
|
|
1656
|
+
signal: controller.signal
|
|
1657
|
+
});
|
|
1658
|
+
clearTimeout(timeout);
|
|
1659
|
+
return "reachable";
|
|
1660
|
+
} catch {
|
|
1661
|
+
return "unreachable";
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
function buildRows(configs, httpResults) {
|
|
1630
1665
|
const processes = getProcessTable();
|
|
1631
1666
|
const rows = [];
|
|
1632
1667
|
for (const config of configs) {
|
|
1633
1668
|
const { required, missing } = checkEnvVars(config);
|
|
1634
1669
|
const packageName = config.args ? extractPackageName(config.args) : null;
|
|
1635
1670
|
if (config.type === "http") {
|
|
1671
|
+
const reachability = httpResults.get(config.name) ?? "unknown";
|
|
1672
|
+
const status = reachability === "reachable" ? "running" : "stopped";
|
|
1673
|
+
const detail = reachability === "reachable" ? "HTTP \u2014 remote service reachable" : reachability === "unreachable" ? "HTTP \u2014 remote service unreachable" : "HTTP \u2014 could not verify";
|
|
1636
1674
|
rows.push({
|
|
1637
1675
|
server_name: config.name,
|
|
1638
1676
|
config_source: config.source,
|
|
@@ -1640,14 +1678,14 @@ function buildRows(configs) {
|
|
|
1640
1678
|
command: null,
|
|
1641
1679
|
package_name: null,
|
|
1642
1680
|
http_url: config.url ?? null,
|
|
1643
|
-
status
|
|
1681
|
+
status,
|
|
1644
1682
|
pid: null,
|
|
1645
1683
|
session_tty: null,
|
|
1646
1684
|
uptime_seconds: null,
|
|
1647
1685
|
memory_mb: null,
|
|
1648
1686
|
env_vars_required: required.length ? required : null,
|
|
1649
1687
|
env_vars_missing: missing.length ? missing : null,
|
|
1650
|
-
error_detail:
|
|
1688
|
+
error_detail: detail
|
|
1651
1689
|
});
|
|
1652
1690
|
continue;
|
|
1653
1691
|
}
|
|
@@ -1717,11 +1755,12 @@ function printTable(rows, deviceName) {
|
|
|
1717
1755
|
MCP Monitor \u2014 ${deviceName}`));
|
|
1718
1756
|
console.log(chalk18.dim(` ${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \xB7 refreshes every 30s \xB7 Ctrl+C to stop
|
|
1719
1757
|
`));
|
|
1720
|
-
const
|
|
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");
|
|
1721
1760
|
const stopped = rows.filter((r) => r.status === "stopped");
|
|
1722
1761
|
const errored = rows.filter((r) => r.status === "error");
|
|
1723
1762
|
const byTty = /* @__PURE__ */ new Map();
|
|
1724
|
-
for (const r of
|
|
1763
|
+
for (const r of runningLocal) {
|
|
1725
1764
|
const key = r.session_tty ?? "unknown";
|
|
1726
1765
|
const list = byTty.get(key) ?? [];
|
|
1727
1766
|
list.push(r);
|
|
@@ -1738,6 +1777,13 @@ function printTable(rows, deviceName) {
|
|
|
1738
1777
|
console.log("");
|
|
1739
1778
|
}
|
|
1740
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
|
+
}
|
|
1741
1787
|
if (stopped.length > 0 || errored.length > 0) {
|
|
1742
1788
|
const notRunning = [...stopped, ...errored];
|
|
1743
1789
|
console.log(chalk18.yellow(` Not Running (${notRunning.length})`));
|
|
@@ -1753,6 +1799,14 @@ function printTable(rows, deviceName) {
|
|
|
1753
1799
|
console.log(chalk18.dim(" Configure servers in ~/.claude/mcp.json or .mcp.json\n"));
|
|
1754
1800
|
}
|
|
1755
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
|
+
}
|
|
1756
1810
|
function formatUptime(seconds) {
|
|
1757
1811
|
if (seconds < 60)
|
|
1758
1812
|
return `${seconds}s`;
|
|
@@ -1768,6 +1822,35 @@ async function mcpWatchCommand() {
|
|
|
1768
1822
|
const deviceName = detectDeviceName();
|
|
1769
1823
|
const myPid = process.pid;
|
|
1770
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
|
+
}
|
|
1771
1854
|
process.stdout.write(`\x1B]0;${deviceName} MCP Monitor\x07`);
|
|
1772
1855
|
console.log(chalk18.blue(`Starting MCP monitor for ${deviceName}...`));
|
|
1773
1856
|
console.log("");
|
|
@@ -1784,11 +1867,15 @@ async function mcpWatchCommand() {
|
|
|
1784
1867
|
started_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1785
1868
|
last_heartbeat: (/* @__PURE__ */ new Date()).toISOString()
|
|
1786
1869
|
}, { onConflict: "device_id,pid" });
|
|
1787
|
-
const staleThreshold = new Date(Date.now() - 12e4).toISOString();
|
|
1788
|
-
await supabase.from("mcp_watchers").delete().eq("device_id", deviceId).lt("last_heartbeat", staleThreshold);
|
|
1789
1870
|
async function cycle() {
|
|
1790
1871
|
const configs = await readAllMcpConfigs();
|
|
1791
|
-
const
|
|
1872
|
+
const httpConfigs = configs.filter((c) => c.type === "http" && c.url);
|
|
1873
|
+
const httpResults = /* @__PURE__ */ new Map();
|
|
1874
|
+
await Promise.all(httpConfigs.map(async (c) => {
|
|
1875
|
+
const result = await checkHttpServer(c.url);
|
|
1876
|
+
httpResults.set(c.name, result);
|
|
1877
|
+
}));
|
|
1878
|
+
const rows = buildRows(configs, httpResults);
|
|
1792
1879
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1793
1880
|
await supabase.from("mcp_server_status").delete().eq("device_id", deviceId);
|
|
1794
1881
|
if (rows.length > 0) {
|