@usex/mikrotik-mcp 4.21.0 → 4.22.0
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/cli.js
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
LOGO_URL,
|
|
12
12
|
MikrotikConfigSchema,
|
|
13
13
|
PKG_META,
|
|
14
|
+
PROJECT_ROOT,
|
|
14
15
|
PROMPTS_DIR,
|
|
15
16
|
REDACTED,
|
|
16
17
|
SERVER_DESCRIPTION,
|
|
@@ -124,7 +125,7 @@ import {
|
|
|
124
125
|
updateAaaEntity,
|
|
125
126
|
updateSummaryLine,
|
|
126
127
|
writeBackup
|
|
127
|
-
} from "./shared/cli-
|
|
128
|
+
} from "./shared/cli-8ecncq7y.js";
|
|
128
129
|
|
|
129
130
|
// src/cli.ts
|
|
130
131
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -132,8 +133,8 @@ import { existsSync as existsSync2 } from "fs";
|
|
|
132
133
|
// src/observability/dashboard.ts
|
|
133
134
|
import { spawn } from "child_process";
|
|
134
135
|
import { readFileSync as readFileSync3 } from "fs";
|
|
135
|
-
import { homedir, networkInterfaces } from "os";
|
|
136
|
-
import { dirname as dirname5, join as
|
|
136
|
+
import { homedir as homedir2, networkInterfaces } from "os";
|
|
137
|
+
import { dirname as dirname5, join as join4 } from "path";
|
|
137
138
|
var {serve } = globalThis.Bun;
|
|
138
139
|
import { z as z2 } from "zod";
|
|
139
140
|
|
|
@@ -815,6 +816,142 @@ function stopHealthChecks() {
|
|
|
815
816
|
}
|
|
816
817
|
}
|
|
817
818
|
|
|
819
|
+
// src/observability/geo.ts
|
|
820
|
+
import { lookup } from "dns/promises";
|
|
821
|
+
var LOG_TAG2 = "mikrotik-mcp";
|
|
822
|
+
var cache = new Map;
|
|
823
|
+
var REFRESH_MS = 24 * 60 * 60000;
|
|
824
|
+
var timer2 = null;
|
|
825
|
+
var inFlight2 = false;
|
|
826
|
+
function getDeviceGeo(name) {
|
|
827
|
+
return cache.get(name)?.geo ?? null;
|
|
828
|
+
}
|
|
829
|
+
var IPV4_RE = /^\d{1,3}(?:\.\d{1,3}){3}$/;
|
|
830
|
+
var PRIVATE_RE = /^(?:10\.|127\.|0\.|169\.254\.|192\.168\.|172\.(?:1[6-9]|2\d|3[01])\.|100\.(?:6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.|::1$|f[cd])/i;
|
|
831
|
+
function isIpLiteral(host) {
|
|
832
|
+
return IPV4_RE.test(host) || host.includes(":");
|
|
833
|
+
}
|
|
834
|
+
async function publicIpOf(host) {
|
|
835
|
+
let ip = host;
|
|
836
|
+
if (!isIpLiteral(host)) {
|
|
837
|
+
try {
|
|
838
|
+
ip = (await lookup(host)).address;
|
|
839
|
+
} catch {
|
|
840
|
+
return null;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
return PRIVATE_RE.test(ip) ? null : ip;
|
|
844
|
+
}
|
|
845
|
+
function toGeo(country, code, city) {
|
|
846
|
+
if (!code)
|
|
847
|
+
return null;
|
|
848
|
+
return { countryCode: code.toLowerCase(), country: country ?? code, city: city || undefined };
|
|
849
|
+
}
|
|
850
|
+
async function fetchIpkit(ip) {
|
|
851
|
+
const res = await fetch(`https://ipkit.ir/${ip}`, {
|
|
852
|
+
headers: { accept: "application/json" },
|
|
853
|
+
signal: AbortSignal.timeout(8000)
|
|
854
|
+
});
|
|
855
|
+
if (!res.ok)
|
|
856
|
+
throw new Error(`ipkit HTTP ${res.status}`);
|
|
857
|
+
const d = await res.json();
|
|
858
|
+
if (d.is_private)
|
|
859
|
+
return null;
|
|
860
|
+
return toGeo(d.country, d.country_code, d.city);
|
|
861
|
+
}
|
|
862
|
+
async function fetchIpquery(ip) {
|
|
863
|
+
const res = await fetch(`https://api.ipquery.io/${ip}`, { signal: AbortSignal.timeout(8000) });
|
|
864
|
+
if (!res.ok)
|
|
865
|
+
throw new Error(`ipquery HTTP ${res.status}`);
|
|
866
|
+
const d = await res.json();
|
|
867
|
+
return toGeo(d.location?.country, d.location?.country_code, d.location?.city);
|
|
868
|
+
}
|
|
869
|
+
async function fetchGeo(ip) {
|
|
870
|
+
try {
|
|
871
|
+
return await fetchIpkit(ip);
|
|
872
|
+
} catch (e) {
|
|
873
|
+
logger.warn(`[${LOG_TAG2}] ipkit geo failed for ${ip}, trying ipquery: ${e instanceof Error ? e.message : String(e)}`);
|
|
874
|
+
}
|
|
875
|
+
try {
|
|
876
|
+
return await fetchIpquery(ip);
|
|
877
|
+
} catch (e) {
|
|
878
|
+
logger.warn(`[${LOG_TAG2}] geo lookup failed for ${ip}: ${e instanceof Error ? e.message : String(e)}`);
|
|
879
|
+
return null;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
async function resolveDevice(name, host) {
|
|
883
|
+
const ip = host ? await publicIpOf(host) : null;
|
|
884
|
+
const geo = ip ? await fetchGeo(ip) : null;
|
|
885
|
+
cache.set(name, { geo, at: Date.now() });
|
|
886
|
+
}
|
|
887
|
+
async function refreshGeo() {
|
|
888
|
+
if (inFlight2)
|
|
889
|
+
return;
|
|
890
|
+
inFlight2 = true;
|
|
891
|
+
try {
|
|
892
|
+
const now = Date.now();
|
|
893
|
+
await Promise.all(Object.entries(getConfig().devices).map(([name, dc]) => {
|
|
894
|
+
const c = cache.get(name);
|
|
895
|
+
if (c && now - c.at < REFRESH_MS)
|
|
896
|
+
return Promise.resolve();
|
|
897
|
+
return resolveDevice(name, dc.mac ? undefined : dc.host);
|
|
898
|
+
}));
|
|
899
|
+
} finally {
|
|
900
|
+
inFlight2 = false;
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
function startGeoLookups() {
|
|
904
|
+
refreshGeo();
|
|
905
|
+
timer2 = setInterval(() => void refreshGeo(), REFRESH_MS);
|
|
906
|
+
}
|
|
907
|
+
function stopGeoLookups() {
|
|
908
|
+
if (timer2) {
|
|
909
|
+
clearInterval(timer2);
|
|
910
|
+
timer2 = null;
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// src/observability/flags.ts
|
|
915
|
+
import { join as join3 } from "path";
|
|
916
|
+
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
917
|
+
import { homedir } from "os";
|
|
918
|
+
var LOG_TAG3 = "mikrotik-mcp";
|
|
919
|
+
var CODE_RE = /^[a-z]{2}$/;
|
|
920
|
+
var sourceUrl = (code) => `https://hatscripts.github.io/circle-flags/flags/${code}.svg`;
|
|
921
|
+
var VENDOR_DIR = join3(PROJECT_ROOT, "assets", "flags");
|
|
922
|
+
var CACHE_DIR = join3(homedir(), ".mikrotik-mcp", "flags");
|
|
923
|
+
var mem = new Map;
|
|
924
|
+
async function flagSvg(code) {
|
|
925
|
+
const c = code.toLowerCase();
|
|
926
|
+
if (!CODE_RE.test(c))
|
|
927
|
+
return null;
|
|
928
|
+
const cached = mem.get(c);
|
|
929
|
+
if (cached !== undefined)
|
|
930
|
+
return cached;
|
|
931
|
+
for (const file of [join3(VENDOR_DIR, `${c}.svg`), join3(CACHE_DIR, `${c}.svg`)]) {
|
|
932
|
+
try {
|
|
933
|
+
const svg = await readFile(file, "utf8");
|
|
934
|
+
mem.set(c, svg);
|
|
935
|
+
return svg;
|
|
936
|
+
} catch {}
|
|
937
|
+
}
|
|
938
|
+
try {
|
|
939
|
+
const res = await fetch(sourceUrl(c), { signal: AbortSignal.timeout(8000) });
|
|
940
|
+
if (!res.ok) {
|
|
941
|
+
if (res.status === 404)
|
|
942
|
+
mem.set(c, null);
|
|
943
|
+
return null;
|
|
944
|
+
}
|
|
945
|
+
const svg = await res.text();
|
|
946
|
+
mem.set(c, svg);
|
|
947
|
+
mkdir(CACHE_DIR, { recursive: true }).then(() => writeFile(join3(CACHE_DIR, `${c}.svg`), svg)).catch(() => {});
|
|
948
|
+
return svg;
|
|
949
|
+
} catch (e) {
|
|
950
|
+
logger.warn(`[${LOG_TAG3}] flag fetch failed for '${c}': ${e instanceof Error ? e.message : String(e)}`);
|
|
951
|
+
return null;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
|
|
818
955
|
// src/observability/traffic-hub.ts
|
|
819
956
|
var POLL_MS = 1000;
|
|
820
957
|
var hubs = new Map;
|
|
@@ -1140,8 +1277,8 @@ var CAPSMAN_RETENTION_MS = 30 * 24 * 60 * 60 * 1000;
|
|
|
1140
1277
|
var DEFAULT_CAPSMAN_INTERVAL_MS = 5 * 60000;
|
|
1141
1278
|
var MIN_CAPSMAN_INTERVAL_MS = 60000;
|
|
1142
1279
|
var MAX_CAPSMAN_INTERVAL_MS = 6 * 60 * 60000;
|
|
1143
|
-
var
|
|
1144
|
-
var
|
|
1280
|
+
var timer3 = null;
|
|
1281
|
+
var inFlight3 = false;
|
|
1145
1282
|
var noCapsman = new Set;
|
|
1146
1283
|
function clampInterval(ms) {
|
|
1147
1284
|
if (!Number.isFinite(ms))
|
|
@@ -1168,9 +1305,9 @@ async function sampleDevice(store, device, ts) {
|
|
|
1168
1305
|
store.recordRadioSamples(device, ts, samples);
|
|
1169
1306
|
}
|
|
1170
1307
|
async function sampleCapsmanOnce(store) {
|
|
1171
|
-
if (
|
|
1308
|
+
if (inFlight3)
|
|
1172
1309
|
return;
|
|
1173
|
-
|
|
1310
|
+
inFlight3 = true;
|
|
1174
1311
|
const ts = Date.now();
|
|
1175
1312
|
try {
|
|
1176
1313
|
const cfg = getConfig();
|
|
@@ -1185,18 +1322,18 @@ async function sampleCapsmanOnce(store) {
|
|
|
1185
1322
|
}));
|
|
1186
1323
|
store.pruneSamples(ts - CAPSMAN_RETENTION_MS);
|
|
1187
1324
|
} finally {
|
|
1188
|
-
|
|
1325
|
+
inFlight3 = false;
|
|
1189
1326
|
}
|
|
1190
1327
|
}
|
|
1191
1328
|
function startCapsmanSampler(store, intervalMs = DEFAULT_CAPSMAN_INTERVAL_MS) {
|
|
1192
1329
|
const ms = clampInterval(intervalMs);
|
|
1193
1330
|
sampleCapsmanOnce(store);
|
|
1194
|
-
|
|
1331
|
+
timer3 = setInterval(() => void sampleCapsmanOnce(store), ms);
|
|
1195
1332
|
}
|
|
1196
1333
|
function stopCapsmanSampler() {
|
|
1197
|
-
if (
|
|
1198
|
-
clearInterval(
|
|
1199
|
-
|
|
1334
|
+
if (timer3) {
|
|
1335
|
+
clearInterval(timer3);
|
|
1336
|
+
timer3 = null;
|
|
1200
1337
|
}
|
|
1201
1338
|
}
|
|
1202
1339
|
|
|
@@ -1336,8 +1473,8 @@ var USAGE_RETENTION_MS = 93 * 24 * 60 * 60 * 1000;
|
|
|
1336
1473
|
var DEFAULT_USAGE_INTERVAL_MS = 60000;
|
|
1337
1474
|
var MIN_USAGE_INTERVAL_MS = 30000;
|
|
1338
1475
|
var MAX_USAGE_INTERVAL_MS = 6 * 60 * 60000;
|
|
1339
|
-
var
|
|
1340
|
-
var
|
|
1476
|
+
var timer4 = null;
|
|
1477
|
+
var inFlight4 = false;
|
|
1341
1478
|
var currentStore = null;
|
|
1342
1479
|
var currentIntervalMs = DEFAULT_USAGE_INTERVAL_MS;
|
|
1343
1480
|
function clampInterval2(ms) {
|
|
@@ -1398,9 +1535,9 @@ async function ingestSessions(store, device) {
|
|
|
1398
1535
|
store.upsertSessions(device, sessions);
|
|
1399
1536
|
}
|
|
1400
1537
|
async function sampleUsageOnce(store) {
|
|
1401
|
-
if (
|
|
1538
|
+
if (inFlight4)
|
|
1402
1539
|
return;
|
|
1403
|
-
|
|
1540
|
+
inFlight4 = true;
|
|
1404
1541
|
const ts = Date.now();
|
|
1405
1542
|
try {
|
|
1406
1543
|
const cfg = getConfig();
|
|
@@ -1416,32 +1553,32 @@ async function sampleUsageOnce(store) {
|
|
|
1416
1553
|
}));
|
|
1417
1554
|
store.pruneSamples(ts - USAGE_RETENTION_MS);
|
|
1418
1555
|
} finally {
|
|
1419
|
-
|
|
1556
|
+
inFlight4 = false;
|
|
1420
1557
|
}
|
|
1421
1558
|
}
|
|
1422
1559
|
function startUsageSampler(store, intervalMs = DEFAULT_USAGE_INTERVAL_MS) {
|
|
1423
1560
|
currentStore = store;
|
|
1424
1561
|
currentIntervalMs = clampInterval2(intervalMs);
|
|
1425
1562
|
sampleUsageOnce(store);
|
|
1426
|
-
|
|
1563
|
+
timer4 = setInterval(() => void sampleUsageOnce(store), currentIntervalMs);
|
|
1427
1564
|
}
|
|
1428
1565
|
function getUsageSamplerInterval() {
|
|
1429
1566
|
return currentIntervalMs;
|
|
1430
1567
|
}
|
|
1431
1568
|
function setUsageSamplerInterval(intervalMs) {
|
|
1432
1569
|
currentIntervalMs = clampInterval2(intervalMs);
|
|
1433
|
-
if (
|
|
1434
|
-
clearInterval(
|
|
1570
|
+
if (timer4)
|
|
1571
|
+
clearInterval(timer4);
|
|
1435
1572
|
if (currentStore) {
|
|
1436
1573
|
const store = currentStore;
|
|
1437
|
-
|
|
1574
|
+
timer4 = setInterval(() => void sampleUsageOnce(store), currentIntervalMs);
|
|
1438
1575
|
}
|
|
1439
1576
|
return currentIntervalMs;
|
|
1440
1577
|
}
|
|
1441
1578
|
function stopUsageSampler() {
|
|
1442
|
-
if (
|
|
1443
|
-
clearInterval(
|
|
1444
|
-
|
|
1579
|
+
if (timer4) {
|
|
1580
|
+
clearInterval(timer4);
|
|
1581
|
+
timer4 = null;
|
|
1445
1582
|
}
|
|
1446
1583
|
currentStore = null;
|
|
1447
1584
|
}
|
|
@@ -1834,7 +1971,7 @@ function runUpgrade(spec) {
|
|
|
1834
1971
|
env: process.env,
|
|
1835
1972
|
stdio: ["ignore", "pipe", "pipe"]
|
|
1836
1973
|
});
|
|
1837
|
-
const
|
|
1974
|
+
const timer5 = setTimeout(() => {
|
|
1838
1975
|
child.kill();
|
|
1839
1976
|
resolve({ ok: false, log: `${out}
|
|
1840
1977
|
(timed out after 180s)` });
|
|
@@ -1842,19 +1979,19 @@ function runUpgrade(spec) {
|
|
|
1842
1979
|
child.stdout?.on("data", (d) => out += d.toString());
|
|
1843
1980
|
child.stderr?.on("data", (d) => out += d.toString());
|
|
1844
1981
|
child.on("error", (e) => {
|
|
1845
|
-
clearTimeout(
|
|
1982
|
+
clearTimeout(timer5);
|
|
1846
1983
|
resolve({ ok: false, log: `${out}
|
|
1847
1984
|
spawn error: ${String(e)}` });
|
|
1848
1985
|
});
|
|
1849
1986
|
child.on("exit", (code) => {
|
|
1850
|
-
clearTimeout(
|
|
1987
|
+
clearTimeout(timer5);
|
|
1851
1988
|
resolve({ ok: code === 0, log: out.trim() || `(exit ${code})` });
|
|
1852
1989
|
});
|
|
1853
1990
|
});
|
|
1854
1991
|
}
|
|
1855
1992
|
function dashboardHtml() {
|
|
1856
1993
|
try {
|
|
1857
|
-
return readFileSync3(
|
|
1994
|
+
return readFileSync3(join4(UI_DIST_DIR, "observability.html"), "utf8");
|
|
1858
1995
|
} catch {
|
|
1859
1996
|
return `<!doctype html><meta charset=utf-8><body style="font:14px system-ui;padding:24px;background:#0b0d10;color:#e8eaed">
|
|
1860
1997
|
<h2>MikroTik MCP \u2014 Observability Dashboard</h2>
|
|
@@ -1948,6 +2085,7 @@ function devicesPayload(store) {
|
|
|
1948
2085
|
jumpVia: dc.jumpVia,
|
|
1949
2086
|
jumpHost: dc.jumpHost ? { host: dc.jumpHost.host, port: dc.jumpHost.port } : undefined,
|
|
1950
2087
|
status: getDeviceStatus(name),
|
|
2088
|
+
geo: getDeviceGeo(name),
|
|
1951
2089
|
history: getDeviceHistory(name),
|
|
1952
2090
|
activity: activity.get(name) ?? {
|
|
1953
2091
|
calls: 0,
|
|
@@ -2733,7 +2871,7 @@ async function featureRoutes(req, url) {
|
|
|
2733
2871
|
const raw = b?.dir?.trim();
|
|
2734
2872
|
if (!raw)
|
|
2735
2873
|
return json3({ error: "dir required" }, 400);
|
|
2736
|
-
const dir = raw === "~" || raw.startsWith("~/") ?
|
|
2874
|
+
const dir = raw === "~" || raw.startsWith("~/") ? join4(homedir2(), raw.slice(1)) : raw;
|
|
2737
2875
|
const next = { ...getConfig(), backupDir: dir };
|
|
2738
2876
|
setConfig(next);
|
|
2739
2877
|
try {
|
|
@@ -2840,14 +2978,15 @@ async function runDashboard(cfg, transportLabel) {
|
|
|
2840
2978
|
transport: transportLabel
|
|
2841
2979
|
});
|
|
2842
2980
|
startHealthChecks(30000);
|
|
2981
|
+
startGeoLookups();
|
|
2843
2982
|
try {
|
|
2844
|
-
usageStore = await openUsageStore(
|
|
2983
|
+
usageStore = await openUsageStore(join4(dirname5(cfg.dbPath), "usage.db"));
|
|
2845
2984
|
startUsageSampler(usageStore);
|
|
2846
2985
|
} catch (e) {
|
|
2847
2986
|
logger.warn(`[${SERVER_TAG3}] usage history disabled: ${String(e)}`);
|
|
2848
2987
|
}
|
|
2849
2988
|
try {
|
|
2850
|
-
capsmanStore = await openCapsmanStore(
|
|
2989
|
+
capsmanStore = await openCapsmanStore(join4(dirname5(cfg.dbPath), "capsman.db"));
|
|
2851
2990
|
startCapsmanSampler(capsmanStore);
|
|
2852
2991
|
} catch (e) {
|
|
2853
2992
|
logger.warn(`[${SERVER_TAG3}] capsman trends disabled: ${String(e)}`);
|
|
@@ -2959,6 +3098,18 @@ async function runDashboard(cfg, transportLabel) {
|
|
|
2959
3098
|
devices: ps
|
|
2960
3099
|
});
|
|
2961
3100
|
}
|
|
3101
|
+
if (url.pathname.startsWith("/api/flag/")) {
|
|
3102
|
+
const code = url.pathname.slice("/api/flag/".length).replace(/\.svg$/, "");
|
|
3103
|
+
const svg = await flagSvg(code);
|
|
3104
|
+
if (!svg)
|
|
3105
|
+
return new Response("flag not found", { status: 404 });
|
|
3106
|
+
return new Response(svg, {
|
|
3107
|
+
headers: {
|
|
3108
|
+
"content-type": "image/svg+xml; charset=utf-8",
|
|
3109
|
+
"cache-control": "public, max-age=86400"
|
|
3110
|
+
}
|
|
3111
|
+
});
|
|
3112
|
+
}
|
|
2962
3113
|
if (url.pathname === "/api/devices") {
|
|
2963
3114
|
return json3(devicesPayload(db));
|
|
2964
3115
|
}
|
|
@@ -3169,6 +3320,7 @@ async function runDashboard(cfg, transportLabel) {
|
|
|
3169
3320
|
store,
|
|
3170
3321
|
stop() {
|
|
3171
3322
|
stopHealthChecks();
|
|
3323
|
+
stopGeoLookups();
|
|
3172
3324
|
stopUsageSampler();
|
|
3173
3325
|
stopCapsmanSampler();
|
|
3174
3326
|
server.stop(true);
|
|
@@ -3359,18 +3511,18 @@ function installToolPagination(server, pageSize) {
|
|
|
3359
3511
|
const sdkHandler = low._requestHandlers?.get("tools/list");
|
|
3360
3512
|
if (typeof sdkHandler !== "function")
|
|
3361
3513
|
return;
|
|
3362
|
-
let
|
|
3514
|
+
let cache2 = null;
|
|
3363
3515
|
server.server.setRequestHandler(ListToolsRequestSchema, async (request, extra) => {
|
|
3364
|
-
if (!
|
|
3365
|
-
|
|
3366
|
-
const total =
|
|
3516
|
+
if (!cache2)
|
|
3517
|
+
cache2 = (await sdkHandler(request, extra)).tools ?? [];
|
|
3518
|
+
const total = cache2.length;
|
|
3367
3519
|
const cursor = request.params?.cursor;
|
|
3368
3520
|
let start = 0;
|
|
3369
3521
|
if (typeof cursor === "string") {
|
|
3370
3522
|
const n = Number.parseInt(cursor, 10);
|
|
3371
3523
|
start = Number.isFinite(n) && n > 0 ? Math.min(n, total) : 0;
|
|
3372
3524
|
}
|
|
3373
|
-
const tools =
|
|
3525
|
+
const tools = cache2.slice(start, start + pageSize);
|
|
3374
3526
|
const end = start + tools.length;
|
|
3375
3527
|
return end < total ? { tools, nextCursor: String(end) } : { tools };
|
|
3376
3528
|
});
|
|
@@ -9070,7 +9070,7 @@ var cache = null;
|
|
|
9070
9070
|
async function gateway() {
|
|
9071
9071
|
if (cache)
|
|
9072
9072
|
return cache;
|
|
9073
|
-
const { moduleCatalog } = await import("./cli-
|
|
9073
|
+
const { moduleCatalog } = await import("./cli-73c3hn9f.js");
|
|
9074
9074
|
const forIndex = [];
|
|
9075
9075
|
const byName = new Map;
|
|
9076
9076
|
for (const mod of moduleCatalog) {
|
|
@@ -33346,4 +33346,4 @@ function selectToolModules(filter = {}, catalog = moduleCatalog) {
|
|
|
33346
33346
|
}).map((m) => m.tools);
|
|
33347
33347
|
}
|
|
33348
33348
|
|
|
33349
|
-
export { __require, DEFAULT_SNAPSHOT_DB, DEFAULT_CONFIG_HISTORY_DIR, DeviceConfigSchema, ToolFilterSchema, MikrotikConfigSchema, getConfigSource, loadConfig, logger, setConfig, getConfig, listDevices, deviceLabels, resolveDeviceName, deviceDirectory, isMacTelnetDevice, createDeviceClient, describeTransport, isPoolEnabled, closeAll, closeDevice, poolStatus, getMemoryStore, closeMemoryStore, reopenMemoryStore, executeMikrotikCommand, createContext, Cmd, isEmpty, looksLikeError, commandUnsupported, parseKeyValues, parseRouterosDate, parseSize, parseSystemResource, parseRecords, parseLeadingNumber, parseDisks, riskOf, REDACTED, redact, configureRecorder, getEventStore, subscribe, subscriberCount, registerTools, fetchDevices, sampleDeviceTraffic, sampleAllTraffic, setDeviceLimits, blockDevice, allowDevice, makeDeviceStatic, setDeviceIp, setDeviceLabel, removeDeviceLease, devicesView, PROMPTS_DIR, UI_DIST_DIR, registerUiResources, backupDir, listBackups, readBackup, writeBackup, deleteBackup, renameBackup, createLocalBackup, restoreLocalBackup, isS3Configured, getS3Client, presignExpiresIn, s3Target, splitCommands, buildChangePlan, renderPlan, diffLines, normalizeExport, analyzeDrift, attributeChanges, openSnapshotStore, applyWritesSafely, captureSnapshot, DEFAULT_TZSP_PORT, capture2 as capture, AAA_ENTITIES, listAaaEntity, addAaaEntity, updateAaaEntity, removeAaaEntity, toggleAaaEntity, getRadiusIncoming, setRadiusIncoming, resetRadiusCounters, getUmSettings, setUmSettings, VERSION, WEBSITE_URL, LOGO_URL, SERVER_TITLE, SERVER_DESCRIPTION, SERVER_NAME, PKG_META, loadFileCacheSync, fetchLatestRelease, checkForUpdate, updateSummaryLine, fetchAllReleases, buildChannelPlanCommands, reportWeakClients, runCapsmanAudit, steerAlreadyPresent, buildSteerCommands, loadBalancePlan, buildLoadBalanceCommands, buildFtCommands, buildHaCommands, haGuidance, capsmanOverview, fetchCapsmanState, moduleCatalog, allToolModules, ALWAYS_ON_MODULES, selectToolModules };
|
|
33349
|
+
export { __require, DEFAULT_SNAPSHOT_DB, DEFAULT_CONFIG_HISTORY_DIR, DeviceConfigSchema, ToolFilterSchema, MikrotikConfigSchema, getConfigSource, loadConfig, logger, setConfig, getConfig, listDevices, deviceLabels, resolveDeviceName, deviceDirectory, isMacTelnetDevice, createDeviceClient, describeTransport, isPoolEnabled, closeAll, closeDevice, poolStatus, getMemoryStore, closeMemoryStore, reopenMemoryStore, executeMikrotikCommand, createContext, Cmd, isEmpty, looksLikeError, commandUnsupported, parseKeyValues, parseRouterosDate, parseSize, parseSystemResource, parseRecords, parseLeadingNumber, parseDisks, riskOf, REDACTED, redact, configureRecorder, getEventStore, subscribe, subscriberCount, registerTools, fetchDevices, sampleDeviceTraffic, sampleAllTraffic, setDeviceLimits, blockDevice, allowDevice, makeDeviceStatic, setDeviceIp, setDeviceLabel, removeDeviceLease, devicesView, PROMPTS_DIR, PROJECT_ROOT, UI_DIST_DIR, registerUiResources, backupDir, listBackups, readBackup, writeBackup, deleteBackup, renameBackup, createLocalBackup, restoreLocalBackup, isS3Configured, getS3Client, presignExpiresIn, s3Target, splitCommands, buildChangePlan, renderPlan, diffLines, normalizeExport, analyzeDrift, attributeChanges, openSnapshotStore, applyWritesSafely, captureSnapshot, DEFAULT_TZSP_PORT, capture2 as capture, AAA_ENTITIES, listAaaEntity, addAaaEntity, updateAaaEntity, removeAaaEntity, toggleAaaEntity, getRadiusIncoming, setRadiusIncoming, resetRadiusCounters, getUmSettings, setUmSettings, VERSION, WEBSITE_URL, LOGO_URL, SERVER_TITLE, SERVER_DESCRIPTION, SERVER_NAME, PKG_META, loadFileCacheSync, fetchLatestRelease, checkForUpdate, updateSummaryLine, fetchAllReleases, buildChannelPlanCommands, reportWeakClients, runCapsmanAudit, steerAlreadyPresent, buildSteerCommands, loadBalancePlan, buildLoadBalanceCommands, buildFtCommands, buildHaCommands, haGuidance, capsmanOverview, fetchCapsmanState, moduleCatalog, allToolModules, ALWAYS_ON_MODULES, selectToolModules };
|
|
@@ -142,7 +142,7 @@ ${n.map(([e,n])=>{let r=n.theme?.[t]??n.color;return r?` --color-${e}: ${r};`:n
|
|
|
142
142
|
`).length;return(0,W.jsxs)(`div`,{className:`border-border bg-background flex max-h-[420px] overflow-hidden rounded-md border`,children:[(0,W.jsx)(`pre`,{className:`bg-muted text-muted-foreground m-0 min-w-[38px] overflow-hidden px-2 py-2.5 text-right font-mono text-xs leading-[1.5] select-none`,"aria-hidden":`true`,ref:f,children:Array.from({length:y},(e,t)=>t+1).join(`
|
|
143
143
|
`)}),(0,W.jsxs)(`div`,{className:`relative flex-1`,children:[(0,W.jsxs)(`pre`,{className:`text-muted-foreground pointer-events-none absolute inset-0 z-0 m-0 box-border overflow-hidden px-3 py-2.5 font-mono text-xs leading-[1.5] whitespace-pre [tab-size:2] [&_.j-bool]:text-chart-4 [&_.j-key]:text-chart-1 [&_.j-null]:text-muted-foreground [&_.j-num]:text-chart-3 [&_.j-str]:text-chart-2`,"aria-hidden":`true`,ref:d,children:[T7(r),`
|
|
144
144
|
`]}),(0,W.jsx)(BS,{ref:l,className:`caret-brand selection:bg-brand/35 relative z-[1] m-0 box-border h-[400px] min-h-0 w-full resize-y rounded-none border-0 bg-transparent px-3 py-2.5 font-mono text-xs leading-[1.5] whitespace-pre text-transparent shadow-none [field-sizing:fixed] [tab-size:2] outline-none focus-visible:border-0 focus-visible:ring-0 dark:bg-transparent`,spellCheck:!1,wrap:`off`,value:r,onChange:e=>{i(e.target.value),h(e.currentTarget)},onKeyDown:_,onClick:()=>c(null),onScroll:e=>{let t=e.currentTarget;d.current&&(d.current.scrollTop=t.scrollTop,d.current.scrollLeft=t.scrollLeft),f.current&&(f.current.scrollTop=t.scrollTop)}}),s&&(0,W.jsx)(`div`,{className:`border-brand bg-card absolute z-[5] max-h-[184px] min-w-[150px] overflow-y-auto rounded-md border shadow-lg`,style:{left:s.x,top:s.y},children:s.items.map((e,t)=>(0,W.jsx)(`div`,{className:q(`cursor-pointer px-[11px] py-[5px] font-mono text-xs`,t===s.index?`bg-brand/20 text-foreground`:`text-muted-foreground`),onMouseDown:t=>{t.preventDefault(),g(e)},children:e},e))})]})]})}var o9=e=>e&&typeof e==`object`&&!Array.isArray(e)?e:{};function kte({initial:e,onClose:t,onReload:n}){let[r,i]=(0,v.useState)(()=>o9(e)),[a,o]=(0,v.useState)(`form`),[s,c]=(0,v.useState)(null),[l,u]=(0,v.useState)([]),[d,f]=(0,v.useState)({}),[p,m]=(0,v.useState)(null),[h,g]=(0,v.useState)(null),[_,y]=(0,v.useState)(0),[b,x]=(0,v.useState)(6e4),[S,C]=(0,v.useState)(null);(0,v.useEffect)(()=>{let e=setTimeout(()=>{_c(`/api/config/validate`,r).then(e=>u(e.errors??[])).catch(()=>{})},350);return()=>clearTimeout(e)},[r]),(0,v.useEffect)(()=>{if(!h||_<=0)return;let e=setInterval(()=>y(e=>Math.max(0,e-1)),1e3);return()=>clearInterval(e)},[h,_]);let w=(0,v.useRef)(n);w.current=n,(0,v.useEffect)(()=>{h&&_===0&&(h.rollbackMs??0)>0&&(C(`Auto-reverted — changes were not confirmed in time.`),g(null),w.current())},[h,_]);let T=!s&&l.length===0,E=async()=>{let e=o9(r.devices);if(Object.keys(e).length===0){C(`No devices configured to test — add one first.`),J.error(`No devices to test`);return}f({}),C(`Testing devices…`);let t={};for(let[n,r]of Object.entries(e)){let e=await _c(`/api/config/test-device`,{name:n,config:r}).catch(()=>({ok:!1}));t[n]=e.ok&&e.status?.reachable===!0?{ok:!0,label:`${Math.round(e.status.latencyMs??0)}ms · ${e.status.identity??`ok`}`}:{ok:!1,label:e.status?.error??e.errors?.[0]?.message??`unreachable`},f({...t})}C(null),Object.values(t).every(e=>e.ok)?J.success(`Devices reachable`):J.error(`Some devices unreachable`)},D=async()=>{m(await _c(`/api/config/preview`,r))},O=async()=>{C(`Saving…`);try{let e=await _c(`/api/config`,{config:r,rollbackMs:b});if(C(null),m(null),!e.ok){u(e.errors??[{path:`(root)`,message:`save rejected`}]),J.error(e.errors?.[0]?.message??`Config save rejected`);return}g(e),y(Math.round((e.rollbackMs??0)/1e3)),J.success(`Config applied`)}catch(e){C(null),J.error(e instanceof Error?e.message:`Save failed`)}},k=async()=>{if(h?.pendingId)try{await _c(`/api/config/keep`,{pendingId:h.pendingId}),g(null),C(`Changes kept.`),n(),J.success(`Change kept`)}catch(e){J.error(e instanceof Error?e.message:`Keep failed`)}},A=async()=>{if(h?.pendingId)try{await _c(`/api/config/rollback`,{pendingId:h.pendingId}),g(null),C(`Reverted to the previous config.`),n(),J.success(`Change rolled back`)}catch(e){J.error(e instanceof Error?e.message:`Rollback failed`)}};return(0,W.jsxs)(`div`,{className:`flex flex-col gap-2.5`,children:[(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2`,children:[(0,W.jsxs)(`div`,{className:`border-border bg-muted inline-flex gap-0.5 rounded-md border p-0.5`,children:[(0,W.jsxs)(`button`,{className:q(`flex cursor-pointer items-center gap-1.5 rounded-[6px] border-0 bg-transparent px-[11px] py-1 text-xs font-semibold`,a===`form`?`bg-card text-foreground shadow-sm`:`text-muted-foreground`),onClick:()=>{c(null),o(`form`)},children:[(0,W.jsx)(Gc,{className:`size-3.5`}),` Form`]}),(0,W.jsxs)(`button`,{className:q(`flex cursor-pointer items-center gap-1.5 rounded-[6px] border-0 bg-transparent px-[11px] py-1 text-xs font-semibold`,a===`json`?`bg-card text-foreground shadow-sm`:`text-muted-foreground`),onClick:()=>o(`json`),children:[(0,W.jsx)(Nc,{className:`size-3.5`}),` JSON`]})]}),(0,W.jsx)(`span`,{className:q(`rounded-full border px-2.5 py-1 font-mono text-[11px]`,T?`border-success/40 bg-success/10 text-success`:`border-destructive/40 bg-destructive/10 text-destructive`),children:s?`invalid JSON`:l.length?`${l.length} schema issue(s)`:`valid ✓`}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(X,{size:`sm`,onClick:()=>void E(),children:`Test devices`}),(0,W.jsx)(X,{size:`sm`,onClick:()=>void D(),disabled:!T,children:`Preview diff`}),(0,W.jsx)(Xx,{size:`sm`,value:String(b),onValueChange:e=>x(Number(e)),options:Dte.map(([e,t])=>({value:String(t),label:e})),"aria-label":`Auto-revert window`}),(0,W.jsx)(X,{size:`sm`,type:`accent`,onClick:()=>void O(),disabled:!T||!!h,children:`Save`}),(0,W.jsx)(X,{size:`sm`,onClick:t,children:`Close`})]}),S&&(0,W.jsx)(`div`,{className:`text-muted-foreground font-mono text-xs`,children:S}),h&&(0,W.jsxs)(`div`,{className:`border-warning/40 bg-warning/10 flex flex-wrap items-center gap-2 rounded-md border px-3 py-2.5 text-xs`,children:[(0,W.jsx)(`strong`,{children:`Applied.`}),` `,(h.rollbackMs??0)>0?(0,W.jsxs)(W.Fragment,{children:[`Reverting in `,(0,W.jsxs)(`span`,{className:`text-warning font-mono font-bold`,children:[_,`s`]}),` `,`unless you keep it.`]}):(0,W.jsx)(W.Fragment,{children:`Saved without an auto-revert window.`}),h.devicesChanged&&(0,W.jsxs)(`span`,{className:`text-warning text-[11px]`,children:[` `,`· device list changed — reconnect the MCP client to expose it to the model`]}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(X,{size:`sm`,type:`accent`,onClick:()=>void k(),children:`Keep changes`}),(0,W.jsx)(X,{size:`sm`,onClick:()=>void A(),children:`Revert now`})]}),Object.keys(d).length>0&&(0,W.jsx)(`div`,{className:`flex flex-wrap gap-1.5`,children:Object.entries(d).map(([e,t])=>(0,W.jsxs)(`span`,{className:q(`rounded-full border px-2.5 py-1 font-mono text-[11px]`,t.ok?`border-success/40 text-success`:`border-destructive/40 text-destructive`),children:[t.ok?`●`:`○`,` `,e,`: `,t.label]},e))}),a===`form`?(0,W.jsx)(wte,{cfg:r,onChange:i}):(0,W.jsx)(Ote,{value:r,onChange:e=>i(o9(e)),onJsonError:c}),(s||l.length>0)&&(0,W.jsx)(`div`,{className:`flex flex-col gap-[3px]`,children:s?(0,W.jsxs)(`div`,{className:`text-destructive font-mono text-[11px]`,children:[`JSON: `,s]}):l.slice(0,12).map((e,t)=>(0,W.jsxs)(`div`,{className:`text-destructive font-mono text-[11px]`,children:[(0,W.jsx)(`code`,{className:`text-warning`,children:e.path}),` — `,e.message]},t))}),p&&(0,W.jsxs)(`div`,{className:`border-border overflow-hidden rounded-md border`,children:[(0,W.jsxs)(`div`,{className:`bg-muted flex items-center gap-2 px-[13px] py-[9px] text-xs`,children:[(0,W.jsx)(`strong`,{children:`Diff vs current`}),(0,W.jsx)(`span`,{className:`text-muted-foreground text-[11px]`,children:p.summary?.changed?`+${p.summary.added} / -${p.summary.removed}`:`no changes`}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(X,{size:`sm`,ghost:!0,icon:(0,W.jsx)(_l,{className:`size-4`}),onClick:()=>m(null),"aria-label":`Close preview`})]}),(0,W.jsx)(`pre`,{className:`bg-background text-muted-foreground m-0 max-h-[320px] overflow-auto px-3 py-2.5 font-mono text-[11px] leading-[1.5]`,children:(p.unified||`(identical)`).split(`
|
|
145
|
-
`).map((e,t)=>(0,W.jsx)(`div`,{className:e.startsWith(`+`)?`text-success`:e.startsWith(`-`)?`text-destructive`:e.startsWith(`@@`)?`text-brand`:``,children:e||` `},t))})]})]})}function Ate({x:e,y:t,text:n,className:r,textClassName:i,padX:a=9,fontSize:o=9}){let s=(0,v.useRef)(null),[c,l]=(0,v.useState)(()=>n.length*o*.62);(0,v.useLayoutEffect)(()=>{let e=s.current?.getComputedTextLength();e&&e>0&&l(e)},[n,o]);let u=Math.round(c+a*2);return(0,W.jsxs)(`g`,{transform:`translate(${e.toFixed(1)},${t.toFixed(1)})`,children:[(0,W.jsx)(`rect`,{className:r,x:-u/2,y:-9,rx:9,width:u,height:18}),(0,W.jsx)(`text`,{ref:s,className:i,x:0,y:3.5,textAnchor:`middle`,fontSize:o,children:n})]})}function s9(e){return e.reachable===!0?{label:`online`,color:`var(--foreground)`}:e.reachable===!1?{label:`offline`,color:`var(--destructive)`}:{label:`checking…`,color:`var(--muted-foreground)`}}function c9(e,t){let n=[...new Set(t)].sort(),r=Math.max(1,n.length),i=Math.max(0,n.indexOf(e));return`hsl(${Math.round(i*360/r)} 70% ${62-i%3*5}%)`}function l9(e,t,n,r){let i=1-r;return{x:i*i*e.x+2*i*r*t.x+r*r*n.x,y:i*i*e.y+2*i*r*t.y+r*r*n.y}}function u9(e,t,n,r){let i=1-r,a=2*i*(t.x-e.x)+2*r*(n.x-t.x),o=2*i*(t.y-e.y)+2*r*(n.y-t.y);return Math.atan2(o,a)*180/Math.PI}var d9=`var(--foreground)`,f9=`var(--muted-foreground)`,p9=`var(--warning)`;function m9(e){return e.jumpVia?e.jumpVia:e.jumpHost?`${e.jumpHost.host}:${e.jumpHost.port}`:null}var h9=18,jte=e=>e.length>h9?`${e.slice(0,h9-1)}…`:e,Mte=e=>Math.max(23,Math.min(70,e.length*3.1+12));function Nte({payload:e,pulses:t}){let n=e.devices,r=Math.max(1,n.length),i=n.map(e=>e.name),a=n.map(e=>{let t=jte(e.name);return{d:e,label:t,r:Mte(t)}}),o=Math.max(23,...a.map(e=>e.r)),s=r>1?(o+18)/Math.sin(Math.PI/r):0,c=Math.max(110+o,s,120),l=o+60,u=Math.max(700,Math.round((c+o+40)*2)),d=Math.round((c+l)*2),f=u/2,p=d/2,m={x:f,y:p},h=a.map((e,t)=>{let n=t/r*Math.PI*2-Math.PI/2+(r%2==0?Math.PI/r:0);return{...e,i:t,x:f+c*Math.cos(n),y:p+c*Math.sin(n)}});return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsxs)(`svg`,{className:`conn`,viewBox:`0 0 ${u} ${d}`,width:`100%`,height:d,preserveAspectRatio:`xMidYMid meet`,children:[(0,W.jsxs)(`defs`,{children:[(0,W.jsxs)(`radialGradient`,{id:`conn-hub`,cx:`0.5`,cy:`0.34`,r:`0.75`,children:[(0,W.jsx)(`stop`,{offset:`0`,stopColor:`var(--foreground)`}),(0,W.jsx)(`stop`,{offset:`0.6`,stopColor:`color-mix(in srgb, var(--foreground) 70%, var(--muted-foreground))`}),(0,W.jsx)(`stop`,{offset:`1`,stopColor:`var(--muted-foreground)`})]}),(0,W.jsxs)(`radialGradient`,{id:`conn-orb`,cx:`0.5`,cy:`0.32`,r:`0.85`,children:[(0,W.jsx)(`stop`,{offset:`0`,stopColor:`var(--muted)`}),(0,W.jsx)(`stop`,{offset:`1`,stopColor:`var(--card)`})]}),(0,W.jsxs)(`radialGradient`,{id:`conn-burst`,cx:`0.5`,cy:`0.5`,r:`0.5`,children:[(0,W.jsx)(`stop`,{offset:`0`,stopColor:`var(--foreground)`}),(0,W.jsx)(`stop`,{offset:`0.5`,stopColor:`color-mix(in srgb, var(--foreground) 80%, transparent)`}),(0,W.jsx)(`stop`,{offset:`1`,stopColor:`var(--muted-foreground)`,stopOpacity:`0`})]}),(0,W.jsxs)(`linearGradient`,{id:`conn-tunnel-grad`,x1:`0`,y1:`0`,x2:`1`,y2:`0`,children:[(0,W.jsx)(`stop`,{offset:`0`,stopColor:p9,stopOpacity:`0.15`}),(0,W.jsx)(`stop`,{offset:`0.5`,stopColor:p9,stopOpacity:`0.95`}),(0,W.jsx)(`stop`,{offset:`1`,stopColor:p9,stopOpacity:`0.15`})]}),(0,W.jsxs)(`filter`,{id:`conn-tunnel-glow`,x:`-40%`,y:`-40%`,width:`180%`,height:`180%`,children:[(0,W.jsx)(`feGaussianBlur`,{stdDeviation:`3`,result:`b`}),(0,W.jsxs)(`feMerge`,{children:[(0,W.jsx)(`feMergeNode`,{in:`b`}),(0,W.jsx)(`feMergeNode`,{in:`SourceGraphic`})]})]})]}),[.5,.78,1].map((e,t)=>(0,W.jsx)(`circle`,{className:`conn-grid`,cx:f,cy:p,r:c*e},`g-${t}`)),[0,1,2].map(e=>(0,W.jsx)(`circle`,{className:`conn-sonar`,cx:f,cy:p,style:{animationDelay:`${e*1.1}s`}},`s-${e}`)),h.map(({d:e,i:n,x:r,y:i})=>{let a=s9(e.status),o=e.status.reachable===!0,s=e.status.reachable==null,c={x:r,y:i},l=(f+r)/2,u=(p+i)/2,d=r-f,h=i-p,g=Math.hypot(d,h)||1,_=-h/g,v=d/g,y={x:l+_*16,y:u+v*16},b={x:l-_*16,y:u-v*16},x=`M${f},${p} Q${y.x.toFixed(1)},${y.y.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,S=`M${f},${p} Q${b.x.toFixed(1)},${b.y.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,C=`M${f},${p} Q${l.toFixed(1)},${u.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,w=l9(m,y,c,.52),T=u9(m,y,c,.52),E=l9(m,b,c,.48),D=u9(m,b,c,.48)+180,O=t[e.name]??0;return(0,W.jsxs)(`g`,{children:[(0,W.jsx)(`path`,{id:`conn-cmd-${n}`,className:`conn-link`,d:x,stroke:o?d9:a.color,strokeOpacity:o?.5:.32,strokeDasharray:s?`2 8`:o?void 0:`6 7`}),(0,W.jsx)(`path`,{id:`conn-res-${n}`,className:`conn-link`,d:S,stroke:o?f9:a.color,strokeOpacity:o?.5:.18,strokeDasharray:o?void 0:`6 7`}),o&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`path`,{className:`conn-flow`,d:x,stroke:`var(--foreground)`}),(0,W.jsx)(`circle`,{className:`conn-packet`,r:3.2,fill:`var(--foreground)`,children:(0,W.jsx)(`animateMotion`,{dur:`2.6s`,repeatCount:`indefinite`,calcMode:`linear`,children:(0,W.jsx)(`mpath`,{href:`#conn-cmd-${n}`})})}),(0,W.jsx)(`rect`,{className:`conn-packet`,x:-2.6,y:-2.6,width:5.2,height:5.2,fill:`var(--muted-foreground)`,transform:`rotate(45)`,children:(0,W.jsx)(`animateMotion`,{dur:`2.6s`,begin:`0.9s`,repeatCount:`indefinite`,calcMode:`linear`,keyPoints:`1;0`,keyTimes:`0;1`,children:(0,W.jsx)(`mpath`,{href:`#conn-res-${n}`})})}),(0,W.jsx)(`path`,{className:`conn-chevron`,d:`M-4,-3 L4,0 L-4,3`,stroke:`var(--foreground)`,transform:`translate(${w.x.toFixed(1)},${w.y.toFixed(1)}) rotate(${T.toFixed(1)})`}),(0,W.jsx)(`path`,{className:`conn-chevron`,d:`M-4,-3 L4,0 L-4,3`,stroke:`var(--muted-foreground)`,transform:`translate(${E.x.toFixed(1)},${E.y.toFixed(1)}) rotate(${D.toFixed(1)})`})]}),O>0&&(0,W.jsx)(`g`,{children:(0,W.jsxs)(`circle`,{r:6,fill:`url(#conn-burst)`,children:[(0,W.jsx)(`animateMotion`,{dur:`1.1s`,repeatCount:`1`,calcMode:`linear`,keyPoints:`0;1;0`,keyTimes:`0;0.5;1`,path:C}),(0,W.jsx)(`animate`,{attributeName:`opacity`,values:`0;1;1;0`,keyTimes:`0;0.1;0.85;1`,dur:`1.1s`,repeatCount:`1`,fill:`freeze`})]})},`burst-${e.name}-${O}`)]},`l-${e.name}`)}),h.map(e=>{let t=e.d.jumpVia,n=t?h.find(e=>e.d.name===t):void 0,r=!n&&e.d.jumpHost?e.d.jumpHost:void 0;if(!n&&!r)return null;let i={x:e.x,y:e.y},a=Math.hypot(e.x-f,e.y-p)||1,o=(e.x-f)/a,s=(e.y-p)/a,c=n?{x:n.x,y:n.y}:{x:e.x+o*(e.r+46),y:e.y+s*(e.r+46)},l=(c.x+i.x)/2,u=(c.y+i.y)/2,d=Math.hypot(l-f,u-p)||1,m=n?50:16,g={x:l+(l-f)/d*m,y:u+(u-p)/d*m},_=`M${c.x.toFixed(1)},${c.y.toFixed(1)} Q${g.x.toFixed(1)},${g.y.toFixed(1)} ${i.x.toFixed(1)},${i.y.toFixed(1)}`,v=m9(e.d)??``,y=l9(c,g,i,.5),b=`conn-tunnel-${e.i}`;return(0,W.jsxs)(`g`,{className:`conn-tunnel-g`,children:[(0,W.jsx)(`path`,{className:`conn-tunnel-halo`,d:_,stroke:p9}),(0,W.jsx)(`path`,{id:b,className:`conn-tunnel`,d:_,stroke:`url(#conn-tunnel-grad)`,filter:`url(#conn-tunnel-glow)`}),r&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`circle`,{className:`conn-tunnel-sat`,cx:c.x,cy:c.y,r:11,fill:`var(--background)`,stroke:`var(--warning)`}),(0,W.jsx)(`text`,{x:c.x,y:c.y+3.5,textAnchor:`middle`,fontSize:11,children:`🛡️`})]}),(0,W.jsxs)(`text`,{className:`conn-tunnel-lock`,fontSize:13,textAnchor:`middle`,children:[`🔒`,(0,W.jsx)(`animateMotion`,{dur:`2.4s`,repeatCount:`indefinite`,calcMode:`linear`,rotate:`auto`,children:(0,W.jsx)(`mpath`,{href:`#${b}`})})]}),(0,W.jsx)(Ate,{x:y.x,y:y.y,text:`⤳ via ${v}`,className:`conn-tunnel-badge`,textClassName:`conn-tunnel-badge-tx`})]},`jump-${e.d.name}`)}),(0,W.jsx)(`circle`,{className:`conn-hub-glow`,cx:f,cy:p,r:42}),(0,W.jsx)(`circle`,{className:`conn-hub-ring`,cx:f,cy:p,r:37}),(0,W.jsx)(`circle`,{cx:f,cy:p,r:29,fill:`url(#conn-hub)`,stroke:`var(--border)`,strokeWidth:1.5}),(0,W.jsx)(`text`,{x:f,y:p-4,textAnchor:`middle`,fill:`var(--background)`,fontSize:12,fontWeight:700,children:`LLM`}),(0,W.jsx)(`text`,{x:f,y:p+8,textAnchor:`middle`,fill:`var(--background)`,fillOpacity:.66,fontSize:8,fontWeight:600,children:`⇄ MCP`}),(0,W.jsx)(`text`,{x:f,y:p+18,textAnchor:`middle`,fill:`var(--background)`,fillOpacity:.66,fontSize:7.5,fontWeight:600,children:`server`}),h.map(({d:e,x:t,y:n,r,label:a})=>{let o=s9(e.status),s=e.status.reachable===!0,c=s?`${e.status.latencyMs??`?`} ms`:o.label,l=c9(e.name,i);return(0,W.jsxs)(`g`,{className:`conn-node`,opacity:e.disabled?.35:1,children:[s&&(0,W.jsx)(`circle`,{className:`conn-node-halo`,cx:t,cy:n,r:r+1,stroke:l}),e.pool?.pooled&&(0,W.jsx)(`circle`,{cx:t,cy:n,r:r+5,fill:`none`,stroke:e.pool.inflight>0?`var(--chart-1)`:`var(--success)`,strokeWidth:1.5,strokeDasharray:e.pool.inflight>0?void 0:`4 4`,opacity:.5,className:e.pool.inflight>0?`conn-blink`:void 0}),(0,W.jsx)(`circle`,{cx:t,cy:n,r,fill:`url(#conn-orb)`}),(0,W.jsx)(`circle`,{cx:t,cy:n,r,fill:l,opacity:.16}),(0,W.jsx)(`circle`,{cx:t,cy:n,r,fill:`none`,stroke:l,strokeWidth:2.5}),(0,W.jsx)(`circle`,{className:s?`conn-blink`:void 0,cx:t+r*.7,cy:n-r*.7,r:4.5,fill:o.color,stroke:`var(--background)`,strokeWidth:1.5}),(0,W.jsx)(`text`,{x:t,y:n+3.5,textAnchor:`middle`,fill:`var(--foreground)`,fontSize:10,fontWeight:600,children:a}),(0,W.jsx)(`text`,{x:t,y:n+r+14,textAnchor:`middle`,fill:`var(--muted-foreground)`,fontSize:9,children:e.address??e.host}),(0,W.jsx)(`text`,{x:t,y:n+r+26,textAnchor:`middle`,fill:o.color,fontSize:9,fontWeight:600,children:c})]},`n-${e.name}`)})]}),(0,W.jsxs)(`div`,{className:`text-muted-foreground mt-2 flex flex-wrap justify-center gap-4 text-[11px] [&>span]:inline-flex [&>span]:items-center [&>span]:gap-1.5 [&_i]:inline-block [&_i]:size-2.5 [&_i]:rounded-[3px]`,children:[(0,W.jsxs)(`span`,{children:[(0,W.jsx)(`i`,{style:{background:d9}}),` command · LLM → device`]}),(0,W.jsxs)(`span`,{children:[(0,W.jsx)(`i`,{style:{background:f9}}),` response · device → LLM`]}),(0,W.jsxs)(`span`,{children:[(0,W.jsx)(`i`,{style:{background:d9}}),` live call (round-trip)`]}),e.devices.some(e=>m9(e))&&(0,W.jsxs)(`span`,{children:[(0,W.jsx)(`i`,{style:{background:`var(--warning)`}}),` 🔒 SSH jump tunnel (ProxyJump)`]}),e.devices.some(e=>e.pool?.pooled)&&(0,W.jsxs)(`span`,{children:[(0,W.jsx)(`i`,{style:{background:`var(--success)`}}),` pooled SSH connection`]})]})]})}function Pte({d:e,allNames:t,onToggle:n,onTest:r,onReconnect:i}){let[a,o]=(0,v.useState)(null),s=t=>{if(a)return;let n=t===`test`?r:i;n&&(o(t),n(e.name).finally(()=>o(null)))},c=s9(e.status),l=e.status.reachable===!0?`${c.label} · ${e.status.latencyMs??`?`}ms${e.status.version?` · v${e.status.version}`:``}`:e.status.reachable===!1?`${c.label}${e.status.error?` · ${e.status.error}`:``}`:c.label,u=c9(e.name,t);return(0,W.jsxs)(`div`,{className:q(`bg-card grid gap-2 rounded-lg border px-[15px] py-3.5 transition-[transform,border-color] duration-300`,`hover:border-chart-2/35 hover:-translate-y-0.5`,e.disabled&&`opacity-50 grayscale-[0.4]`),style:{borderLeft:`3px solid ${u}`},"data-disabled":e.disabled?`1`:void 0,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(`span`,{className:`size-[9px] shrink-0 rounded-full`,style:{background:u},title:`device colour`}),(0,W.jsx)(`span`,{className:`text-[13px] font-medium`,children:e.name}),e.isDefault&&(0,W.jsx)(Hx,{type:`accent`,children:`default`}),e.disabled&&(0,W.jsx)(Hx,{type:`warning`,children:`disabled`}),(0,W.jsx)(`span`,{className:`size-[7px] shrink-0 rounded-full`,style:{background:c.color},title:c.label}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(`span`,{onClick:e=>e.stopPropagation(),children:(0,W.jsx)(Y7,{checked:!e.disabled,onCheckedChange:()=>n?.(e.name,!e.disabled),title:e.disabled?`Enable device`:`Disable device`,"aria-label":e.disabled?`Enable device`:`Disable device`})}),(0,W.jsx)(Hx,{type:`secondary`,children:e.authMode})]}),(0,W.jsxs)(`div`,{className:`text-muted-foreground [&_b]:text-foreground grid grid-cols-[auto_1fr] gap-x-3 gap-y-[3px] text-[11px] [&_b]:font-medium [&_b]:break-words`,children:[(0,W.jsx)(`span`,{children:e.mac?`mac`:`host`}),(0,W.jsx)(`b`,{children:e.address??`${e.host}:${e.port}`}),(0,W.jsx)(`span`,{children:`user`}),(0,W.jsx)(`b`,{children:e.username}),(0,W.jsx)(`span`,{children:`status`}),(0,W.jsx)(`b`,{style:{color:c.color},children:l}),(0,W.jsx)(`span`,{children:`activity`}),(0,W.jsxs)(`b`,{children:[e.activity.calls,` calls · `,e.activity.errors,` err`,e.activity.avgMs?` · ${fx(e.activity.avgMs)} avg`:``]}),e.pool&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`span`,{children:`pool`}),(0,W.jsx)(`b`,{className:e.pool.dead?`text-destructive`:e.pool.inflight>0?`text-chart-1`:e.pool.pooled?`text-success`:`text-muted-foreground`,children:e.pool.pooled?e.pool.inflight>0?`${e.pool.inflight} inflight`:`connected`:`—`})]}),e.description&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`span`,{children:`note`}),(0,W.jsx)(`b`,{children:e.description})]})]}),m9(e)&&(0,W.jsxs)(`div`,{className:`border-warning/30 bg-warning/8 mt-2.5 mb-0.5 flex items-center gap-1.5 overflow-hidden rounded-[10px] border px-2.5 py-[7px] text-[10px]`,title:`Reached over SSH through the bastion ${m9(e)} (ProxyJump) — no port exposed on ${e.name}.`,children:[(0,W.jsxs)(`span`,{className:`border-warning/60 text-warning bg-background inline-flex items-center gap-1.5 rounded-[7px] border px-2 py-[3px] whitespace-nowrap`,children:[(0,W.jsx)(cl,{className:`size-3`}),` `,m9(e),e.jumpVia?(0,W.jsx)(`i`,{className:`bg-warning text-background rounded-[4px] px-1 py-px text-[7px] tracking-[0.08em] uppercase not-italic`,children:`jump`}):null]}),(0,W.jsx)(`span`,{className:`jump-route__wire jump-route__wire--enc`,children:(0,W.jsx)(`span`,{className:`jump-route__lock`,"aria-hidden":!0,children:`🔒`})}),(0,W.jsxs)(`span`,{className:`bg-background text-foreground inline-flex items-center gap-1.5 rounded-[7px] border px-2 py-[3px] whitespace-nowrap`,style:{borderColor:u},title:e.name,children:[(0,W.jsx)(tl,{className:`size-3`}),` `,e.name]})]}),(r||i)&&(0,W.jsxs)(`div`,{className:`mt-1.5 flex gap-2`,onClick:e=>e.stopPropagation(),children:[r&&(0,W.jsxs)(vb,{variant:`outline`,size:`xs`,disabled:!!a,onClick:()=>s(`test`),title:`Probe this device now (fresh SSH connect + health refresh)`,children:[a===`test`?(0,W.jsx)(Kc,{className:`animate-spin`}):(0,W.jsx)(Ac,{}),` Test`]}),i&&(0,W.jsxs)(vb,{variant:`outline`,size:`xs`,disabled:!!a,onClick:()=>s(`reconnect`),title:`Drop the pooled SSH connection and re-establish it`,children:[a===`reconnect`?(0,W.jsx)(Kc,{className:`animate-spin`}):(0,W.jsx)(il,{}),` Reconnect`]})]})]})}var Fte={READ:`text-success border-success/45 bg-success/10`,WRITE:`text-chart-1 border-chart-1/45 bg-chart-1/10`,WRITE_IDEMPOTENT:`text-chart-2 border-chart-2/45 bg-chart-2/10`,DESTRUCTIVE:`text-warning border-warning/45 bg-warning/10`,DANGEROUS:`text-destructive border-destructive/45 bg-destructive/10`},g9=`border-b border-border/60 px-3.5 py-[7px] text-xs [overflow-wrap:anywhere]`,_9=q(g9,`bg-muted/40 text-muted-foreground`),v9=q(g9,`text-foreground`),y9=`m-0 max-h-[40vh] overflow-auto rounded border border-border bg-background p-3 font-mono text-xs break-words whitespace-pre-wrap`,b9=`m-0 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase`;function Ite({event:e,onClose:t}){let[n,r]=(0,v.useState)(k7);return(0,W.jsx)(q7,{title:(0,W.jsxs)(`span`,{className:`flex items-center gap-2.5`,children:[(0,W.jsx)(`span`,{className:q(`inline-block rounded-full border px-2 py-px font-mono text-[10px] tracking-wide uppercase`,Fte[e.risk]??`text-muted-foreground`),children:e.risk}),(0,W.jsx)(`span`,{className:`font-mono text-[15px]`,children:e.tool}),(0,W.jsx)(ux,{text:e.tool,icon:!0,title:`Copy tool name`})]}),onClose:t,children:(0,W.jsxs)(`div`,{className:`grid gap-3`,children:[(0,W.jsxs)(`div`,{className:`grid grid-cols-[minmax(120px,0.4fr)_1fr] overflow-hidden rounded-lg border border-border font-mono`,children:[(0,W.jsx)(`div`,{className:_9,children:`title`}),(0,W.jsx)(`div`,{className:v9,children:e.title}),e.reason&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`div`,{className:q(g9,`bg-muted/40 text-warning`),children:`reason`}),(0,W.jsx)(`div`,{className:q(g9,`text-foreground italic`),children:e.reason})]}),(0,W.jsx)(`div`,{className:_9,children:`time`}),(0,W.jsx)(`div`,{className:v9,children:new Date(e.ts).toLocaleString(void 0,{hour12:!1})}),(0,W.jsx)(`div`,{className:_9,children:`device`}),(0,W.jsx)(`div`,{className:v9,children:e.device??`—`}),(0,W.jsx)(`div`,{className:_9,children:`transport`}),(0,W.jsx)(`div`,{className:v9,children:e.transport??`—`}),(0,W.jsx)(`div`,{className:_9,children:`duration`}),(0,W.jsx)(`div`,{className:v9,children:fx(e.durationMs)}),(0,W.jsx)(`div`,{className:_9,children:`status`}),(0,W.jsx)(`div`,{className:v9,children:(0,W.jsx)(`span`,{className:e.isError?`text-destructive`:`text-success`,children:e.isError?`error`:`ok`})}),(0,W.jsx)(`div`,{className:_9,children:`output size`}),(0,W.jsxs)(`div`,{className:v9,children:[px(e.outputBytes),e.truncated?` (truncated)`:``]}),(0,W.jsx)(`div`,{className:_9,children:`structured`}),(0,W.jsx)(`div`,{className:v9,children:e.hasStructured?`yes (renders an MCP App view)`:`no`})]}),e.error&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`h2`,{className:b9,children:`ERROR`}),(0,W.jsx)(`pre`,{className:q(y9,`text-destructive`),children:e.error})]}),(0,W.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[(0,W.jsx)(`h2`,{className:b9,children:`INPUT`}),(0,W.jsx)(`span`,{className:`flex-1`}),e.input&&(0,W.jsx)(X,{type:`secondary`,size:`sm`,ghost:!0,onClick:()=>r(e=>{let t=!e;return A7(t),t}),title:n?`Showing pretty-printed JSON — click for raw`:`Showing raw JSON — click to pretty-print`,children:n?`✦ Pretty`:`{ } Raw`}),(0,W.jsx)(ux,{text:e.input,title:`Copy input JSON`})]}),e.input?(0,W.jsx)(M7,{value:j7(e.input,n)}):(0,W.jsx)(`pre`,{className:q(y9,`text-muted-foreground`),children:`—`}),(0,W.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[(0,W.jsx)(`h2`,{className:b9,children:`OUTPUT`}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(ux,{text:e.output,title:`Copy output`})]}),(0,W.jsx)(`pre`,{className:q(y9,`text-muted-foreground`),children:e.output?D7(e.output):`—`})]})})}var x9=e=>e==null?`?`:px(e);function Lte({d:e}){let t=e.status,n=e.history??[];return t.reachable===!0||n.length>0?(0,W.jsxs)(`div`,{className:`bg-card text-card-foreground flex flex-col gap-2.5 rounded-lg border p-4`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(Wx,{color:s9(t).color}),(0,W.jsx)(`span`,{className:`font-mono text-[13px] font-medium`,children:e.name}),e.isDefault&&(0,W.jsx)(Hx,{type:`accent`,children:`default`}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(Hx,{type:t.version?`success`:`default`,children:t.version?`v${t.version}`:`—`})]}),(0,W.jsxs)(`div`,{className:`text-muted-foreground -mt-1 font-mono text-[11px]`,children:[t.boardName??`router`,t.architecture?` · ${t.architecture}`:``,t.cpuCount?` · ${t.cpuCount} cpu`:``,t.uptime?` · up ${t.uptime}`:``]}),(0,W.jsxs)(`div`,{className:`flex justify-around gap-3.5`,children:[(0,W.jsx)(l7,{value:t.cpuLoad,label:`CPU`,color:_x.cpu}),(0,W.jsx)(l7,{value:t.memUsedPct,label:`MEM`,color:_x.mem}),(0,W.jsx)(l7,{value:t.hddUsedPct,label:`DISK`,color:_x.disk})]}),(0,W.jsxs)(`div`,{className:`grid gap-2`,children:[(0,W.jsxs)(`div`,{className:`grid gap-0.5`,children:[(0,W.jsx)(`span`,{className:`text-muted-foreground font-mono text-[10px] tracking-[0.05em] uppercase`,children:`CPU load`}),(0,W.jsx)(c7,{id:`${e.name}-cpu`,values:n.map(e=>e.cpuLoad),color:_x.cpu,maxValue:100,unit:`%`})]}),(0,W.jsxs)(`div`,{className:`grid gap-0.5`,children:[(0,W.jsx)(`span`,{className:`text-muted-foreground font-mono text-[10px] tracking-[0.05em] uppercase`,children:`Memory used`}),(0,W.jsx)(c7,{id:`${e.name}-mem`,values:n.map(e=>e.memUsedPct),color:_x.mem,maxValue:100,unit:`%`})]}),(0,W.jsxs)(`div`,{className:`grid gap-0.5`,children:[(0,W.jsx)(`span`,{className:`text-muted-foreground font-mono text-[10px] tracking-[0.05em] uppercase`,children:`Probe latency`}),(0,W.jsx)(c7,{id:`${e.name}-lat`,values:n.map(e=>e.latencyMs),color:_x.latency,unit:`ms`})]})]}),(0,W.jsxs)(`div`,{className:`text-muted-foreground font-mono text-[11px]`,children:[`RAM `,x9(t.totalMemory&&t.freeMemory?t.totalMemory-t.freeMemory:void 0),` /`,` `,x9(t.totalMemory),` · free disk `,x9(t.freeHdd)]}),t.disks&&t.disks.length>0&&(0,W.jsxs)(`div`,{className:`grid gap-1`,children:[(0,W.jsx)(`span`,{className:`text-muted-foreground font-mono text-[10px] tracking-[0.05em] uppercase`,children:`External storage`}),t.disks.map(e=>(0,W.jsxs)(`div`,{className:`flex items-center justify-between gap-2 font-mono text-[11px]`,children:[(0,W.jsxs)(`span`,{className:`truncate`,children:[(0,W.jsx)(Hx,{type:`secondary`,children:e.slot}),` `,e.model??e.mountPoint??`disk`,e.fs?` · ${e.fs}`:``]}),(0,W.jsxs)(`span`,{className:`text-muted-foreground whitespace-nowrap`,children:[x9(e.free),` free / `,x9(e.size),e.usedPct==null?``:` · ${e.usedPct}%`]})]},e.slot))]})]}):(0,W.jsxs)(`div`,{className:`bg-card text-card-foreground flex min-h-[120px] flex-col justify-center gap-1.5 rounded-lg border p-4`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(Wx,{color:s9(t).color}),(0,W.jsx)(`span`,{className:`font-mono text-[13px] font-medium`,children:e.name}),e.isDefault&&(0,W.jsx)(Hx,{type:`accent`,children:`default`})]}),(0,W.jsx)(`p`,{className:`text-muted-foreground m-0 text-[11px]`,children:t.reachable===!1?`Offline — ${t.error??`unreachable`}`:e.mac?`Waiting for the first MAC-Telnet probe (these run every few minutes to avoid contending with tool calls)…`:`Waiting for the first health probe…`})]})}function Rte(e,t){let n=(0,v.useRef)(e),r=(0,v.useRef)(t);n.current=e,r.current=t,(0,v.useEffect)(()=>{let e=!1,t=null,i=null,a=()=>{t&&=(t.onopen=t.onerror=t.onmessage=t.onclose=null,t.close(),null),i&&=(i.close(),null)},o=()=>{e||(i=new EventSource(hc(`/api/sse`)),i.addEventListener(`hello`,()=>r.current(`sse`)),i.addEventListener(`tool`,e=>{try{n.current(JSON.parse(e.data))}catch{}}),i.onerror=()=>{i&&i.readyState===EventSource.CONNECTING&&r.current(`off`)})},s=()=>{if(e)return;let i=location.protocol===`https:`?`wss`:`ws`;t=new WebSocket(hc(`${i}://${location.host}/api/stream`));let a=!1;t.onopen=()=>{a=!0,r.current(`ws`)},t.onerror=()=>t?.close(),t.onmessage=e=>{try{let t=JSON.parse(e.data);t.type===`event`&&t.event&&n.current(t.event)}catch{}},t.onclose=()=>{e||(r.current(`off`),a?setTimeout(s,2e3):o())}},c=()=>t?.readyState===WebSocket.OPEN||i?.readyState===EventSource.OPEN,l=()=>{e||c()||(a(),s())},u=()=>{document.visibilityState===`visible`&&l()};return s(),document.addEventListener(`visibilitychange`,u),window.addEventListener(`online`,l),()=>{e=!0,document.removeEventListener(`visibilitychange`,u),window.removeEventListener(`online`,l),a()}},[])}function zte(e){(0,v.useLayoutEffect)(()=>{let t=e.current;if(!t||window.matchMedia(`(prefers-reduced-motion: reduce)`).matches)return;document.documentElement.classList.add(`js-motion`);let n=new WeakSet,r=e=>{n.has(e)||(n.add(e),zi.set(e,{opacity:0,y:26}),ic.create({trigger:e,start:`top 90%`,once:!0,onEnter:()=>zi.to(e,{opacity:1,y:0,duration:.7,ease:`power3.out`})}))},i=e=>{for(let t of e.querySelectorAll(`.reveal`))r(t)};i(t);let a=new MutationObserver(e=>{for(let t of e)for(let e of t.addedNodes)e instanceof Element&&(e.matches(`.reveal`)&&r(e),i(e))});a.observe(t,{childList:!0,subtree:!0});let o=setInterval(()=>ic.refresh(),1200),s=setTimeout(()=>clearInterval(o),7e3);return()=>{a.disconnect(),clearInterval(o),clearTimeout(s);for(let e of ic.getAll())e.kill();document.documentElement.classList.remove(`js-motion`)}},[e])}function S9(e){return new Date(e).toLocaleString()}function C9(e){let t=Date.now()-e;return t<6e4?`just now`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}var Bte={"in-sync":`success`,drifted:`warning`,unknown:`secondary`,"no-baseline":`secondary`},Vte={"in-sync":`In Sync`,drifted:`Drifted`,unknown:`Unknown`,"no-baseline":`No Baseline`};function Hte({device:e,onDone:t}){let[n,r]=(0,v.useState)([]),[i,a]=(0,v.useState)(``),[o,s]=(0,v.useState)(``),[c,l]=(0,v.useState)(!1),[u,d]=(0,v.useState)(``);(0,v.useEffect)(()=>{gc(`/api/drift/history/${encodeURIComponent(e)}?limit=20`).then(e=>{r(e.snapshots),e.snapshots.length>0&&a(e.snapshots[0].id)})},[e]);let f=async()=>{if(!i)return;l(!0),d(``);let n=await _c(`/api/drift/baseline`,{device:e,snapshotId:i,label:o||void 0});if(l(!1),n.ok)J.success(`Baseline set`),t();else{let e=n.error??`Failed`;d(e),J.error(e)}};return n.length===0?(0,W.jsx)(`div`,{className:`p-2`,children:(0,W.jsx)(`span`,{className:`text-muted-foreground text-[11px]`,children:`No snapshots for this device. Capture one with capture_config_snapshot first.`})}):(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 p-2`,children:[(0,W.jsx)(Xx,{value:i,onValueChange:a,"aria-label":`Baseline snapshot`,className:`min-w-[200px] flex-1`,options:n.map(e=>({value:e.id,label:`${e.id} — ${S9(e.ts)} — ${e.lines} lines${e.label?` "${e.label}"`:``}`}))}),(0,W.jsx)(Kx,{placeholder:`Label (optional)`,value:o,onChange:e=>s(e.target.value),className:`w-40`}),(0,W.jsx)(X,{size:`sm`,onClick:()=>void f(),disabled:c||!i,children:c?`Setting...`:`Set as Baseline`}),u&&(0,W.jsx)(`span`,{className:`text-destructive text-xs`,children:u})]})}function Ute({unified:e}){return e?(0,W.jsx)(`pre`,{className:`m-0 max-h-[500px] overflow-auto rounded bg-background p-3 font-mono text-[11px] leading-normal`,children:e.split(`
|
|
145
|
+
`).map((e,t)=>(0,W.jsx)(`div`,{className:e.startsWith(`+`)?`text-success`:e.startsWith(`-`)?`text-destructive`:e.startsWith(`@@`)?`text-brand`:``,children:e||` `},t))})]})]})}function Ate({x:e,y:t,text:n,className:r,textClassName:i,padX:a=9,fontSize:o=9}){let s=(0,v.useRef)(null),[c,l]=(0,v.useState)(()=>n.length*o*.62);(0,v.useLayoutEffect)(()=>{let e=s.current?.getComputedTextLength();e&&e>0&&l(e)},[n,o]);let u=Math.round(c+a*2);return(0,W.jsxs)(`g`,{transform:`translate(${e.toFixed(1)},${t.toFixed(1)})`,children:[(0,W.jsx)(`rect`,{className:r,x:-u/2,y:-9,rx:9,width:u,height:18}),(0,W.jsx)(`text`,{ref:s,className:i,x:0,y:3.5,textAnchor:`middle`,fontSize:o,children:n})]})}function s9(e){return e.reachable===!0?{label:`online`,color:`var(--foreground)`}:e.reachable===!1?{label:`offline`,color:`var(--destructive)`}:{label:`checking…`,color:`var(--muted-foreground)`}}function c9(e,t){let n=[...new Set(t)].sort(),r=Math.max(1,n.length),i=Math.max(0,n.indexOf(e));return`hsl(${Math.round(i*360/r)} 70% ${62-i%3*5}%)`}function l9(e,t,n,r){let i=1-r;return{x:i*i*e.x+2*i*r*t.x+r*r*n.x,y:i*i*e.y+2*i*r*t.y+r*r*n.y}}function u9(e,t,n,r){let i=1-r,a=2*i*(t.x-e.x)+2*r*(n.x-t.x),o=2*i*(t.y-e.y)+2*r*(n.y-t.y);return Math.atan2(o,a)*180/Math.PI}var d9=`var(--foreground)`,f9=`var(--muted-foreground)`,p9=`var(--warning)`;function m9(e){return e.jumpVia?e.jumpVia:e.jumpHost?`${e.jumpHost.host}:${e.jumpHost.port}`:null}var h9=18,jte=e=>e.length>h9?`${e.slice(0,h9-1)}…`:e,Mte=e=>Math.max(23,Math.min(70,e.length*3.1+12));function Nte({payload:e,pulses:t}){let n=e.devices,r=Math.max(1,n.length),i=n.map(e=>e.name),a=n.map(e=>{let t=jte(e.name);return{d:e,label:t,r:Mte(t)}}),o=Math.max(23,...a.map(e=>e.r)),s=r>1?(o+18)/Math.sin(Math.PI/r):0,c=Math.max(110+o,s,120),l=o+60,u=Math.max(700,Math.round((c+o+40)*2)),d=Math.round((c+l)*2),f=u/2,p=d/2,m={x:f,y:p},h=a.map((e,t)=>{let n=t/r*Math.PI*2-Math.PI/2+(r%2==0?Math.PI/r:0);return{...e,i:t,x:f+c*Math.cos(n),y:p+c*Math.sin(n)}});return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsxs)(`svg`,{className:`conn`,viewBox:`0 0 ${u} ${d}`,width:`100%`,height:d,preserveAspectRatio:`xMidYMid meet`,children:[(0,W.jsxs)(`defs`,{children:[(0,W.jsxs)(`radialGradient`,{id:`conn-hub`,cx:`0.5`,cy:`0.34`,r:`0.75`,children:[(0,W.jsx)(`stop`,{offset:`0`,stopColor:`var(--foreground)`}),(0,W.jsx)(`stop`,{offset:`0.6`,stopColor:`color-mix(in srgb, var(--foreground) 70%, var(--muted-foreground))`}),(0,W.jsx)(`stop`,{offset:`1`,stopColor:`var(--muted-foreground)`})]}),(0,W.jsxs)(`radialGradient`,{id:`conn-orb`,cx:`0.5`,cy:`0.32`,r:`0.85`,children:[(0,W.jsx)(`stop`,{offset:`0`,stopColor:`var(--muted)`}),(0,W.jsx)(`stop`,{offset:`1`,stopColor:`var(--card)`})]}),(0,W.jsxs)(`radialGradient`,{id:`conn-orb-veil`,cx:`0.5`,cy:`0.5`,r:`0.5`,children:[(0,W.jsx)(`stop`,{offset:`0`,stopColor:`var(--card)`,stopOpacity:`0.2`}),(0,W.jsx)(`stop`,{offset:`0.6`,stopColor:`var(--card)`,stopOpacity:`0.25`}),(0,W.jsx)(`stop`,{offset:`1`,stopColor:`var(--card)`,stopOpacity:`0.85`})]}),(0,W.jsxs)(`radialGradient`,{id:`conn-burst`,cx:`0.5`,cy:`0.5`,r:`0.5`,children:[(0,W.jsx)(`stop`,{offset:`0`,stopColor:`var(--foreground)`}),(0,W.jsx)(`stop`,{offset:`0.5`,stopColor:`color-mix(in srgb, var(--foreground) 80%, transparent)`}),(0,W.jsx)(`stop`,{offset:`1`,stopColor:`var(--muted-foreground)`,stopOpacity:`0`})]}),(0,W.jsxs)(`linearGradient`,{id:`conn-tunnel-grad`,x1:`0`,y1:`0`,x2:`1`,y2:`0`,children:[(0,W.jsx)(`stop`,{offset:`0`,stopColor:p9,stopOpacity:`0.15`}),(0,W.jsx)(`stop`,{offset:`0.5`,stopColor:p9,stopOpacity:`0.95`}),(0,W.jsx)(`stop`,{offset:`1`,stopColor:p9,stopOpacity:`0.15`})]}),(0,W.jsxs)(`filter`,{id:`conn-tunnel-glow`,x:`-40%`,y:`-40%`,width:`180%`,height:`180%`,children:[(0,W.jsx)(`feGaussianBlur`,{stdDeviation:`3`,result:`b`}),(0,W.jsxs)(`feMerge`,{children:[(0,W.jsx)(`feMergeNode`,{in:`b`}),(0,W.jsx)(`feMergeNode`,{in:`SourceGraphic`})]})]})]}),[.5,.78,1].map((e,t)=>(0,W.jsx)(`circle`,{className:`conn-grid`,cx:f,cy:p,r:c*e},`g-${t}`)),[0,1,2].map(e=>(0,W.jsx)(`circle`,{className:`conn-sonar`,cx:f,cy:p,style:{animationDelay:`${e*1.1}s`}},`s-${e}`)),h.map(({d:e,i:n,x:r,y:i})=>{let a=s9(e.status),o=e.status.reachable===!0,s=e.status.reachable==null,c={x:r,y:i},l=(f+r)/2,u=(p+i)/2,d=r-f,h=i-p,g=Math.hypot(d,h)||1,_=-h/g,v=d/g,y={x:l+_*16,y:u+v*16},b={x:l-_*16,y:u-v*16},x=`M${f},${p} Q${y.x.toFixed(1)},${y.y.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,S=`M${f},${p} Q${b.x.toFixed(1)},${b.y.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,C=`M${f},${p} Q${l.toFixed(1)},${u.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,w=l9(m,y,c,.52),T=u9(m,y,c,.52),E=l9(m,b,c,.48),D=u9(m,b,c,.48)+180,O=t[e.name]??0;return(0,W.jsxs)(`g`,{children:[(0,W.jsx)(`path`,{id:`conn-cmd-${n}`,className:`conn-link`,d:x,stroke:o?d9:a.color,strokeOpacity:o?.5:.32,strokeDasharray:s?`2 8`:o?void 0:`6 7`}),(0,W.jsx)(`path`,{id:`conn-res-${n}`,className:`conn-link`,d:S,stroke:o?f9:a.color,strokeOpacity:o?.5:.18,strokeDasharray:o?void 0:`6 7`}),o&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`path`,{className:`conn-flow`,d:x,stroke:`var(--foreground)`}),(0,W.jsx)(`circle`,{className:`conn-packet`,r:3.2,fill:`var(--foreground)`,children:(0,W.jsx)(`animateMotion`,{dur:`2.6s`,repeatCount:`indefinite`,calcMode:`linear`,children:(0,W.jsx)(`mpath`,{href:`#conn-cmd-${n}`})})}),(0,W.jsx)(`rect`,{className:`conn-packet`,x:-2.6,y:-2.6,width:5.2,height:5.2,fill:`var(--muted-foreground)`,transform:`rotate(45)`,children:(0,W.jsx)(`animateMotion`,{dur:`2.6s`,begin:`0.9s`,repeatCount:`indefinite`,calcMode:`linear`,keyPoints:`1;0`,keyTimes:`0;1`,children:(0,W.jsx)(`mpath`,{href:`#conn-res-${n}`})})}),(0,W.jsx)(`path`,{className:`conn-chevron`,d:`M-4,-3 L4,0 L-4,3`,stroke:`var(--foreground)`,transform:`translate(${w.x.toFixed(1)},${w.y.toFixed(1)}) rotate(${T.toFixed(1)})`}),(0,W.jsx)(`path`,{className:`conn-chevron`,d:`M-4,-3 L4,0 L-4,3`,stroke:`var(--muted-foreground)`,transform:`translate(${E.x.toFixed(1)},${E.y.toFixed(1)}) rotate(${D.toFixed(1)})`})]}),O>0&&(0,W.jsx)(`g`,{children:(0,W.jsxs)(`circle`,{r:6,fill:`url(#conn-burst)`,children:[(0,W.jsx)(`animateMotion`,{dur:`1.1s`,repeatCount:`1`,calcMode:`linear`,keyPoints:`0;1;0`,keyTimes:`0;0.5;1`,path:C}),(0,W.jsx)(`animate`,{attributeName:`opacity`,values:`0;1;1;0`,keyTimes:`0;0.1;0.85;1`,dur:`1.1s`,repeatCount:`1`,fill:`freeze`})]})},`burst-${e.name}-${O}`)]},`l-${e.name}`)}),h.map(e=>{let t=e.d.jumpVia,n=t?h.find(e=>e.d.name===t):void 0,r=!n&&e.d.jumpHost?e.d.jumpHost:void 0;if(!n&&!r)return null;let i={x:e.x,y:e.y},a=Math.hypot(e.x-f,e.y-p)||1,o=(e.x-f)/a,s=(e.y-p)/a,c=n?{x:n.x,y:n.y}:{x:e.x+o*(e.r+46),y:e.y+s*(e.r+46)},l=(c.x+i.x)/2,u=(c.y+i.y)/2,d=Math.hypot(l-f,u-p)||1,m=n?50:16,g={x:l+(l-f)/d*m,y:u+(u-p)/d*m},_=`M${c.x.toFixed(1)},${c.y.toFixed(1)} Q${g.x.toFixed(1)},${g.y.toFixed(1)} ${i.x.toFixed(1)},${i.y.toFixed(1)}`,v=m9(e.d)??``,y=l9(c,g,i,.5),b=`conn-tunnel-${e.i}`;return(0,W.jsxs)(`g`,{className:`conn-tunnel-g`,children:[(0,W.jsx)(`path`,{className:`conn-tunnel-halo`,d:_,stroke:p9}),(0,W.jsx)(`path`,{id:b,className:`conn-tunnel`,d:_,stroke:`url(#conn-tunnel-grad)`,filter:`url(#conn-tunnel-glow)`}),r&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`circle`,{className:`conn-tunnel-sat`,cx:c.x,cy:c.y,r:11,fill:`var(--background)`,stroke:`var(--warning)`}),(0,W.jsx)(`text`,{x:c.x,y:c.y+3.5,textAnchor:`middle`,fontSize:11,children:`🛡️`})]}),(0,W.jsxs)(`text`,{className:`conn-tunnel-lock`,fontSize:13,textAnchor:`middle`,children:[`🔒`,(0,W.jsx)(`animateMotion`,{dur:`2.4s`,repeatCount:`indefinite`,calcMode:`linear`,rotate:`auto`,children:(0,W.jsx)(`mpath`,{href:`#${b}`})})]}),(0,W.jsx)(Ate,{x:y.x,y:y.y,text:`⤳ via ${v}`,className:`conn-tunnel-badge`,textClassName:`conn-tunnel-badge-tx`})]},`jump-${e.d.name}`)}),(0,W.jsx)(`circle`,{className:`conn-hub-glow`,cx:f,cy:p,r:42}),(0,W.jsx)(`circle`,{className:`conn-hub-ring`,cx:f,cy:p,r:37}),(0,W.jsx)(`circle`,{cx:f,cy:p,r:29,fill:`url(#conn-hub)`,stroke:`var(--border)`,strokeWidth:1.5}),(0,W.jsx)(`text`,{x:f,y:p-4,textAnchor:`middle`,fill:`var(--background)`,fontSize:12,fontWeight:700,children:`LLM`}),(0,W.jsx)(`text`,{x:f,y:p+8,textAnchor:`middle`,fill:`var(--background)`,fillOpacity:.66,fontSize:8,fontWeight:600,children:`⇄ MCP`}),(0,W.jsx)(`text`,{x:f,y:p+18,textAnchor:`middle`,fill:`var(--background)`,fillOpacity:.66,fontSize:7.5,fontWeight:600,children:`server`}),h.map(({d:e,x:t,y:n,r,label:a,i:o})=>{let s=s9(e.status),c=e.status.reachable===!0,l=c?`${e.status.latencyMs??`?`} ms`:s.label,u=c9(e.name,i);return(0,W.jsxs)(`g`,{className:`conn-node`,opacity:e.disabled?.35:1,children:[c&&(0,W.jsx)(`circle`,{className:`conn-node-halo`,cx:t,cy:n,r:r+1,stroke:u}),e.pool?.pooled&&(0,W.jsx)(`circle`,{cx:t,cy:n,r:r+5,fill:`none`,stroke:e.pool.inflight>0?`var(--chart-1)`:`var(--success)`,strokeWidth:1.5,strokeDasharray:e.pool.inflight>0?void 0:`4 4`,opacity:.5,className:e.pool.inflight>0?`conn-blink`:void 0}),(0,W.jsx)(`circle`,{cx:t,cy:n,r,fill:`url(#conn-orb)`}),e.geo?.countryCode?(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`clipPath`,{id:`conn-orb-clip-${o}`,children:(0,W.jsx)(`circle`,{cx:t,cy:n,r})}),(0,W.jsx)(`image`,{href:hc(`/api/flag/${e.geo.countryCode}`),x:t-r,y:n-r,width:r*2,height:r*2,clipPath:`url(#conn-orb-clip-${o})`,preserveAspectRatio:`xMidYMid slice`,opacity:.6,children:(0,W.jsx)(`title`,{children:e.geo.city?`${e.geo.country} · ${e.geo.city}`:e.geo.country})}),(0,W.jsx)(`circle`,{cx:t,cy:n,r,fill:`url(#conn-orb-veil)`})]}):(0,W.jsx)(`circle`,{cx:t,cy:n,r,fill:u,opacity:.16}),(0,W.jsx)(`circle`,{cx:t,cy:n,r,fill:`none`,stroke:u,strokeWidth:2.5}),(0,W.jsx)(`circle`,{className:c?`conn-blink`:void 0,cx:t+r*.7,cy:n-r*.7,r:4.5,fill:s.color,stroke:`var(--background)`,strokeWidth:1.5}),(0,W.jsx)(`text`,{x:t,y:n+3.5,textAnchor:`middle`,fill:`var(--foreground)`,fontSize:10,fontWeight:600,stroke:`var(--card)`,strokeWidth:2.75,paintOrder:`stroke`,children:a}),(0,W.jsx)(`text`,{x:t,y:n+r+14,textAnchor:`middle`,fill:`var(--muted-foreground)`,fontSize:9,children:e.address??e.host}),(0,W.jsx)(`text`,{x:t,y:n+r+26,textAnchor:`middle`,fill:s.color,fontSize:9,fontWeight:600,children:l})]},`n-${e.name}`)})]}),(0,W.jsxs)(`div`,{className:`text-muted-foreground mt-2 flex flex-wrap justify-center gap-4 text-[11px] [&>span]:inline-flex [&>span]:items-center [&>span]:gap-1.5 [&_i]:inline-block [&_i]:size-2.5 [&_i]:rounded-[3px]`,children:[(0,W.jsxs)(`span`,{children:[(0,W.jsx)(`i`,{style:{background:d9}}),` command · LLM → device`]}),(0,W.jsxs)(`span`,{children:[(0,W.jsx)(`i`,{style:{background:f9}}),` response · device → LLM`]}),(0,W.jsxs)(`span`,{children:[(0,W.jsx)(`i`,{style:{background:d9}}),` live call (round-trip)`]}),e.devices.some(e=>m9(e))&&(0,W.jsxs)(`span`,{children:[(0,W.jsx)(`i`,{style:{background:`var(--warning)`}}),` 🔒 SSH jump tunnel (ProxyJump)`]}),e.devices.some(e=>e.pool?.pooled)&&(0,W.jsxs)(`span`,{children:[(0,W.jsx)(`i`,{style:{background:`var(--success)`}}),` pooled SSH connection`]})]})]})}function Pte({d:e,allNames:t,onToggle:n,onTest:r,onReconnect:i}){let[a,o]=(0,v.useState)(null),s=t=>{if(a)return;let n=t===`test`?r:i;n&&(o(t),n(e.name).finally(()=>o(null)))},c=s9(e.status),l=e.status.reachable===!0?`${c.label} · ${e.status.latencyMs??`?`}ms${e.status.version?` · v${e.status.version}`:``}`:e.status.reachable===!1?`${c.label}${e.status.error?` · ${e.status.error}`:``}`:c.label,u=c9(e.name,t);return(0,W.jsxs)(`div`,{className:q(`bg-card grid gap-2 rounded-lg border px-[15px] py-3.5 transition-[transform,border-color] duration-300`,`hover:border-chart-2/35 hover:-translate-y-0.5`,e.disabled&&`opacity-50 grayscale-[0.4]`),style:{borderLeft:`3px solid ${u}`},"data-disabled":e.disabled?`1`:void 0,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(`span`,{className:`size-[9px] shrink-0 rounded-full`,style:{background:u},title:`device colour`}),e.geo?.countryCode&&(0,W.jsx)(`img`,{src:hc(`/api/flag/${e.geo.countryCode}`),alt:e.geo.country,title:e.geo.city?`${e.geo.country} · ${e.geo.city}`:e.geo.country,className:`size-4 shrink-0`,loading:`lazy`}),(0,W.jsx)(`span`,{className:`text-[13px] font-medium`,children:e.name}),e.isDefault&&(0,W.jsx)(Hx,{type:`accent`,children:`default`}),e.disabled&&(0,W.jsx)(Hx,{type:`warning`,children:`disabled`}),(0,W.jsx)(`span`,{className:`size-[7px] shrink-0 rounded-full`,style:{background:c.color},title:c.label}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(`span`,{onClick:e=>e.stopPropagation(),children:(0,W.jsx)(Y7,{checked:!e.disabled,onCheckedChange:()=>n?.(e.name,!e.disabled),title:e.disabled?`Enable device`:`Disable device`,"aria-label":e.disabled?`Enable device`:`Disable device`})}),(0,W.jsx)(Hx,{type:`secondary`,children:e.authMode})]}),(0,W.jsxs)(`div`,{className:`text-muted-foreground [&_b]:text-foreground grid grid-cols-[auto_1fr] gap-x-3 gap-y-[3px] text-[11px] [&_b]:font-medium [&_b]:break-words`,children:[(0,W.jsx)(`span`,{children:e.mac?`mac`:`host`}),(0,W.jsx)(`b`,{children:e.address??`${e.host}:${e.port}`}),(0,W.jsx)(`span`,{children:`user`}),(0,W.jsx)(`b`,{children:e.username}),(0,W.jsx)(`span`,{children:`status`}),(0,W.jsx)(`b`,{style:{color:c.color},children:l}),(0,W.jsx)(`span`,{children:`activity`}),(0,W.jsxs)(`b`,{children:[e.activity.calls,` calls · `,e.activity.errors,` err`,e.activity.avgMs?` · ${fx(e.activity.avgMs)} avg`:``]}),e.pool&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`span`,{children:`pool`}),(0,W.jsx)(`b`,{className:e.pool.dead?`text-destructive`:e.pool.inflight>0?`text-chart-1`:e.pool.pooled?`text-success`:`text-muted-foreground`,children:e.pool.pooled?e.pool.inflight>0?`${e.pool.inflight} inflight`:`connected`:`—`})]}),e.description&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`span`,{children:`note`}),(0,W.jsx)(`b`,{children:e.description})]})]}),m9(e)&&(0,W.jsxs)(`div`,{className:`border-warning/30 bg-warning/8 mt-2.5 mb-0.5 flex items-center gap-1.5 overflow-hidden rounded-[10px] border px-2.5 py-[7px] text-[10px]`,title:`Reached over SSH through the bastion ${m9(e)} (ProxyJump) — no port exposed on ${e.name}.`,children:[(0,W.jsxs)(`span`,{className:`border-warning/60 text-warning bg-background inline-flex items-center gap-1.5 rounded-[7px] border px-2 py-[3px] whitespace-nowrap`,children:[(0,W.jsx)(cl,{className:`size-3`}),` `,m9(e),e.jumpVia?(0,W.jsx)(`i`,{className:`bg-warning text-background rounded-[4px] px-1 py-px text-[7px] tracking-[0.08em] uppercase not-italic`,children:`jump`}):null]}),(0,W.jsx)(`span`,{className:`jump-route__wire jump-route__wire--enc`,children:(0,W.jsx)(`span`,{className:`jump-route__lock`,"aria-hidden":!0,children:`🔒`})}),(0,W.jsxs)(`span`,{className:`bg-background text-foreground inline-flex items-center gap-1.5 rounded-[7px] border px-2 py-[3px] whitespace-nowrap`,style:{borderColor:u},title:e.name,children:[(0,W.jsx)(tl,{className:`size-3`}),` `,e.name]})]}),(r||i)&&(0,W.jsxs)(`div`,{className:`mt-1.5 flex gap-2`,onClick:e=>e.stopPropagation(),children:[r&&(0,W.jsxs)(vb,{variant:`outline`,size:`xs`,disabled:!!a,onClick:()=>s(`test`),title:`Probe this device now (fresh SSH connect + health refresh)`,children:[a===`test`?(0,W.jsx)(Kc,{className:`animate-spin`}):(0,W.jsx)(Ac,{}),` Test`]}),i&&(0,W.jsxs)(vb,{variant:`outline`,size:`xs`,disabled:!!a,onClick:()=>s(`reconnect`),title:`Drop the pooled SSH connection and re-establish it`,children:[a===`reconnect`?(0,W.jsx)(Kc,{className:`animate-spin`}):(0,W.jsx)(il,{}),` Reconnect`]})]})]})}var Fte={READ:`text-success border-success/45 bg-success/10`,WRITE:`text-chart-1 border-chart-1/45 bg-chart-1/10`,WRITE_IDEMPOTENT:`text-chart-2 border-chart-2/45 bg-chart-2/10`,DESTRUCTIVE:`text-warning border-warning/45 bg-warning/10`,DANGEROUS:`text-destructive border-destructive/45 bg-destructive/10`},g9=`border-b border-border/60 px-3.5 py-[7px] text-xs [overflow-wrap:anywhere]`,_9=q(g9,`bg-muted/40 text-muted-foreground`),v9=q(g9,`text-foreground`),y9=`m-0 max-h-[40vh] overflow-auto rounded border border-border bg-background p-3 font-mono text-xs break-words whitespace-pre-wrap`,b9=`m-0 text-[11px] font-semibold tracking-wide text-muted-foreground uppercase`;function Ite({event:e,onClose:t}){let[n,r]=(0,v.useState)(k7);return(0,W.jsx)(q7,{title:(0,W.jsxs)(`span`,{className:`flex items-center gap-2.5`,children:[(0,W.jsx)(`span`,{className:q(`inline-block rounded-full border px-2 py-px font-mono text-[10px] tracking-wide uppercase`,Fte[e.risk]??`text-muted-foreground`),children:e.risk}),(0,W.jsx)(`span`,{className:`font-mono text-[15px]`,children:e.tool}),(0,W.jsx)(ux,{text:e.tool,icon:!0,title:`Copy tool name`})]}),onClose:t,children:(0,W.jsxs)(`div`,{className:`grid gap-3`,children:[(0,W.jsxs)(`div`,{className:`grid grid-cols-[minmax(120px,0.4fr)_1fr] overflow-hidden rounded-lg border border-border font-mono`,children:[(0,W.jsx)(`div`,{className:_9,children:`title`}),(0,W.jsx)(`div`,{className:v9,children:e.title}),e.reason&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`div`,{className:q(g9,`bg-muted/40 text-warning`),children:`reason`}),(0,W.jsx)(`div`,{className:q(g9,`text-foreground italic`),children:e.reason})]}),(0,W.jsx)(`div`,{className:_9,children:`time`}),(0,W.jsx)(`div`,{className:v9,children:new Date(e.ts).toLocaleString(void 0,{hour12:!1})}),(0,W.jsx)(`div`,{className:_9,children:`device`}),(0,W.jsx)(`div`,{className:v9,children:e.device??`—`}),(0,W.jsx)(`div`,{className:_9,children:`transport`}),(0,W.jsx)(`div`,{className:v9,children:e.transport??`—`}),(0,W.jsx)(`div`,{className:_9,children:`duration`}),(0,W.jsx)(`div`,{className:v9,children:fx(e.durationMs)}),(0,W.jsx)(`div`,{className:_9,children:`status`}),(0,W.jsx)(`div`,{className:v9,children:(0,W.jsx)(`span`,{className:e.isError?`text-destructive`:`text-success`,children:e.isError?`error`:`ok`})}),(0,W.jsx)(`div`,{className:_9,children:`output size`}),(0,W.jsxs)(`div`,{className:v9,children:[px(e.outputBytes),e.truncated?` (truncated)`:``]}),(0,W.jsx)(`div`,{className:_9,children:`structured`}),(0,W.jsx)(`div`,{className:v9,children:e.hasStructured?`yes (renders an MCP App view)`:`no`})]}),e.error&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`h2`,{className:b9,children:`ERROR`}),(0,W.jsx)(`pre`,{className:q(y9,`text-destructive`),children:e.error})]}),(0,W.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[(0,W.jsx)(`h2`,{className:b9,children:`INPUT`}),(0,W.jsx)(`span`,{className:`flex-1`}),e.input&&(0,W.jsx)(X,{type:`secondary`,size:`sm`,ghost:!0,onClick:()=>r(e=>{let t=!e;return A7(t),t}),title:n?`Showing pretty-printed JSON — click for raw`:`Showing raw JSON — click to pretty-print`,children:n?`✦ Pretty`:`{ } Raw`}),(0,W.jsx)(ux,{text:e.input,title:`Copy input JSON`})]}),e.input?(0,W.jsx)(M7,{value:j7(e.input,n)}):(0,W.jsx)(`pre`,{className:q(y9,`text-muted-foreground`),children:`—`}),(0,W.jsxs)(`div`,{className:`flex items-center gap-2.5`,children:[(0,W.jsx)(`h2`,{className:b9,children:`OUTPUT`}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(ux,{text:e.output,title:`Copy output`})]}),(0,W.jsx)(`pre`,{className:q(y9,`text-muted-foreground`),children:e.output?D7(e.output):`—`})]})})}var x9=e=>e==null?`?`:px(e);function Lte({d:e}){let t=e.status,n=e.history??[];return t.reachable===!0||n.length>0?(0,W.jsxs)(`div`,{className:`bg-card text-card-foreground flex flex-col gap-2.5 rounded-lg border p-4`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(Wx,{color:s9(t).color}),(0,W.jsx)(`span`,{className:`font-mono text-[13px] font-medium`,children:e.name}),e.isDefault&&(0,W.jsx)(Hx,{type:`accent`,children:`default`}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(Hx,{type:t.version?`success`:`default`,children:t.version?`v${t.version}`:`—`})]}),(0,W.jsxs)(`div`,{className:`text-muted-foreground -mt-1 font-mono text-[11px]`,children:[t.boardName??`router`,t.architecture?` · ${t.architecture}`:``,t.cpuCount?` · ${t.cpuCount} cpu`:``,t.uptime?` · up ${t.uptime}`:``]}),(0,W.jsxs)(`div`,{className:`flex justify-around gap-3.5`,children:[(0,W.jsx)(l7,{value:t.cpuLoad,label:`CPU`,color:_x.cpu}),(0,W.jsx)(l7,{value:t.memUsedPct,label:`MEM`,color:_x.mem}),(0,W.jsx)(l7,{value:t.hddUsedPct,label:`DISK`,color:_x.disk})]}),(0,W.jsxs)(`div`,{className:`grid gap-2`,children:[(0,W.jsxs)(`div`,{className:`grid gap-0.5`,children:[(0,W.jsx)(`span`,{className:`text-muted-foreground font-mono text-[10px] tracking-[0.05em] uppercase`,children:`CPU load`}),(0,W.jsx)(c7,{id:`${e.name}-cpu`,values:n.map(e=>e.cpuLoad),color:_x.cpu,maxValue:100,unit:`%`})]}),(0,W.jsxs)(`div`,{className:`grid gap-0.5`,children:[(0,W.jsx)(`span`,{className:`text-muted-foreground font-mono text-[10px] tracking-[0.05em] uppercase`,children:`Memory used`}),(0,W.jsx)(c7,{id:`${e.name}-mem`,values:n.map(e=>e.memUsedPct),color:_x.mem,maxValue:100,unit:`%`})]}),(0,W.jsxs)(`div`,{className:`grid gap-0.5`,children:[(0,W.jsx)(`span`,{className:`text-muted-foreground font-mono text-[10px] tracking-[0.05em] uppercase`,children:`Probe latency`}),(0,W.jsx)(c7,{id:`${e.name}-lat`,values:n.map(e=>e.latencyMs),color:_x.latency,unit:`ms`})]})]}),(0,W.jsxs)(`div`,{className:`text-muted-foreground font-mono text-[11px]`,children:[`RAM `,x9(t.totalMemory&&t.freeMemory?t.totalMemory-t.freeMemory:void 0),` /`,` `,x9(t.totalMemory),` · free disk `,x9(t.freeHdd)]}),t.disks&&t.disks.length>0&&(0,W.jsxs)(`div`,{className:`grid gap-1`,children:[(0,W.jsx)(`span`,{className:`text-muted-foreground font-mono text-[10px] tracking-[0.05em] uppercase`,children:`External storage`}),t.disks.map(e=>(0,W.jsxs)(`div`,{className:`flex items-center justify-between gap-2 font-mono text-[11px]`,children:[(0,W.jsxs)(`span`,{className:`truncate`,children:[(0,W.jsx)(Hx,{type:`secondary`,children:e.slot}),` `,e.model??e.mountPoint??`disk`,e.fs?` · ${e.fs}`:``]}),(0,W.jsxs)(`span`,{className:`text-muted-foreground whitespace-nowrap`,children:[x9(e.free),` free / `,x9(e.size),e.usedPct==null?``:` · ${e.usedPct}%`]})]},e.slot))]})]}):(0,W.jsxs)(`div`,{className:`bg-card text-card-foreground flex min-h-[120px] flex-col justify-center gap-1.5 rounded-lg border p-4`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(Wx,{color:s9(t).color}),(0,W.jsx)(`span`,{className:`font-mono text-[13px] font-medium`,children:e.name}),e.isDefault&&(0,W.jsx)(Hx,{type:`accent`,children:`default`})]}),(0,W.jsx)(`p`,{className:`text-muted-foreground m-0 text-[11px]`,children:t.reachable===!1?`Offline — ${t.error??`unreachable`}`:e.mac?`Waiting for the first MAC-Telnet probe (these run every few minutes to avoid contending with tool calls)…`:`Waiting for the first health probe…`})]})}function Rte(e,t){let n=(0,v.useRef)(e),r=(0,v.useRef)(t);n.current=e,r.current=t,(0,v.useEffect)(()=>{let e=!1,t=null,i=null,a=()=>{t&&=(t.onopen=t.onerror=t.onmessage=t.onclose=null,t.close(),null),i&&=(i.close(),null)},o=()=>{e||(i=new EventSource(hc(`/api/sse`)),i.addEventListener(`hello`,()=>r.current(`sse`)),i.addEventListener(`tool`,e=>{try{n.current(JSON.parse(e.data))}catch{}}),i.onerror=()=>{i&&i.readyState===EventSource.CONNECTING&&r.current(`off`)})},s=()=>{if(e)return;let i=location.protocol===`https:`?`wss`:`ws`;t=new WebSocket(hc(`${i}://${location.host}/api/stream`));let a=!1;t.onopen=()=>{a=!0,r.current(`ws`)},t.onerror=()=>t?.close(),t.onmessage=e=>{try{let t=JSON.parse(e.data);t.type===`event`&&t.event&&n.current(t.event)}catch{}},t.onclose=()=>{e||(r.current(`off`),a?setTimeout(s,2e3):o())}},c=()=>t?.readyState===WebSocket.OPEN||i?.readyState===EventSource.OPEN,l=()=>{e||c()||(a(),s())},u=()=>{document.visibilityState===`visible`&&l()};return s(),document.addEventListener(`visibilitychange`,u),window.addEventListener(`online`,l),()=>{e=!0,document.removeEventListener(`visibilitychange`,u),window.removeEventListener(`online`,l),a()}},[])}function zte(e){(0,v.useLayoutEffect)(()=>{let t=e.current;if(!t||window.matchMedia(`(prefers-reduced-motion: reduce)`).matches)return;document.documentElement.classList.add(`js-motion`);let n=new WeakSet,r=e=>{n.has(e)||(n.add(e),zi.set(e,{opacity:0,y:26}),ic.create({trigger:e,start:`top 90%`,once:!0,onEnter:()=>zi.to(e,{opacity:1,y:0,duration:.7,ease:`power3.out`})}))},i=e=>{for(let t of e.querySelectorAll(`.reveal`))r(t)};i(t);let a=new MutationObserver(e=>{for(let t of e)for(let e of t.addedNodes)e instanceof Element&&(e.matches(`.reveal`)&&r(e),i(e))});a.observe(t,{childList:!0,subtree:!0});let o=setInterval(()=>ic.refresh(),1200),s=setTimeout(()=>clearInterval(o),7e3);return()=>{a.disconnect(),clearInterval(o),clearTimeout(s);for(let e of ic.getAll())e.kill();document.documentElement.classList.remove(`js-motion`)}},[e])}function S9(e){return new Date(e).toLocaleString()}function C9(e){let t=Date.now()-e;return t<6e4?`just now`:t<36e5?`${Math.floor(t/6e4)}m ago`:t<864e5?`${Math.floor(t/36e5)}h ago`:`${Math.floor(t/864e5)}d ago`}var Bte={"in-sync":`success`,drifted:`warning`,unknown:`secondary`,"no-baseline":`secondary`},Vte={"in-sync":`In Sync`,drifted:`Drifted`,unknown:`Unknown`,"no-baseline":`No Baseline`};function Hte({device:e,onDone:t}){let[n,r]=(0,v.useState)([]),[i,a]=(0,v.useState)(``),[o,s]=(0,v.useState)(``),[c,l]=(0,v.useState)(!1),[u,d]=(0,v.useState)(``);(0,v.useEffect)(()=>{gc(`/api/drift/history/${encodeURIComponent(e)}?limit=20`).then(e=>{r(e.snapshots),e.snapshots.length>0&&a(e.snapshots[0].id)})},[e]);let f=async()=>{if(!i)return;l(!0),d(``);let n=await _c(`/api/drift/baseline`,{device:e,snapshotId:i,label:o||void 0});if(l(!1),n.ok)J.success(`Baseline set`),t();else{let e=n.error??`Failed`;d(e),J.error(e)}};return n.length===0?(0,W.jsx)(`div`,{className:`p-2`,children:(0,W.jsx)(`span`,{className:`text-muted-foreground text-[11px]`,children:`No snapshots for this device. Capture one with capture_config_snapshot first.`})}):(0,W.jsxs)(`div`,{className:`flex flex-wrap items-center gap-2 p-2`,children:[(0,W.jsx)(Xx,{value:i,onValueChange:a,"aria-label":`Baseline snapshot`,className:`min-w-[200px] flex-1`,options:n.map(e=>({value:e.id,label:`${e.id} — ${S9(e.ts)} — ${e.lines} lines${e.label?` "${e.label}"`:``}`}))}),(0,W.jsx)(Kx,{placeholder:`Label (optional)`,value:o,onChange:e=>s(e.target.value),className:`w-40`}),(0,W.jsx)(X,{size:`sm`,onClick:()=>void f(),disabled:c||!i,children:c?`Setting...`:`Set as Baseline`}),u&&(0,W.jsx)(`span`,{className:`text-destructive text-xs`,children:u})]})}function Ute({unified:e}){return e?(0,W.jsx)(`pre`,{className:`m-0 max-h-[500px] overflow-auto rounded bg-background p-3 font-mono text-[11px] leading-normal`,children:e.split(`
|
|
146
146
|
`).map((e,t)=>{let n=`text-muted-foreground`;return e.startsWith(`+`)?n=`text-success bg-success/10`:e.startsWith(`-`)?n=`text-destructive bg-destructive/10`:e.startsWith(`@@`)?n=`text-brand`:e.startsWith(`/`)&&(n=`text-warning`),(0,W.jsx)(`div`,{className:q(`px-1`,n),children:e||`\xA0`},t)})}):(0,W.jsx)(`span`,{className:`text-muted-foreground text-[11px]`,children:`No differences.`})}function Wte({sections:e}){if(e.length===0)return null;let t=Math.max(...e.map(e=>e.added+e.removed),1);return(0,W.jsx)(`div`,{className:`flex flex-col gap-1.5`,children:e.slice(0,15).map(e=>(0,W.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,W.jsx)(`span`,{className:`w-[220px] truncate text-[11px] text-foreground`,title:e.path,children:e.path}),(0,W.jsxs)(`div`,{className:`flex h-3.5 flex-1 overflow-hidden rounded-[3px] bg-muted`,children:[(0,W.jsx)(`div`,{className:`h-full bg-success`,style:{width:`${e.added/t*100}%`}}),(0,W.jsx)(`div`,{className:`h-full bg-destructive`,style:{width:`${e.removed/t*100}%`}})]}),(0,W.jsxs)(`span`,{className:`min-w-[60px] text-[11px] text-muted-foreground`,children:[`+`,e.added,` -`,e.removed]})]},e.path))})}function Gte({attributions:e}){return e.length===0?(0,W.jsx)(`span`,{className:`text-muted-foreground text-[11px]`,children:`No config-related log entries found.`}):(0,W.jsx)(`div`,{className:`max-h-[250px] overflow-auto`,children:(0,W.jsxs)(Qb,{className:`text-[11px]`,children:[(0,W.jsx)($b,{children:(0,W.jsxs)(tx,{children:[(0,W.jsx)(Y,{children:`Time`}),(0,W.jsx)(Y,{children:`User`}),(0,W.jsx)(Y,{children:`Action`}),(0,W.jsx)(Y,{children:`Section`}),(0,W.jsx)(Y,{children:`Log`})]})}),(0,W.jsx)(ex,{children:e.map((e,t)=>(0,W.jsxs)(tx,{children:[(0,W.jsx)(nx,{className:`whitespace-nowrap`,children:e.timestamp??`—`}),(0,W.jsx)(nx,{children:e.user??`—`}),(0,W.jsx)(nx,{children:(0,W.jsx)(`span`,{className:`rounded bg-muted px-1.5 py-px text-[10px]`,children:e.action??`?`})}),(0,W.jsx)(nx,{className:`max-w-[160px] truncate`,title:e.section,children:e.section}),(0,W.jsx)(nx,{className:`max-w-[300px] truncate text-muted-foreground`,title:e.logLine,children:e.logLine})]},t))})]})})}function Kte({device:e,onClose:t,onChanged:n}){let[r,i]=(0,v.useState)(null),[a,o]=(0,v.useState)(!1),[s,c]=(0,v.useState)(``),l=(0,v.useCallback)(async()=>{o(!0),c(``);try{let t=await gc(`/api/drift/check/${encodeURIComponent(e)}`);`error`in t?c(t.error):i(t)}catch(e){c(e instanceof Error?e.message:String(e))}o(!1)},[e]),u=async()=>{let t=await _c(`/api/drift/baseline`,{device:e,snapshotId:r?.baselineId,label:`promoted`});t.error?J.error(t.error):(J.success(`Drift promoted`),n())},d=async()=>{await vc(`/api/drift/baseline/${encodeURIComponent(e)}`,{}),J.success(`Baseline removed`),n()};return(0,W.jsxs)(sx,{title:`${e} — Drift Detail`,className:`reveal`,extra:(0,W.jsxs)(`div`,{className:`flex gap-2`,children:[(0,W.jsx)(X,{size:`sm`,onClick:()=>void l(),disabled:a,children:a?`Checking...`:`Check Now`}),(0,W.jsx)(X,{size:`sm`,type:`error`,ghost:!0,onClick:()=>void d(),children:`Remove Baseline`}),(0,W.jsx)(X,{size:`sm`,type:`secondary`,onClick:t,children:`Close`})]}),children:[s&&(0,W.jsx)(`div`,{className:`text-destructive`,children:s}),r&&(0,W.jsxs)(`div`,{children:[(0,W.jsxs)(`div`,{className:`mb-3 flex items-center gap-6 border-b border-border py-3`,children:[(0,W.jsxs)(`div`,{className:`flex items-center gap-1.5`,children:[(0,W.jsx)(Wx,{type:r.identical?`success`:`warning`}),(0,W.jsx)(`strong`,{children:r.identical?`In Sync`:`Drifted`})]}),!r.identical&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsxs)(`div`,{children:[`Score: `,(0,W.jsx)(`strong`,{children:r.score}),`/100`]}),(0,W.jsxs)(`div`,{className:`text-success`,children:[`+`,r.summary.added]}),(0,W.jsxs)(`div`,{className:`text-destructive`,children:[`-`,r.summary.removed]}),(0,W.jsxs)(`div`,{className:`text-muted-foreground text-[11px]`,children:[r.summary.unchanged,` unchanged`]}),(0,W.jsx)(X,{size:`sm`,onClick:()=>void u(),children:`Promote as Baseline`})]}),(0,W.jsxs)(`div`,{className:`text-muted-foreground ml-auto text-[11px]`,children:[`Checked: `,S9(r.capturedAt)]})]}),r.sections.length>0&&(0,W.jsxs)(`div`,{className:`mb-4`,children:[(0,W.jsx)(`h4`,{className:`mt-0 mb-2 text-[13px]`,children:`Sections with drift`}),(0,W.jsx)(Wte,{sections:r.sections})]}),r.attributions.length>0&&(0,W.jsxs)(`div`,{className:`mb-4`,children:[(0,W.jsx)(`h4`,{className:`mt-0 mb-2 text-[13px]`,children:`Change attribution`}),(0,W.jsx)(Gte,{attributions:r.attributions})]}),!r.identical&&(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`h4`,{className:`mt-0 mb-2 text-[13px]`,children:`Unified diff`}),(0,W.jsx)(Ute,{unified:r.unified})]})]}),!r&&!a&&!s&&(0,W.jsx)(`p`,{className:`text-muted-foreground text-center text-[11px]`,children:`Click "Check Now" to run a live drift check against the golden baseline.`})]})}function qte(){let[e,t]=(0,v.useState)([]),[n,r]=(0,v.useState)([]),[i,a]=(0,v.useState)(null),[o,s]=(0,v.useState)(null),[c,l]=(0,v.useState)(``),u=(0,v.useCallback)(async()=>{try{let[e,n]=await Promise.all([gc(`/api/drift/status`),gc(`/api/drift/baselines`)]);t(e.devices),r(n.baselines),l(``)}catch(e){l(e instanceof Error?e.message:String(e))}},[]);if((0,v.useEffect)(()=>{u();let e=setInterval(()=>void u(),15e3);return()=>clearInterval(e)},[u]),c&&e.length===0)return(0,W.jsx)(sx,{className:`reveal`,children:(0,W.jsx)(`p`,{className:`text-destructive`,children:c})});let d=e.filter(e=>e.status!==`no-baseline`);return(0,W.jsxs)(W.Fragment,{children:[(0,W.jsxs)(`div`,{className:`reveal grid grid-cols-2 gap-3 sm:grid-cols-4`,children:[(0,W.jsx)(cx,{k:`Devices`,v:String(e.length)}),(0,W.jsx)(cx,{k:`With Baseline`,v:String(d.length)}),(0,W.jsx)(cx,{k:`In Sync`,v:String(e.filter(e=>e.status===`in-sync`).length)}),(0,W.jsx)(cx,{k:`Drifted`,v:String(e.filter(e=>e.status===`drifted`).length)})]}),(0,W.jsx)(sx,{title:`Fleet Drift Overview`,className:`reveal`,children:(0,W.jsx)(`div`,{className:`grid grid-cols-[repeat(auto-fill,minmax(240px,1fr))] gap-3`,children:e.map(e=>(0,W.jsxs)(`div`,{onClick:()=>{e.status!==`no-baseline`&&a(e.device)},className:q(`rounded-lg border bg-card p-4 transition-colors`,i===e.device?`border-brand`:`border-border`,e.status===`no-baseline`?`cursor-default`:`cursor-pointer hover:border-brand/40`),children:[(0,W.jsxs)(`div`,{className:`mb-2 flex justify-between`,children:[(0,W.jsx)(`strong`,{className:`text-sm`,children:e.device}),(0,W.jsx)(Hx,{type:Bte[e.status],children:Vte[e.status]})]}),e.baseline&&(0,W.jsxs)(`div`,{className:`text-muted-foreground text-[11px]`,children:[(0,W.jsxs)(`div`,{children:[`Baseline: `,C9(e.baseline.setAt)]}),e.baseline.label&&(0,W.jsxs)(`div`,{children:[`Label: `,e.baseline.label]})]}),e.status===`no-baseline`&&(0,W.jsx)(X,{size:`sm`,className:`mt-2`,onClick:t=>{t.stopPropagation(),s(e.device)},children:`Set Baseline`})]},e.device))})}),o&&(0,W.jsx)(sx,{title:`Set baseline for ${o}`,className:`reveal`,extra:(0,W.jsx)(X,{size:`sm`,type:`secondary`,onClick:()=>s(null),children:`Cancel`}),children:(0,W.jsx)(Hte,{device:o,onDone:()=>{s(null),u()}})}),i&&(0,W.jsx)(Kte,{device:i,onClose:()=>a(null),onChanged:()=>{a(null),u()}}),n.length>0&&(0,W.jsx)(sx,{title:`Baseline Manager`,className:`reveal`,children:(0,W.jsx)(`div`,{className:`overflow-x-auto`,children:(0,W.jsxs)(Qb,{className:`text-xs`,children:[(0,W.jsx)($b,{children:(0,W.jsxs)(tx,{children:[(0,W.jsx)(Y,{children:`Device`}),(0,W.jsx)(Y,{children:`Snapshot ID`}),(0,W.jsx)(Y,{children:`Set At`}),(0,W.jsx)(Y,{children:`Set By`}),(0,W.jsx)(Y,{children:`Label`}),(0,W.jsx)(Y,{children:`Size`}),(0,W.jsx)(Y,{children:`SHA`}),(0,W.jsx)(Y,{})]})}),(0,W.jsx)(ex,{children:n.map(e=>(0,W.jsxs)(tx,{children:[(0,W.jsx)(nx,{children:(0,W.jsx)(`strong`,{children:e.device})}),(0,W.jsx)(nx,{className:`font-mono text-[11px]`,children:e.snapshotId}),(0,W.jsx)(nx,{title:S9(e.setAt),children:C9(e.setAt)}),(0,W.jsx)(nx,{children:e.setBy}),(0,W.jsx)(nx,{children:e.label??`—`}),(0,W.jsx)(nx,{children:e.snapshot?`${e.snapshot.lines} lines / ${e.snapshot.bytes} bytes`:`deleted`}),(0,W.jsx)(nx,{className:`font-mono text-[10px]`,children:e.snapshot?.sha?.slice(0,8)??`—`}),(0,W.jsx)(nx,{children:(0,W.jsx)(X,{size:`sm`,type:`error`,ghost:!0,onClick:()=>{vc(`/api/drift/baseline/${encodeURIComponent(e.device)}`,{}).then(()=>{J.success(`Baseline removed`),u()})},children:`Remove`})})]},e.device))})]})})}),e.length===0&&!c&&(0,W.jsx)(sx,{className:`reveal`,children:(0,W.jsx)(`p`,{className:`text-muted-foreground text-center text-[11px]`,children:`No devices configured. Add devices to start using Drift Guard.`})})]})}function Jte(e){let t=0;for(let n=0;n<e.length;n++)t=t*31+e.charCodeAt(n)&2147483647;return t%360}var w9=[`var(--entity-1)`,`var(--entity-2)`,`var(--entity-3)`,`var(--entity-4)`,`var(--entity-5)`,`var(--entity-6)`,`var(--entity-7)`,`var(--entity-8)`,`var(--entity-9)`,`var(--entity-10)`];function T9(e){return w9[Math.abs(Jte(e))%w9.length]}var Yte={create_entity:`Created entity`,delete_entity:`Deleted entity`,create_relation:`Created relation`,delete_relation:`Deleted relation`,add_observation:`Added observations`,delete_observation:`Deleted observations`};function Xte(e,t,n,r){let i=n/2,a=r/2,o=e.map((t,o)=>{let s=2*Math.PI*o/Math.max(e.length,1),c=Math.min(n,r)*.3;return{id:t.name,label:t.name,type:t.entityType,x:i+c*Math.cos(s),y:a+c*Math.sin(s),vx:0,vy:0,obsCount:t.observations.length}}),s=t.map(e=>({source:e.from,target:e.to,label:e.relationType})),c=new Map(o.map(e=>[e.id,e])),l=.005,u=.85;for(let e=0;e<200;e++){for(let e=0;e<o.length;e++)for(let t=e+1;t<o.length;t++){let n=o[e],r=o[t],i=n.x-r.x,a=n.y-r.y,s=Math.sqrt(i*i+a*a)||1,c=3e3/(s*s);i=i/s*c,a=a/s*c,n.vx+=i,n.vy+=a,r.vx-=i,r.vy-=a}for(let e of s){let t=c.get(e.source),n=c.get(e.target);if(!t||!n)continue;let r=n.x-t.x,i=n.y-t.y,a=r*l,o=i*l;t.vx+=a,t.vy+=o,n.vx-=a,n.vy-=o}for(let e of o)e.vx+=(i-e.x)*.001,e.vy+=(a-e.y)*.001;for(let e of o){e.vx*=u,e.vy*=u,e.x+=e.vx,e.y+=e.vy;let t=Math.max(40,e.label.length*2.7+16);e.x=Math.max(t,Math.min(n-t,e.x)),e.y=Math.max(30,Math.min(r-30,e.y))}}return{nodes:o,edges:s}}function Zte({graph:e}){let t=(0,v.useRef)(null),[n,r]=(0,v.useState)(null),i=(0,v.useMemo)(()=>Xte(e.entities,e.relations,700,460),[e.entities,e.relations]),a=(0,v.useMemo)(()=>new Map(i.nodes.map(e=>[e.id,e])),[i.nodes]);return e.entities.length===0?(0,W.jsx)(`div`,{className:`text-muted-foreground flex items-center justify-center`,style:{height:460},children:(0,W.jsx)(`p`,{children:`No entities yet — create some via the MCP tools.`})}):(0,W.jsxs)(`svg`,{ref:t,viewBox:`0 0 700 460`,style:{width:`100%`,height:`auto`,maxHeight:460,display:`block`},children:[(0,W.jsx)(`defs`,{children:(0,W.jsx)(`marker`,{id:`mem-arrow`,viewBox:`0 0 10 6`,refX:`10`,refY:`3`,markerWidth:`8`,markerHeight:`6`,orient:`auto-start-reverse`,children:(0,W.jsx)(`path`,{d:`M0 0 L10 3 L0 6Z`,fill:`var(--muted-foreground)`})})}),i.edges.map((e,t)=>{let r=a.get(e.source),i=a.get(e.target);if(!r||!i)return null;let o=n!=null&&(e.source===n||e.target===n),s=n!=null&&!o,c=i.x-r.x,l=i.y-r.y,u=Math.sqrt(c*c+l*l)||1,d=e=>{let t=e.label.length*5.4,n=13+Math.min(e.obsCount,6);return Math.max(n,t/2+12)},f=d(r)+4,p=d(i)+4,m=r.x+c/u*f,h=r.y+l/u*f,g=i.x-c/u*p,_=i.y-l/u*p,v=(m+g)/2,y=(h+_)/2;return(0,W.jsxs)(`g`,{opacity:s?.15:1,children:[(0,W.jsx)(`line`,{x1:m,y1:h,x2:g,y2:_,stroke:o?`var(--foreground)`:`var(--border)`,strokeWidth:o?1.8:1,markerEnd:`url(#mem-arrow)`}),(0,W.jsx)(`text`,{x:v,y:y-5,textAnchor:`middle`,fill:`var(--muted-foreground)`,fontSize:`8`,style:{pointerEvents:`none`},children:e.label})]},t)}),i.nodes.map(e=>{let t=e.id===n,i=n!=null&&!t,a=T9(e.type),o=e.label,s=o.length*5.4,c=13+Math.min(e.obsCount,6),l=Math.max(c,s/2+12),u=c;return(0,W.jsxs)(`g`,{opacity:i?.25:1,style:{cursor:`pointer`},onClick:()=>r(t?null:e.id),children:[(0,W.jsx)(`rect`,{x:e.x-l,y:e.y-c,width:l*2,height:c*2,rx:u,ry:u,fill:a,fillOpacity:.15,stroke:t?`var(--foreground)`:a,strokeWidth:t?2:1.2}),(0,W.jsx)(`text`,{x:e.x,y:e.y+1,textAnchor:`middle`,dominantBaseline:`central`,fill:`var(--foreground)`,fontSize:`9`,fontWeight:t?600:400,style:{pointerEvents:`none`},children:o}),(0,W.jsx)(`text`,{x:e.x,y:e.y+c+11,textAnchor:`middle`,fill:`var(--muted-foreground)`,fontSize:`7`,style:{pointerEvents:`none`},children:e.type})]},e.id)})]})}function Qte({entity:e,relations:t,onClose:n,onDelete:r}){let i=t.filter(t=>t.to===e.name),a=t.filter(t=>t.from===e.name);return(0,W.jsxs)(`div`,{className:`rounded-lg border border-border bg-card p-5`,children:[(0,W.jsxs)(`div`,{className:`mb-2 flex items-center gap-2.5`,children:[(0,W.jsxs)(`h3`,{className:`m-0 flex items-center font-mono text-[15px]`,children:[(0,W.jsx)(Wx,{color:T9(e.entityType),className:`mr-2 size-2.5`}),e.name]}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(X,{type:`error`,ghost:!0,size:`sm`,onClick:r,title:`Delete entity`,children:`Delete`}),(0,W.jsx)(X,{type:`secondary`,size:`sm`,onClick:n,children:`Close`})]}),(0,W.jsxs)(`p`,{className:`mt-0 mb-2 text-xs text-muted-foreground`,children:[`Type: `,(0,W.jsx)(`strong`,{children:e.entityType}),` · Created:`,` `,new Date(e.createdAt).toLocaleString(),` · Updated:`,` `,new Date(e.updatedAt).toLocaleString()]}),e.observations.length>0&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsxs)(`h4`,{className:`mt-3 mb-1.5 text-xs text-muted-foreground`,children:[`Observations (`,e.observations.length,`)`]}),(0,W.jsx)(`ul`,{className:`m-0 pl-[18px] text-[13px]`,children:e.observations.map((e,t)=>(0,W.jsx)(`li`,{className:`mb-0.5`,children:e},t))})]}),(a.length>0||i.length>0)&&(0,W.jsxs)(W.Fragment,{children:[(0,W.jsx)(`h4`,{className:`mt-3 mb-1.5 text-xs text-muted-foreground`,children:`Relations`}),(0,W.jsxs)(`div`,{className:`text-[13px]`,children:[a.map((e,t)=>(0,W.jsxs)(`div`,{children:[`→ `,(0,W.jsx)(`strong`,{children:e.relationType}),` → `,e.to]},`o${t}`)),i.map((e,t)=>(0,W.jsxs)(`div`,{children:[`← `,(0,W.jsx)(`strong`,{children:e.relationType}),` ← `,e.from]},`i${t}`))]})]})]})}function $te({activity:e}){return e.length===0?(0,W.jsx)(`p`,{className:`text-muted-foreground text-[13px]`,children:`No activity yet.`}):(0,W.jsx)(`div`,{className:`max-h-[280px] overflow-y-auto`,children:(0,W.jsxs)(Qb,{className:`text-xs`,children:[(0,W.jsx)($b,{children:(0,W.jsxs)(tx,{children:[(0,W.jsx)(Y,{children:`Time`}),(0,W.jsx)(Y,{children:`Action`}),(0,W.jsx)(Y,{children:`Subject`})]})}),(0,W.jsx)(ex,{children:e.map(e=>(0,W.jsxs)(tx,{children:[(0,W.jsx)(nx,{className:`whitespace-nowrap text-muted-foreground`,children:mx(e.ts)}),(0,W.jsx)(nx,{children:Yte[e.action]??e.action}),(0,W.jsx)(nx,{className:`max-w-[260px] truncate`,children:e.subject})]},e.id))})]})})}function ene({config:e,onSaved:t}){let[n,r]=(0,v.useState)(e.dbPath),[i,a]=(0,v.useState)(!1),[o,s]=(0,v.useState)(``);(0,v.useEffect)(()=>{r(e.dbPath)},[e.dbPath]);let c=(0,v.useCallback)(async()=>{a(!0),s(``);try{let e=await _c(`/api/memory/config`,{dbPath:n});if(e.ok)s(`Saved`),J.success(`Config saved`),t();else{let t=e.error??`Failed`;s(t),J.error(t)}}catch(e){s(String(e)),J.error(String(e))}finally{a(!1)}},[n,t]);return(0,W.jsxs)(`div`,{children:[(0,W.jsx)(`label`,{className:`mb-1 block text-xs text-muted-foreground`,children:`Database path`}),(0,W.jsxs)(`div`,{className:`flex gap-1.5`,children:[(0,W.jsx)(Kx,{type:`text`,value:n,onChange:e=>r(e.target.value),className:`flex-1 text-[13px]`}),(0,W.jsx)(X,{size:`sm`,onClick:c,disabled:i||n===e.dbPath,children:i?`Saving…`:`Save`})]}),o&&(0,W.jsx)(`p`,{className:q(`mt-1 text-xs`,o===`Saved`?`text-success`:`text-destructive`),children:o}),e.stats&&(0,W.jsxs)(`div`,{className:`mt-3 grid grid-cols-3 gap-3`,children:[(0,W.jsx)(cx,{k:`Entities`,v:String(e.stats.entities)}),(0,W.jsx)(cx,{k:`Relations`,v:String(e.stats.relations)}),(0,W.jsx)(cx,{k:`Observations`,v:String(e.stats.observations)})]})]})}function tne(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(null),[i,a]=(0,v.useState)(null),[o,s]=(0,v.useState)([]),[c,l]=(0,v.useState)(null),[u,d]=(0,v.useState)(``),[f,p]=(0,v.useState)(``),m=(0,v.useCallback)(async()=>{try{let[e,n,i,o]=await Promise.all([gc(`/api/memory/graph`),gc(`/api/memory/stats`),gc(`/api/memory/config`),gc(`/api/memory/activity?limit=50`)]);t(e),r(n),a(i),s(o),p(``)}catch(e){p(String(e))}},[]);(0,v.useEffect)(()=>{m();let e=setInterval(()=>void m(),1e4);return()=>clearInterval(e)},[m]);let h=(0,v.useCallback)(async()=>{if(!u.trim()){m();return}try{let e=await gc(`/api/memory/search?q=${encodeURIComponent(u)}`);t(e)}catch(e){p(String(e))}},[u,m]),g=(0,v.useCallback)(async e=>{try{await vc(`/api/memory/entities`,{names:[e]}),J.success(`Entity deleted`)}catch(e){J.error(e instanceof Error?e.message:`Entity deletion failed`)}l(null),m()},[m]);return f&&!e?(0,W.jsx)(sx,{title:`Knowledge Graph`,children:(0,W.jsx)(`p`,{className:`text-destructive`,children:f})}):(0,W.jsxs)(W.Fragment,{children:[n&&(0,W.jsxs)(`div`,{className:`reveal grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-5`,children:[(0,W.jsx)(cx,{k:`Entities`,v:String(n.entities)}),(0,W.jsx)(cx,{k:`Relations`,v:String(n.relations)}),(0,W.jsx)(cx,{k:`Observations`,v:String(n.observations)}),(0,W.jsx)(cx,{k:`Entity types`,v:String(n.entityTypes.length)}),(0,W.jsx)(cx,{k:`Relation types`,v:String(n.relationTypes.length)})]}),(0,W.jsx)(sx,{title:`Knowledge Graph`,className:`reveal`,extra:(0,W.jsxs)(`div`,{className:`flex gap-1.5`,children:[(0,W.jsx)(Kx,{type:`text`,placeholder:`Search entities…`,value:u,onChange:e=>d(e.target.value),onKeyDown:e=>e.key===`Enter`&&h(),className:`w-[200px] text-xs`}),(0,W.jsx)(X,{size:`sm`,onClick:h,children:`Search`}),u&&(0,W.jsx)(X,{size:`sm`,type:`secondary`,onClick:()=>{d(``),m()},children:`Clear`})]}),children:e&&(0,W.jsx)(Zte,{graph:e})}),(0,W.jsxs)(`div`,{className:`reveal grid grid-cols-2 gap-4`,children:[(0,W.jsx)(sx,{title:`Entities`,children:e&&e.entities.length>0?(0,W.jsx)(`div`,{className:`max-h-[360px] overflow-y-auto`,children:(0,W.jsxs)(Qb,{className:`text-xs`,children:[(0,W.jsx)($b,{children:(0,W.jsxs)(tx,{children:[(0,W.jsx)(Y,{children:`Name`}),(0,W.jsx)(Y,{children:`Type`}),(0,W.jsx)(Y,{children:`Obs`}),(0,W.jsx)(Y,{children:`Created`})]})}),(0,W.jsx)(ex,{children:e.entities.map(e=>(0,W.jsxs)(tx,{className:`cursor-pointer`,"data-state":c?.name===e.name?`selected`:void 0,onClick:()=>l(e),children:[(0,W.jsxs)(nx,{children:[(0,W.jsx)(Wx,{color:T9(e.entityType),className:`mr-1.5`}),e.name]}),(0,W.jsx)(nx,{className:`text-muted-foreground`,children:e.entityType}),(0,W.jsx)(nx,{children:e.observations.length}),(0,W.jsx)(nx,{className:`text-muted-foreground`,children:mx(e.createdAt)})]},e.name))})]})}):(0,W.jsx)(`p`,{className:`text-muted-foreground text-[13px]`,children:e?`No entities.`:`Loading…`})}),c&&e?(0,W.jsx)(Qte,{entity:c,relations:e.relations,onClose:()=>l(null),onDelete:()=>g(c.name)}):(0,W.jsx)(sx,{title:`Activity Log`,children:(0,W.jsx)($te,{activity:o})})]}),n&&(n.entityTypes.length>0||n.relationTypes.length>0)&&(0,W.jsxs)(`div`,{className:`reveal grid grid-cols-2 gap-4`,children:[n.entityTypes.length>0&&(0,W.jsx)(sx,{title:`Entity Types`,children:(0,W.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:n.entityTypes.map(e=>(0,W.jsxs)(`span`,{className:`inline-flex items-center gap-1 rounded-xl px-2.5 py-0.5 text-xs`,style:{background:`${T9(e.type)}22`,border:`1px solid ${T9(e.type)}44`},children:[(0,W.jsx)(Wx,{color:T9(e.type)}),e.type,(0,W.jsxs)(`span`,{className:`text-muted-foreground`,children:[`(`,e.count,`)`]})]},e.type))})}),n.relationTypes.length>0&&(0,W.jsx)(sx,{title:`Relation Types`,children:(0,W.jsx)(`div`,{className:`flex flex-wrap gap-2`,children:n.relationTypes.map(e=>(0,W.jsxs)(`span`,{className:`inline-flex items-center gap-1 rounded-xl border border-border bg-muted px-2.5 py-0.5 text-xs`,children:[e.type,(0,W.jsxs)(`span`,{className:`text-muted-foreground`,children:[`(`,e.count,`)`]})]},e.type))})})]}),i&&(0,W.jsx)(sx,{title:`Memory Configuration`,className:`reveal`,children:(0,W.jsx)(ene,{config:i,onSaved:m})})]})}var E9=e=>q(`flex items-start gap-2.5 rounded-md border px-[11px] py-[9px] cursor-pointer select-none transition-colors`,e?`border-brand/50 bg-brand/10`:`border-border bg-card hover:border-brand/35`);function nne({m:e,busy:t,onToggle:n}){return(0,W.jsxs)(`label`,{className:E9(e.enabled),title:e.description,children:[(0,W.jsx)(`input`,{type:`checkbox`,className:`mt-0.5 cursor-pointer accent-brand`,checked:e.enabled,disabled:t,onChange:()=>n(e.slug,!e.enabled)}),(0,W.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,W.jsxs)(`span`,{className:`flex flex-wrap items-center gap-[7px] text-xs font-semibold text-foreground`,children:[e.label,(0,W.jsx)(`code`,{className:`rounded border border-border bg-background px-[5px] py-px font-mono text-[10px] text-muted-foreground`,children:e.slug})]}),(0,W.jsx)(`span`,{className:`text-[11px] leading-[1.35] text-muted-foreground`,children:e.description})]}),(0,W.jsxs)(`span`,{className:`whitespace-nowrap pt-px font-mono text-[10px] text-muted-foreground`,children:[e.toolCount,` tool`,e.toolCount===1?``:`s`]})]})}function rne(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(new Set),[i,a]=(0,v.useState)(null),[o,s]=(0,v.useState)(``),[c,l]=(0,v.useState)(!1),[u,d]=(0,v.useState)(!1),f=(0,v.useCallback)(()=>{gc(`/api/modules`).then(e=>{t(e),e.appViews!==void 0&&l(e.appViews)}).catch(()=>a(`could not load modules`))},[]);(0,v.useEffect)(()=>f(),[f]);let p=e=>t(t=>t?{...t,...e}:e),m=(0,v.useCallback)(async(e,n)=>{r(t=>new Set(t).add(e)),t(t=>t&&{...t,modules:t.modules.map(t=>t.slug===e?{...t,enabled:n}:t)});let i=await _c(`/api/modules/toggle`,{slug:e,enabled:n}).catch(()=>({error:`request failed`}));if(r(t=>{let n=new Set(t);return n.delete(e),n}),i.error||i.ok===!1){a(i.error??`toggle failed`),J.error(i.error??`Toggle failed`),f();return}p(i);let o=i.persisted?`saved to config`:`applied live (not saved)`,s=i.warning?` ⚠ ${i.warning}`:``;a(`${e} ${n?`enabled`:`disabled`} — ${o}. Reconnect the MCP client (or restart the server) for the tool list to update.${s}`),J.success(`Module ${n?`enabled`:`disabled`}`)},[f]),h=(0,v.useCallback)(async(e,t)=>{for(let n of e)await m(n,t)},[m]),g=(0,v.useCallback)(async e=>{d(!0),l(e);let t=await _c(`/api/modules/app-views`,{enabled:e}).catch(()=>({error:`request failed`}));if(d(!1),`error`in t&&t.error){a(t.error),J.error(t.error),l(!e);return}`appViews`in t&&t.appViews!==void 0&&l(t.appViews);let n=`persisted`in t&&t.persisted?`saved to config`:`applied live (not saved)`,r=`warning`in t&&t.warning?` — ${t.warning}`:``;a(`App views ${e?`enabled`:`disabled`} — ${n}. Restart the server for the change to take effect.${r}`),J.success(`App Views ${e?`enabled`:`disabled`}`)},[]),_=(0,v.useMemo)(()=>{if(!e)return[];let t=o.trim().toLowerCase(),n=e=>!t||e.slug.toLowerCase().includes(t)||e.label.toLowerCase().includes(t)||e.group.toLowerCase().includes(t)||e.description.toLowerCase().includes(t),r=new Map;for(let t of e.modules){if(!n(t))continue;let e=r.get(t.group)??[];e.push(t),r.set(t.group,e)}return[...r.entries()].map(([e,t])=>({group:e,modules:t}))},[e,o]);if(!e)return(0,W.jsx)(`div`,{className:`text-muted-foreground text-[11px]`,children:`loading modules…`});let y=_.reduce((e,t)=>e+t.modules.length,0);return(0,W.jsx)(`section`,{className:`grid content-start gap-[18px]`,children:(0,W.jsxs)(sx,{title:`Tool modules`,className:`reveal`,extra:(0,W.jsxs)(`div`,{className:`flex items-center gap-3`,children:[(0,W.jsxs)(`span`,{className:`text-muted-foreground text-[11px]`,children:[e.enabledModules,`/`,e.total,` modules · `,e.enabledTools,`/`,e.totalTools,` `,`tools exposed`]}),(0,W.jsx)(X,{size:`sm`,ghost:!0,icon:(0,W.jsx)(rl,{}),onClick:f,children:`Refresh`})]}),children:[(0,W.jsxs)(`label`,{className:q(E9(c),`mb-2.5`),title:`Emit MCP App view metadata (_meta.ui) on read tools`,children:[(0,W.jsx)(`input`,{type:`checkbox`,className:`mt-0.5 cursor-pointer accent-brand`,checked:c,disabled:u,onChange:()=>void g(!c)}),(0,W.jsxs)(`span`,{className:`flex min-w-0 flex-1 flex-col gap-0.5`,children:[(0,W.jsx)(`span`,{className:`flex flex-wrap items-center gap-[7px] text-xs font-semibold text-foreground`,children:`MCP App Views`}),(0,W.jsxs)(`span`,{className:`text-[11px] leading-[1.35] text-muted-foreground`,children:[`When on, read tools emit interactive table/detail widgets via `,(0,W.jsx)(`code`,{children:`_meta.ui`}),`. Disable to keep the LLM context lean. Requires server restart.`]})]})]}),(0,W.jsxs)(`div`,{className:`mb-2.5 flex flex-wrap gap-3 font-mono text-[11px] text-muted-foreground`,children:[(0,W.jsxs)(`span`,{className:`inline-flex items-center gap-[5px]`,children:[`writes to: `,(0,W.jsx)(`code`,{children:e.source?.path??`config file`})]}),(0,W.jsx)(`span`,{className:`inline-flex items-center gap-[5px]`,children:e.hasAllowList?`allow-list active`:`all modules on by default`})]}),(0,W.jsxs)(`p`,{className:`mb-3 text-muted-foreground text-xs`,children:[`Toggle a module to expose or hide all of its tools. Disabling adds it to`,` `,(0,W.jsx)(`code`,{children:`tools.disabledModules`}),` in your config file; enabling removes it (or adds it to`,` `,(0,W.jsx)(`code`,{children:`tools.enabledModules`}),` when an allow-list is in force). Trimming the surface below ~150–200 tools makes every remaining tool reliably findable by the MCP client. The client must reconnect for changes to take effect.`]}),(0,W.jsxs)(`div`,{className:`mb-1.5 flex flex-wrap items-center gap-2`,children:[(0,W.jsx)(Kx,{placeholder:`Filter modules by name, slug, group or description…`,value:o,onChange:e=>s(e.target.value)}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(X,{size:`sm`,onClick:()=>void h(e.modules.filter(e=>!e.enabled).map(e=>e.slug),!0),children:`Enable all`}),(0,W.jsx)(X,{size:`sm`,onClick:()=>void h(e.modules.filter(e=>e.enabled).map(e=>e.slug),!1),children:`Disable all`})]}),i&&(0,W.jsx)(`div`,{className:`mt-3 font-mono text-xs text-muted-foreground`,children:i}),y===0?(0,W.jsxs)(`div`,{className:`p-3 text-muted-foreground text-[11px]`,children:[`No modules match “`,o,`”.`]}):(0,W.jsx)(`div`,{className:`mt-3 flex flex-col gap-[18px]`,children:_.map(({group:e,modules:t})=>{let r=t.filter(e=>e.enabled).length,i=t.map(e=>e.slug);return(0,W.jsxs)(`div`,{children:[(0,W.jsxs)(`div`,{className:`mb-2 flex items-center gap-2.5 border-b border-border pb-1.5`,children:[(0,W.jsx)(`h3`,{className:`m-0 text-[13px] font-semibold text-foreground`,children:e}),(0,W.jsxs)(`span`,{className:`text-muted-foreground text-[11px]`,children:[r,`/`,t.length,` on`]}),(0,W.jsx)(`span`,{className:`flex-1`}),(0,W.jsx)(X,{size:`sm`,onClick:()=>void h(i.filter(e=>{let n=t.find(t=>t.slug===e);return n?!n.enabled:!1}),!0),children:`Enable group`}),(0,W.jsx)(X,{size:`sm`,onClick:()=>void h(i.filter(e=>{let n=t.find(t=>t.slug===e);return n?n.enabled:!1}),!1),children:`Disable group`})]}),(0,W.jsx)(`div`,{className:`grid grid-cols-[repeat(auto-fill,minmax(min(100%,360px),1fr))] gap-2`,children:t.map(e=>(0,W.jsx)(nne,{m:e,busy:n.has(e.slug),onToggle:(e,t)=>void m(e,t)},e.slug))})]},e)})})]})})}function ine({...e}){return(0,W.jsx)(_f,{"data-slot":`dialog`,...e})}function ane({...e}){return(0,W.jsx)(Cf,{"data-slot":`dialog-portal`,...e})}function one({className:e,...t}){return(0,W.jsx)(Tf,{"data-slot":`dialog-overlay`,className:q(`fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0`,e),...t})}function sne({className:e,children:t,showCloseButton:n=!0,...r}){return(0,W.jsxs)(ane,{"data-slot":`dialog-portal`,children:[(0,W.jsx)(one,{}),(0,W.jsxs)(kf,{"data-slot":`dialog-content`,className:q(`fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg`,e),...r,children:[t,n&&(0,W.jsxs)(Rf,{"data-slot":`dialog-close`,className:`absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,children:[(0,W.jsx)(_l,{}),(0,W.jsx)(`span`,{className:`sr-only`,children:`Close`})]})]})]})}function cne({className:e,...t}){return(0,W.jsx)(`div`,{"data-slot":`dialog-header`,className:q(`flex flex-col gap-2 text-center sm:text-left`,e),...t})}function lne({className:e,showCloseButton:t=!1,children:n,...r}){return(0,W.jsxs)(`div`,{"data-slot":`dialog-footer`,className:q(`flex flex-col-reverse gap-2 sm:flex-row sm:justify-end`,e),...r,children:[n,t&&(0,W.jsx)(Rf,{asChild:!0,children:(0,W.jsx)(vb,{variant:`outline`,children:`Close`})})]})}function une({className:e,...t}){return(0,W.jsx)(Pf,{"data-slot":`dialog-title`,className:q(`text-lg leading-none font-semibold`,e),...t})}function dne({className:e,...t}){return(0,W.jsx)(If,{"data-slot":`dialog-description`,className:q(`text-sm text-muted-foreground`,e),...t})}var D9=` {0,3}`;function O9(e){let t=e.replace(/\r\n?/g,`
|
|
147
147
|
`);t=t.replace(/&/g,`&`).replace(/</g,`<`).replace(/>/g,`>`);let n=[];t=t.replace(/```(\w*)\n([\s\S]*?)```/g,(e,t,r)=>(n.push(`<pre><code>${r.trim()}</code></pre>`),`%%CB${n.length-1}%%`));let r=[];t=t.replace(/`([^`]+)`/g,(e,t)=>(r.push(`<code>${t}</code>`),`%%IC${r.length-1}%%`)),t=t.replace(/!\[([^\]]*)\]\(([^)]+)\)/g,`<img src="$2" alt="$1"/>`),t=t.replace(/\[([^\]]+)\]\(([^)]+)\)/g,`<a href="$2" target="_blank" rel="noopener">$1</a>`),t=t.replace(/(?<!=["'])(https?:\/\/[^\s<>")\]]+)/g,`<a href="$1" target="_blank" rel="noopener">$1</a>`),t=t.replace(/\*\*\*(.+?)\*\*\*/g,`<strong><em>$1</em></strong>`),t=t.replace(/\*\*(.+?)\*\*/g,`<strong>$1</strong>`),t=t.replace(/(?<!\*)\*([^*]+)\*(?!\*)/g,`<em>$1</em>`),t=t.replace(/~~(.+?)~~/g,`<del>$1</del>`),t=t.replace(RegExp(`^${D9}#### (.+)$`,`gm`),`<h5>$1</h5>`),t=t.replace(RegExp(`^${D9}### (.+)$`,`gm`),`<h4>$1</h4>`),t=t.replace(RegExp(`^${D9}## (.+)$`,`gm`),`<h3>$1</h3>`),t=t.replace(RegExp(`^${D9}# (.+)$`,`gm`),`<h2>$1</h2>`),t=t.replace(RegExp(`^${D9}> (.+)$`,`gm`),`<blockquote>$1</blockquote>`),t=t.replace(/<\/blockquote>\n<blockquote>/g,`
|
|
148
148
|
`),t=t.replace(RegExp(`^${D9}(?:-{3,}|\\*{3,}|_{3,})\\s*$`,`gm`),`<hr/>`),t=t.replace(RegExp(`((?:^${D9}\\|.+\\|\\n?)+)`,`gm`),e=>{let t=e.trim().split(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usex/mikrotik-mcp",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.22.0",
|
|
4
4
|
"description": "MCP server for MikroTik RouterOS — 780+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -59,6 +59,7 @@
|
|
|
59
59
|
"gen": "bun run --bun gen:schemas && bun run --bun gen:docs",
|
|
60
60
|
"start": "bun run --bun src/cli.ts serve",
|
|
61
61
|
"discover": "bun run --bun scripts/discover-macs.ts",
|
|
62
|
+
"flags:download": "bun run --bun scripts/download-flags.ts",
|
|
62
63
|
"chat": "bunx mcp-chat --server \"./dist/cli.js serve\"",
|
|
63
64
|
"inspect": "bunx @modelcontextprotocol/inspector bun run --bun src/cli.ts serve",
|
|
64
65
|
"inspect:built": "bun run --bun build && bunx @modelcontextprotocol/inspector bun dist/cli.js serve",
|