md4ai 0.5.2 → 0.6.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.
- package/dist/index.bundled.js +415 -8
- package/package.json +1 -1
package/dist/index.bundled.js
CHANGED
|
@@ -1001,9 +1001,9 @@ function identifyRoots(allFiles, projectRoot) {
|
|
|
1001
1001
|
return roots;
|
|
1002
1002
|
}
|
|
1003
1003
|
async function readClaudeConfigFiles(projectRoot) {
|
|
1004
|
-
const { readFile:
|
|
1005
|
-
const { join:
|
|
1006
|
-
const { existsSync:
|
|
1004
|
+
const { readFile: readFile8, stat, glob } = await import("node:fs/promises");
|
|
1005
|
+
const { join: join12, relative: relative2 } = await import("node:path");
|
|
1006
|
+
const { existsSync: existsSync11 } = await import("node:fs");
|
|
1007
1007
|
const configPatterns = [
|
|
1008
1008
|
"CLAUDE.md",
|
|
1009
1009
|
".claude/CLAUDE.md",
|
|
@@ -1013,11 +1013,11 @@ async function readClaudeConfigFiles(projectRoot) {
|
|
|
1013
1013
|
];
|
|
1014
1014
|
const files = [];
|
|
1015
1015
|
for (const pattern of configPatterns) {
|
|
1016
|
-
for await (const fullPath of glob(
|
|
1017
|
-
if (!
|
|
1016
|
+
for await (const fullPath of glob(join12(projectRoot, pattern))) {
|
|
1017
|
+
if (!existsSync11(fullPath))
|
|
1018
1018
|
continue;
|
|
1019
1019
|
try {
|
|
1020
|
-
const content = await
|
|
1020
|
+
const content = await readFile8(fullPath, "utf-8");
|
|
1021
1021
|
const fileStat = await stat(fullPath);
|
|
1022
1022
|
const lastMod = getGitLastModified(fullPath, projectRoot);
|
|
1023
1023
|
files.push({
|
|
@@ -1161,7 +1161,7 @@ function escapeHtml(text) {
|
|
|
1161
1161
|
|
|
1162
1162
|
// dist/check-update.js
|
|
1163
1163
|
import chalk8 from "chalk";
|
|
1164
|
-
var CURRENT_VERSION = "0.
|
|
1164
|
+
var CURRENT_VERSION = "0.6.1";
|
|
1165
1165
|
async function checkForUpdate() {
|
|
1166
1166
|
try {
|
|
1167
1167
|
const controller = new AbortController();
|
|
@@ -1524,8 +1524,10 @@ async function syncCommand(options) {
|
|
|
1524
1524
|
|
|
1525
1525
|
// dist/commands/link.js
|
|
1526
1526
|
import { resolve as resolve4 } from "node:path";
|
|
1527
|
-
import { hostname as hostname2, platform as platform2 } from "node:os";
|
|
1528
1527
|
import chalk13 from "chalk";
|
|
1528
|
+
|
|
1529
|
+
// dist/device-utils.js
|
|
1530
|
+
import { hostname as hostname2, platform as platform2 } from "node:os";
|
|
1529
1531
|
function detectOs2() {
|
|
1530
1532
|
const p = platform2();
|
|
1531
1533
|
if (p === "win32")
|
|
@@ -1542,6 +1544,22 @@ function detectDeviceName() {
|
|
|
1542
1544
|
const osLabel = os.charAt(0).toUpperCase() + os.slice(1);
|
|
1543
1545
|
return `${host}-${osLabel}`;
|
|
1544
1546
|
}
|
|
1547
|
+
async function resolveDeviceId(supabase, userId) {
|
|
1548
|
+
const deviceName = detectDeviceName();
|
|
1549
|
+
const osType = detectOs2();
|
|
1550
|
+
await supabase.from("devices").upsert({
|
|
1551
|
+
user_id: userId,
|
|
1552
|
+
device_name: deviceName,
|
|
1553
|
+
os_type: osType
|
|
1554
|
+
}, { onConflict: "user_id,device_name" });
|
|
1555
|
+
const { data, error } = await supabase.from("devices").select("id").eq("user_id", userId).eq("device_name", deviceName).single();
|
|
1556
|
+
if (error || !data) {
|
|
1557
|
+
throw new Error(`Failed to resolve device ID: ${error?.message ?? "not found"}`);
|
|
1558
|
+
}
|
|
1559
|
+
return data.id;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
// dist/commands/link.js
|
|
1545
1563
|
async function linkCommand(projectId) {
|
|
1546
1564
|
const { supabase, userId } = await getAuthenticatedClient();
|
|
1547
1565
|
const cwd = resolve4(process.cwd());
|
|
@@ -1982,6 +2000,394 @@ async function fetchGitHubVersions(repo, signal) {
|
|
|
1982
2000
|
return { stable, beta };
|
|
1983
2001
|
}
|
|
1984
2002
|
|
|
2003
|
+
// dist/commands/mcp-watch.js
|
|
2004
|
+
import chalk18 from "chalk";
|
|
2005
|
+
|
|
2006
|
+
// dist/mcp/read-configs.js
|
|
2007
|
+
import { readFile as readFile7 } from "node:fs/promises";
|
|
2008
|
+
import { join as join11 } from "node:path";
|
|
2009
|
+
import { homedir as homedir7 } from "node:os";
|
|
2010
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
2011
|
+
import { readdir as readdir3 } from "node:fs/promises";
|
|
2012
|
+
async function readJsonSafe(path) {
|
|
2013
|
+
try {
|
|
2014
|
+
if (!existsSync10(path))
|
|
2015
|
+
return null;
|
|
2016
|
+
const raw = await readFile7(path, "utf-8");
|
|
2017
|
+
return JSON.parse(raw);
|
|
2018
|
+
} catch {
|
|
2019
|
+
return null;
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
2022
|
+
function extractPackageName(args) {
|
|
2023
|
+
for (const arg of args) {
|
|
2024
|
+
if (arg.startsWith("-"))
|
|
2025
|
+
continue;
|
|
2026
|
+
if (arg.includes("mcp") || arg.startsWith("@"))
|
|
2027
|
+
return arg;
|
|
2028
|
+
}
|
|
2029
|
+
for (const arg of args) {
|
|
2030
|
+
if (!arg.startsWith("-"))
|
|
2031
|
+
return arg;
|
|
2032
|
+
}
|
|
2033
|
+
return null;
|
|
2034
|
+
}
|
|
2035
|
+
function parseServers(data, source) {
|
|
2036
|
+
if (!data?.mcpServers)
|
|
2037
|
+
return [];
|
|
2038
|
+
const entries = [];
|
|
2039
|
+
for (const [name, server] of Object.entries(data.mcpServers)) {
|
|
2040
|
+
const serverType = server.type === "http" ? "http" : "stdio";
|
|
2041
|
+
entries.push({
|
|
2042
|
+
name,
|
|
2043
|
+
source,
|
|
2044
|
+
type: serverType,
|
|
2045
|
+
command: server.command,
|
|
2046
|
+
args: server.args,
|
|
2047
|
+
env: server.env,
|
|
2048
|
+
url: server.url
|
|
2049
|
+
});
|
|
2050
|
+
}
|
|
2051
|
+
return entries;
|
|
2052
|
+
}
|
|
2053
|
+
function parseFlatConfig(data, source) {
|
|
2054
|
+
if (!data)
|
|
2055
|
+
return [];
|
|
2056
|
+
const entries = [];
|
|
2057
|
+
for (const [name, server] of Object.entries(data)) {
|
|
2058
|
+
if (typeof server !== "object" || server === null)
|
|
2059
|
+
continue;
|
|
2060
|
+
if (!server.command && !server.url)
|
|
2061
|
+
continue;
|
|
2062
|
+
const serverType = server.type === "http" ? "http" : "stdio";
|
|
2063
|
+
entries.push({
|
|
2064
|
+
name,
|
|
2065
|
+
source,
|
|
2066
|
+
type: serverType,
|
|
2067
|
+
command: server.command,
|
|
2068
|
+
args: server.args,
|
|
2069
|
+
env: server.env,
|
|
2070
|
+
url: server.url
|
|
2071
|
+
});
|
|
2072
|
+
}
|
|
2073
|
+
return entries;
|
|
2074
|
+
}
|
|
2075
|
+
async function readAllMcpConfigs() {
|
|
2076
|
+
const home = homedir7();
|
|
2077
|
+
const entries = [];
|
|
2078
|
+
const userConfig = await readJsonSafe(join11(home, ".claude.json"));
|
|
2079
|
+
entries.push(...parseServers(userConfig, "global"));
|
|
2080
|
+
const globalMcp = await readJsonSafe(join11(home, ".claude", "mcp.json"));
|
|
2081
|
+
entries.push(...parseServers(globalMcp, "global"));
|
|
2082
|
+
const cwdMcp = await readJsonSafe(join11(process.cwd(), ".mcp.json"));
|
|
2083
|
+
entries.push(...parseServers(cwdMcp, "project"));
|
|
2084
|
+
const pluginsBase = join11(home, ".claude", "plugins", "marketplaces");
|
|
2085
|
+
if (existsSync10(pluginsBase)) {
|
|
2086
|
+
try {
|
|
2087
|
+
const marketplaces = await readdir3(pluginsBase, { withFileTypes: true });
|
|
2088
|
+
for (const mp of marketplaces) {
|
|
2089
|
+
if (!mp.isDirectory())
|
|
2090
|
+
continue;
|
|
2091
|
+
const extDir = join11(pluginsBase, mp.name, "external_plugins");
|
|
2092
|
+
if (!existsSync10(extDir))
|
|
2093
|
+
continue;
|
|
2094
|
+
const plugins = await readdir3(extDir, { withFileTypes: true });
|
|
2095
|
+
for (const plugin of plugins) {
|
|
2096
|
+
if (!plugin.isDirectory())
|
|
2097
|
+
continue;
|
|
2098
|
+
const pluginMcp = await readJsonSafe(join11(extDir, plugin.name, ".mcp.json"));
|
|
2099
|
+
entries.push(...parseServers(pluginMcp, "plugin"));
|
|
2100
|
+
}
|
|
2101
|
+
}
|
|
2102
|
+
} catch {
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
const cacheBase = join11(home, ".claude", "plugins", "cache");
|
|
2106
|
+
if (existsSync10(cacheBase)) {
|
|
2107
|
+
try {
|
|
2108
|
+
const registries = await readdir3(cacheBase, { withFileTypes: true });
|
|
2109
|
+
for (const reg of registries) {
|
|
2110
|
+
if (!reg.isDirectory())
|
|
2111
|
+
continue;
|
|
2112
|
+
const regDir = join11(cacheBase, reg.name);
|
|
2113
|
+
const plugins = await readdir3(regDir, { withFileTypes: true });
|
|
2114
|
+
for (const plugin of plugins) {
|
|
2115
|
+
if (!plugin.isDirectory())
|
|
2116
|
+
continue;
|
|
2117
|
+
const versionDirs = await readdir3(join11(regDir, plugin.name), { withFileTypes: true });
|
|
2118
|
+
for (const ver of versionDirs) {
|
|
2119
|
+
if (!ver.isDirectory())
|
|
2120
|
+
continue;
|
|
2121
|
+
const mcpPath = join11(regDir, plugin.name, ver.name, ".mcp.json");
|
|
2122
|
+
const flatData = await readJsonSafe(mcpPath);
|
|
2123
|
+
if (flatData) {
|
|
2124
|
+
entries.push(...parseFlatConfig(flatData, "plugin"));
|
|
2125
|
+
break;
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
} catch {
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2134
|
+
const deduped = [];
|
|
2135
|
+
for (const entry of entries) {
|
|
2136
|
+
if (seen.has(entry.name))
|
|
2137
|
+
continue;
|
|
2138
|
+
seen.add(entry.name);
|
|
2139
|
+
deduped.push(entry);
|
|
2140
|
+
}
|
|
2141
|
+
return deduped;
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
// dist/mcp/scan-processes.js
|
|
2145
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
2146
|
+
function parsePsOutput(output) {
|
|
2147
|
+
const lines = output.trim().split("\n").slice(1);
|
|
2148
|
+
const entries = [];
|
|
2149
|
+
for (const line of lines) {
|
|
2150
|
+
const trimmed = line.trim();
|
|
2151
|
+
const match = trimmed.match(/^\s*(\d+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(.+)$/);
|
|
2152
|
+
if (!match)
|
|
2153
|
+
continue;
|
|
2154
|
+
entries.push({
|
|
2155
|
+
pid: parseInt(match[1], 10),
|
|
2156
|
+
tty: match[2],
|
|
2157
|
+
etimeRaw: match[3],
|
|
2158
|
+
rss: parseInt(match[4], 10),
|
|
2159
|
+
args: match[5]
|
|
2160
|
+
});
|
|
2161
|
+
}
|
|
2162
|
+
return entries;
|
|
2163
|
+
}
|
|
2164
|
+
function parseEtime(etime) {
|
|
2165
|
+
let days = 0;
|
|
2166
|
+
let rest = etime;
|
|
2167
|
+
if (rest.includes("-")) {
|
|
2168
|
+
const [d, r] = rest.split("-");
|
|
2169
|
+
days = parseInt(d, 10);
|
|
2170
|
+
rest = r;
|
|
2171
|
+
}
|
|
2172
|
+
const parts = rest.split(":").map(Number);
|
|
2173
|
+
if (parts.length === 3) {
|
|
2174
|
+
return days * 86400 + parts[0] * 3600 + parts[1] * 60 + parts[2];
|
|
2175
|
+
} else if (parts.length === 2) {
|
|
2176
|
+
return days * 86400 + parts[0] * 60 + parts[1];
|
|
2177
|
+
}
|
|
2178
|
+
return days * 86400 + (parts[0] ?? 0);
|
|
2179
|
+
}
|
|
2180
|
+
function findProcessesForConfig(config, processes) {
|
|
2181
|
+
if (config.type === "http")
|
|
2182
|
+
return [];
|
|
2183
|
+
const packageName = config.args ? extractPackageName(config.args) : null;
|
|
2184
|
+
const matches = [];
|
|
2185
|
+
for (const proc of processes) {
|
|
2186
|
+
let matched = false;
|
|
2187
|
+
if (packageName && proc.args.includes(packageName)) {
|
|
2188
|
+
matched = true;
|
|
2189
|
+
} else if (config.command === "node" && config.args?.[0]) {
|
|
2190
|
+
if (proc.args.includes(config.args[0])) {
|
|
2191
|
+
matched = true;
|
|
2192
|
+
}
|
|
2193
|
+
}
|
|
2194
|
+
if (matched) {
|
|
2195
|
+
matches.push({
|
|
2196
|
+
pid: proc.pid,
|
|
2197
|
+
tty: proc.tty === "?" ? "" : proc.tty,
|
|
2198
|
+
uptimeSeconds: parseEtime(proc.etimeRaw),
|
|
2199
|
+
memoryMb: Math.round(proc.rss / 1024),
|
|
2200
|
+
commandLine: proc.args
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2204
|
+
return matches;
|
|
2205
|
+
}
|
|
2206
|
+
function getProcessTable() {
|
|
2207
|
+
try {
|
|
2208
|
+
const output = execFileSync4("ps", ["-eo", "pid,tty,etime,rss,args"], {
|
|
2209
|
+
encoding: "utf-8",
|
|
2210
|
+
timeout: 5e3
|
|
2211
|
+
});
|
|
2212
|
+
return parsePsOutput(output);
|
|
2213
|
+
} catch {
|
|
2214
|
+
return [];
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
|
|
2218
|
+
// dist/commands/mcp-watch.js
|
|
2219
|
+
var POLL_INTERVAL_MS = 3e4;
|
|
2220
|
+
function checkEnvVars(config) {
|
|
2221
|
+
const required = config.env ? Object.keys(config.env) : [];
|
|
2222
|
+
const missing = required.filter((key) => !process.env[key]);
|
|
2223
|
+
return { required, missing };
|
|
2224
|
+
}
|
|
2225
|
+
function buildRows(configs) {
|
|
2226
|
+
const processes = getProcessTable();
|
|
2227
|
+
const rows = [];
|
|
2228
|
+
for (const config of configs) {
|
|
2229
|
+
const { required, missing } = checkEnvVars(config);
|
|
2230
|
+
const packageName = config.args ? extractPackageName(config.args) : null;
|
|
2231
|
+
if (config.type === "http") {
|
|
2232
|
+
rows.push({
|
|
2233
|
+
server_name: config.name,
|
|
2234
|
+
config_source: config.source,
|
|
2235
|
+
server_type: "http",
|
|
2236
|
+
command: null,
|
|
2237
|
+
package_name: null,
|
|
2238
|
+
http_url: config.url ?? null,
|
|
2239
|
+
status: "stopped",
|
|
2240
|
+
pid: null,
|
|
2241
|
+
session_tty: null,
|
|
2242
|
+
uptime_seconds: null,
|
|
2243
|
+
memory_mb: null,
|
|
2244
|
+
env_vars_required: required.length ? required : null,
|
|
2245
|
+
env_vars_missing: missing.length ? missing : null,
|
|
2246
|
+
error_detail: "HTTP server \u2014 cannot verify from CLI"
|
|
2247
|
+
});
|
|
2248
|
+
continue;
|
|
2249
|
+
}
|
|
2250
|
+
if (missing.length > 0) {
|
|
2251
|
+
rows.push({
|
|
2252
|
+
server_name: config.name,
|
|
2253
|
+
config_source: config.source,
|
|
2254
|
+
server_type: "stdio",
|
|
2255
|
+
command: config.command ?? null,
|
|
2256
|
+
package_name: packageName,
|
|
2257
|
+
http_url: null,
|
|
2258
|
+
status: "error",
|
|
2259
|
+
pid: null,
|
|
2260
|
+
session_tty: null,
|
|
2261
|
+
uptime_seconds: null,
|
|
2262
|
+
memory_mb: null,
|
|
2263
|
+
env_vars_required: required,
|
|
2264
|
+
env_vars_missing: missing,
|
|
2265
|
+
error_detail: `Missing env: ${missing.join(", ")}`
|
|
2266
|
+
});
|
|
2267
|
+
continue;
|
|
2268
|
+
}
|
|
2269
|
+
const matches = findProcessesForConfig(config, processes);
|
|
2270
|
+
if (matches.length === 0) {
|
|
2271
|
+
rows.push({
|
|
2272
|
+
server_name: config.name,
|
|
2273
|
+
config_source: config.source,
|
|
2274
|
+
server_type: "stdio",
|
|
2275
|
+
command: config.command ?? null,
|
|
2276
|
+
package_name: packageName,
|
|
2277
|
+
http_url: null,
|
|
2278
|
+
status: "stopped",
|
|
2279
|
+
pid: null,
|
|
2280
|
+
session_tty: null,
|
|
2281
|
+
uptime_seconds: null,
|
|
2282
|
+
memory_mb: null,
|
|
2283
|
+
env_vars_required: required.length ? required : null,
|
|
2284
|
+
env_vars_missing: null,
|
|
2285
|
+
error_detail: null
|
|
2286
|
+
});
|
|
2287
|
+
} else {
|
|
2288
|
+
for (const match of matches) {
|
|
2289
|
+
rows.push({
|
|
2290
|
+
server_name: config.name,
|
|
2291
|
+
config_source: config.source,
|
|
2292
|
+
server_type: "stdio",
|
|
2293
|
+
command: config.command ?? null,
|
|
2294
|
+
package_name: packageName,
|
|
2295
|
+
http_url: null,
|
|
2296
|
+
status: "running",
|
|
2297
|
+
pid: match.pid,
|
|
2298
|
+
session_tty: match.tty || null,
|
|
2299
|
+
uptime_seconds: match.uptimeSeconds,
|
|
2300
|
+
memory_mb: match.memoryMb,
|
|
2301
|
+
env_vars_required: required.length ? required : null,
|
|
2302
|
+
env_vars_missing: null,
|
|
2303
|
+
error_detail: null
|
|
2304
|
+
});
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
return rows;
|
|
2309
|
+
}
|
|
2310
|
+
function printTable(rows, deviceName) {
|
|
2311
|
+
process.stdout.write("\x1B[2J\x1B[H");
|
|
2312
|
+
console.log(chalk18.bold.cyan(`
|
|
2313
|
+
MCP Monitor \u2014 ${deviceName}`));
|
|
2314
|
+
console.log(chalk18.dim(` ${(/* @__PURE__ */ new Date()).toLocaleTimeString()} \xB7 refreshes every 30s \xB7 Ctrl+C to stop
|
|
2315
|
+
`));
|
|
2316
|
+
const running = rows.filter((r) => r.status === "running");
|
|
2317
|
+
const stopped = rows.filter((r) => r.status === "stopped");
|
|
2318
|
+
const errored = rows.filter((r) => r.status === "error");
|
|
2319
|
+
const byTty = /* @__PURE__ */ new Map();
|
|
2320
|
+
for (const r of running) {
|
|
2321
|
+
const key = r.session_tty ?? "unknown";
|
|
2322
|
+
const list = byTty.get(key) ?? [];
|
|
2323
|
+
list.push(r);
|
|
2324
|
+
byTty.set(key, list);
|
|
2325
|
+
}
|
|
2326
|
+
if (byTty.size > 0) {
|
|
2327
|
+
for (const [tty, servers] of byTty) {
|
|
2328
|
+
const totalMem = servers.reduce((sum, s) => sum + (s.memory_mb ?? 0), 0);
|
|
2329
|
+
console.log(chalk18.green(` Session ${tty}`) + chalk18.dim(` \u2014 ${servers.length} server${servers.length !== 1 ? "s" : ""} \xB7 ${totalMem} MB`));
|
|
2330
|
+
for (const s of servers) {
|
|
2331
|
+
const uptime = formatUptime(s.uptime_seconds ?? 0);
|
|
2332
|
+
console.log(` ${chalk18.green("\u25CF")} ${s.server_name.padEnd(20)} ${chalk18.dim((s.package_name ?? "").padEnd(30))} ${String(s.memory_mb ?? 0).padStart(4)} MB ${uptime}`);
|
|
2333
|
+
}
|
|
2334
|
+
console.log("");
|
|
2335
|
+
}
|
|
2336
|
+
}
|
|
2337
|
+
if (stopped.length > 0 || errored.length > 0) {
|
|
2338
|
+
const notRunning = [...stopped, ...errored];
|
|
2339
|
+
console.log(chalk18.yellow(` Not Running (${notRunning.length})`));
|
|
2340
|
+
for (const s of notRunning) {
|
|
2341
|
+
const icon = s.status === "error" ? chalk18.red("\u2717") : chalk18.yellow("\u25CB");
|
|
2342
|
+
const detail = s.error_detail ? chalk18.dim(` \u2014 ${s.error_detail}`) : "";
|
|
2343
|
+
console.log(` ${icon} ${s.server_name.padEnd(20)} ${chalk18.dim(s.config_source.padEnd(10))}${detail}`);
|
|
2344
|
+
}
|
|
2345
|
+
console.log("");
|
|
2346
|
+
}
|
|
2347
|
+
if (rows.length === 0) {
|
|
2348
|
+
console.log(chalk18.yellow(" No MCP servers configured."));
|
|
2349
|
+
console.log(chalk18.dim(" Configure servers in ~/.claude/mcp.json or .mcp.json\n"));
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
function formatUptime(seconds) {
|
|
2353
|
+
if (seconds < 60)
|
|
2354
|
+
return `${seconds}s`;
|
|
2355
|
+
if (seconds < 3600)
|
|
2356
|
+
return `${Math.floor(seconds / 60)}m`;
|
|
2357
|
+
const h = Math.floor(seconds / 3600);
|
|
2358
|
+
const m = Math.floor(seconds % 3600 / 60);
|
|
2359
|
+
return `${h}h ${m}m`;
|
|
2360
|
+
}
|
|
2361
|
+
async function mcpWatchCommand() {
|
|
2362
|
+
const { supabase, userId } = await getAuthenticatedClient();
|
|
2363
|
+
const deviceId = await resolveDeviceId(supabase, userId);
|
|
2364
|
+
const deviceName = detectDeviceName();
|
|
2365
|
+
console.log(chalk18.blue(`Starting MCP monitor for ${deviceName}...`));
|
|
2366
|
+
async function cycle() {
|
|
2367
|
+
const configs = await readAllMcpConfigs();
|
|
2368
|
+
const rows = buildRows(configs);
|
|
2369
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
2370
|
+
await supabase.from("mcp_server_status").delete().eq("device_id", deviceId);
|
|
2371
|
+
if (rows.length > 0) {
|
|
2372
|
+
await supabase.from("mcp_server_status").insert(rows.map((row) => ({
|
|
2373
|
+
device_id: deviceId,
|
|
2374
|
+
...row,
|
|
2375
|
+
checked_at: now
|
|
2376
|
+
})));
|
|
2377
|
+
}
|
|
2378
|
+
printTable(rows, deviceName);
|
|
2379
|
+
}
|
|
2380
|
+
await cycle();
|
|
2381
|
+
const interval = setInterval(cycle, POLL_INTERVAL_MS);
|
|
2382
|
+
const shutdown = () => {
|
|
2383
|
+
clearInterval(interval);
|
|
2384
|
+
console.log(chalk18.dim("\nMCP monitor stopped."));
|
|
2385
|
+
process.exit(0);
|
|
2386
|
+
};
|
|
2387
|
+
process.on("SIGINT", shutdown);
|
|
2388
|
+
process.on("SIGTERM", shutdown);
|
|
2389
|
+
}
|
|
2390
|
+
|
|
1985
2391
|
// dist/index.js
|
|
1986
2392
|
var program = new Command();
|
|
1987
2393
|
program.name("md4ai").description("MD4AI \u2014 Claude tooling visualiser").version(CURRENT_VERSION).addHelpText("after", `
|
|
@@ -2003,6 +2409,7 @@ program.command("print <title>").description("Generate a printable wall-chart HT
|
|
|
2003
2409
|
program.command("sync").description("Re-push latest scan data to Supabase").option("--all", "Sync all folders on all devices").action(syncCommand);
|
|
2004
2410
|
program.command("import <zipfile>").description("Import a Claude setup bundle exported from the web dashboard").action(importBundleCommand);
|
|
2005
2411
|
program.command("check-update").description("Check if a newer version of md4ai is available").action(checkForUpdate);
|
|
2412
|
+
program.command("mcp-watch").description("Monitor MCP server status on this device (runs until Ctrl+C)").action(mcpWatchCommand);
|
|
2006
2413
|
var admin = program.command("admin").description("Admin commands for managing the tools registry");
|
|
2007
2414
|
admin.command("update-tool").description("Add or update a tool in the master registry").requiredOption("--name <name>", "Canonical tool name (e.g. next, playwright)").option("--display <display>", 'Human-friendly display name (e.g. "Next.js")').option("--category <category>", "Tool category (framework|runtime|cli|mcp|package|database|other)").option("--stable <version>", "Latest stable version").option("--beta <version>", "Latest beta/RC version").option("--source <url>", "Source of truth URL for checking versions").option("--install <url>", "Download/install link").option("--notes <text>", "Compatibility notes or warnings").action(adminUpdateToolCommand);
|
|
2008
2415
|
admin.command("list-tools").description("List all tools in the master registry").action(adminListToolsCommand);
|