@usex/mikrotik-mcp 4.4.0 → 4.5.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 +109 -9
- package/dist/index.js +1 -1
- package/dist/shared/{cli-k18dkv4w.js → cli-6xcbh1kn.js} +1 -1
- package/dist/shared/{cli-mm90zmj3.js → cli-hfhmh1qe.js} +141 -16
- package/dist/shared/{library-t0cfgfyj.js → library-399v2azd.js} +7 -3
- package/dist/shared/{library-tmmvpwjf.js → library-8hq0qqwm.js} +1 -1
- package/dist/ui/observability.html +45 -45
- package/package.json +1 -1
- package/schemas/config.schema.json +10 -2
- package/schemas/tool-catalog.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -106,7 +106,7 @@ import {
|
|
|
106
106
|
updateAaaEntity,
|
|
107
107
|
updateSummaryLine,
|
|
108
108
|
writeBackup
|
|
109
|
-
} from "./shared/cli-
|
|
109
|
+
} from "./shared/cli-hfhmh1qe.js";
|
|
110
110
|
|
|
111
111
|
// src/cli.ts
|
|
112
112
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -650,6 +650,52 @@ function stopHealthChecks() {
|
|
|
650
650
|
}
|
|
651
651
|
}
|
|
652
652
|
|
|
653
|
+
// src/observability/traffic-hub.ts
|
|
654
|
+
var POLL_MS = 1000;
|
|
655
|
+
var hubs = new Map;
|
|
656
|
+
function subscribeTraffic(device, fn) {
|
|
657
|
+
let hub = hubs.get(device);
|
|
658
|
+
if (!hub) {
|
|
659
|
+
const broadcast = (sample) => {
|
|
660
|
+
const h = hubs.get(device);
|
|
661
|
+
if (!h)
|
|
662
|
+
return;
|
|
663
|
+
h.last = sample;
|
|
664
|
+
for (const l of h.listeners)
|
|
665
|
+
l(sample);
|
|
666
|
+
};
|
|
667
|
+
const poll = async () => {
|
|
668
|
+
try {
|
|
669
|
+
broadcast(await sampleAllTraffic(createContext(undefined, device || undefined)));
|
|
670
|
+
} catch (e) {
|
|
671
|
+
broadcast({
|
|
672
|
+
ts: Date.now(),
|
|
673
|
+
source: "none",
|
|
674
|
+
note: e instanceof Error ? e.message : String(e),
|
|
675
|
+
hosts: {},
|
|
676
|
+
limits: {}
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
hub = { listeners: new Set, timer: setInterval(() => void poll(), POLL_MS) };
|
|
681
|
+
hubs.set(device, hub);
|
|
682
|
+
poll();
|
|
683
|
+
}
|
|
684
|
+
hub.listeners.add(fn);
|
|
685
|
+
if (hub.last)
|
|
686
|
+
fn(hub.last);
|
|
687
|
+
return () => {
|
|
688
|
+
const h = hubs.get(device);
|
|
689
|
+
if (!h)
|
|
690
|
+
return;
|
|
691
|
+
h.listeners.delete(fn);
|
|
692
|
+
if (h.listeners.size === 0) {
|
|
693
|
+
clearInterval(h.timer);
|
|
694
|
+
hubs.delete(device);
|
|
695
|
+
}
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
|
|
653
699
|
// src/observability/store.ts
|
|
654
700
|
import { mkdirSync as mkdirSync3 } from "fs";
|
|
655
701
|
import { dirname as dirname2 } from "path";
|
|
@@ -1595,6 +1641,45 @@ data: ${JSON.stringify(e)}
|
|
|
1595
1641
|
`));
|
|
1596
1642
|
ping = setInterval(() => send(`: ping
|
|
1597
1643
|
|
|
1644
|
+
`), 15000);
|
|
1645
|
+
},
|
|
1646
|
+
cancel() {
|
|
1647
|
+
unsub?.();
|
|
1648
|
+
if (ping)
|
|
1649
|
+
clearInterval(ping);
|
|
1650
|
+
}
|
|
1651
|
+
});
|
|
1652
|
+
return new Response(stream, {
|
|
1653
|
+
headers: {
|
|
1654
|
+
"content-type": "text/event-stream; charset=utf-8",
|
|
1655
|
+
"cache-control": "no-cache, no-transform",
|
|
1656
|
+
connection: "keep-alive"
|
|
1657
|
+
}
|
|
1658
|
+
});
|
|
1659
|
+
}
|
|
1660
|
+
function sseTrafficResponse(device, transportLabel) {
|
|
1661
|
+
let unsub;
|
|
1662
|
+
let ping;
|
|
1663
|
+
const stream = new ReadableStream({
|
|
1664
|
+
start(controller) {
|
|
1665
|
+
const enc = new TextEncoder;
|
|
1666
|
+
const send = (text) => {
|
|
1667
|
+
try {
|
|
1668
|
+
controller.enqueue(enc.encode(text));
|
|
1669
|
+
} catch {}
|
|
1670
|
+
};
|
|
1671
|
+
send(`event: hello
|
|
1672
|
+
data: ${JSON.stringify({ transport: transportLabel })}
|
|
1673
|
+
|
|
1674
|
+
`);
|
|
1675
|
+
unsub = subscribeTraffic(device, (sample) => {
|
|
1676
|
+
send(`event: traffic
|
|
1677
|
+
data: ${JSON.stringify(sample)}
|
|
1678
|
+
|
|
1679
|
+
`);
|
|
1680
|
+
});
|
|
1681
|
+
ping = setInterval(() => send(`: ping
|
|
1682
|
+
|
|
1598
1683
|
`), 15000);
|
|
1599
1684
|
},
|
|
1600
1685
|
cancel() {
|
|
@@ -1775,7 +1860,12 @@ async function modulesRoutes(req, url) {
|
|
|
1775
1860
|
if (p === "/api/modules" && req.method === "GET") {
|
|
1776
1861
|
const cfg = getConfig();
|
|
1777
1862
|
const src = getConfigSource();
|
|
1778
|
-
return json3({
|
|
1863
|
+
return json3({
|
|
1864
|
+
...moduleSurface(cfg.tools),
|
|
1865
|
+
filter: cfg.tools,
|
|
1866
|
+
source: src,
|
|
1867
|
+
appViews: cfg.mcp.appViews
|
|
1868
|
+
});
|
|
1779
1869
|
}
|
|
1780
1870
|
if (p === "/api/modules/toggle" && req.method === "POST") {
|
|
1781
1871
|
const b = await readJson(req);
|
|
@@ -2295,12 +2385,14 @@ async function runDashboard(cfg, transportLabel) {
|
|
|
2295
2385
|
};
|
|
2296
2386
|
async function dashboardRoute(req, url, srv, ca, tl) {
|
|
2297
2387
|
if (url.pathname === "/api/stream") {
|
|
2298
|
-
|
|
2388
|
+
const traffic = url.searchParams.has("traffic") ? url.searchParams.get("traffic") ?? "" : undefined;
|
|
2389
|
+
if (srv.upgrade(req, { data: { traffic } }))
|
|
2299
2390
|
return;
|
|
2300
2391
|
return new Response("WebSocket upgrade failed", { status: 400 });
|
|
2301
2392
|
}
|
|
2302
2393
|
if (url.pathname === "/api/sse") {
|
|
2303
|
-
|
|
2394
|
+
const traffic = url.searchParams.has("traffic") ? url.searchParams.get("traffic") ?? "" : undefined;
|
|
2395
|
+
return traffic === undefined ? sseResponse(tl) : sseTrafficResponse(traffic, tl);
|
|
2304
2396
|
}
|
|
2305
2397
|
const configResp = await configRoutes(req, url, ca);
|
|
2306
2398
|
if (configResp)
|
|
@@ -2471,11 +2563,19 @@ async function runDashboard(cfg, transportLabel) {
|
|
|
2471
2563
|
},
|
|
2472
2564
|
websocket: {
|
|
2473
2565
|
open(ws) {
|
|
2474
|
-
ws.data.
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2566
|
+
if (ws.data.traffic !== undefined) {
|
|
2567
|
+
ws.data.unsub = subscribeTraffic(ws.data.traffic, (sample) => {
|
|
2568
|
+
try {
|
|
2569
|
+
ws.send(JSON.stringify({ type: "traffic", sample }));
|
|
2570
|
+
} catch {}
|
|
2571
|
+
});
|
|
2572
|
+
} else {
|
|
2573
|
+
ws.data.unsub = subscribe((e) => {
|
|
2574
|
+
try {
|
|
2575
|
+
ws.send(JSON.stringify({ type: "event", event: e }));
|
|
2576
|
+
} catch {}
|
|
2577
|
+
});
|
|
2578
|
+
}
|
|
2479
2579
|
ws.send(JSON.stringify({ type: "hello", transport: transportLabel }));
|
|
2480
2580
|
},
|
|
2481
2581
|
message() {},
|
package/dist/index.js
CHANGED
|
@@ -29,7 +29,7 @@ import {
|
|
|
29
29
|
selectToolModules,
|
|
30
30
|
setConfig,
|
|
31
31
|
updateSummaryLine
|
|
32
|
-
} from "./shared/library-
|
|
32
|
+
} from "./shared/library-399v2azd.js";
|
|
33
33
|
// src/server.ts
|
|
34
34
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
35
35
|
import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
@@ -359,7 +359,8 @@ var DashboardConfigSchema = z.object({
|
|
|
359
359
|
captureBody: z.boolean().default(true),
|
|
360
360
|
redactInput: z.boolean().default(false),
|
|
361
361
|
maxBodyBytes: z.coerce.number().int().nonnegative().default(16384),
|
|
362
|
-
token: z.string().optional()
|
|
362
|
+
token: z.string().optional(),
|
|
363
|
+
feedLimit: z.coerce.number().int().positive().max(1e4).default(200)
|
|
363
364
|
});
|
|
364
365
|
var MemoryConfigSchema = z.object({
|
|
365
366
|
enabled: z.boolean().default(true),
|
|
@@ -572,7 +573,8 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
572
573
|
captureBody: boolOpt(pick("dashboard-capture-body", "MIKROTIK_DASHBOARD__CAPTURE_BODY")),
|
|
573
574
|
redactInput: boolOpt(pick("dashboard-redact-input", "MIKROTIK_DASHBOARD__REDACT_INPUT")),
|
|
574
575
|
maxBodyBytes: pick("dashboard-max-body-bytes", "MIKROTIK_DASHBOARD__MAX_BODY_BYTES"),
|
|
575
|
-
token: pick("dashboard-token", "MIKROTIK_DASHBOARD__TOKEN")
|
|
576
|
+
token: pick("dashboard-token", "MIKROTIK_DASHBOARD__TOKEN"),
|
|
577
|
+
feedLimit: pick("dashboard-feed-limit", "MIKROTIK_DASHBOARD__FEED_LIMIT")
|
|
576
578
|
});
|
|
577
579
|
const ssh = overlay(fileSsh, {
|
|
578
580
|
keepAlive: boolOpt(pick("ssh-keep-alive", "MIKROTIK_SSH__KEEP_ALIVE")),
|
|
@@ -2761,6 +2763,43 @@ var BLOCK_TAG = "mcp-blocked";
|
|
|
2761
2763
|
function normalizeMac(mac) {
|
|
2762
2764
|
return mac.trim().toUpperCase().replace(/-/g, ":");
|
|
2763
2765
|
}
|
|
2766
|
+
var resolvedSource = new Map;
|
|
2767
|
+
var firstLine = (s) => s.trim().split(`
|
|
2768
|
+
`)[0].trim();
|
|
2769
|
+
async function enableAccounting(ctx) {
|
|
2770
|
+
const out = await executeMikrotikCommand("/ip accounting set enabled=yes threshold=2000", ctx);
|
|
2771
|
+
return looksLikeError(out) ? firstLine(out) : null;
|
|
2772
|
+
}
|
|
2773
|
+
async function enableKidControl(ctx) {
|
|
2774
|
+
const count = await executeMikrotikCommand("/ip kid-control print count-only", ctx);
|
|
2775
|
+
if (looksLikeError(count))
|
|
2776
|
+
return firstLine(count);
|
|
2777
|
+
if ((parseLeadingNumber(count.trim()) ?? 0) > 0)
|
|
2778
|
+
return null;
|
|
2779
|
+
const days = ["mon", "tue", "wed", "thu", "fri", "sat", "sun"].map((d) => `${d}=0s-1d`).join(" ");
|
|
2780
|
+
const add = await executeMikrotikCommand(`/ip kid-control add name=mcp-monitor ${days}`, ctx);
|
|
2781
|
+
return looksLikeError(add) ? firstLine(add) : null;
|
|
2782
|
+
}
|
|
2783
|
+
async function resolveSource(ctx) {
|
|
2784
|
+
const key = ctx.device ?? "";
|
|
2785
|
+
const cached = resolvedSource.get(key);
|
|
2786
|
+
if (cached)
|
|
2787
|
+
return { source: cached };
|
|
2788
|
+
const accErr = await enableAccounting(ctx);
|
|
2789
|
+
if (!accErr) {
|
|
2790
|
+
resolvedSource.set(key, "accounting");
|
|
2791
|
+
return { source: "accounting" };
|
|
2792
|
+
}
|
|
2793
|
+
const kidErr = await enableKidControl(ctx);
|
|
2794
|
+
if (!kidErr) {
|
|
2795
|
+
resolvedSource.set(key, "kid-control");
|
|
2796
|
+
return { source: "kid-control" };
|
|
2797
|
+
}
|
|
2798
|
+
return {
|
|
2799
|
+
source: "none",
|
|
2800
|
+
note: `No per-host traffic source. /ip accounting (v6): ${accErr}. Kid Control (v7): ${kidErr}.`
|
|
2801
|
+
};
|
|
2802
|
+
}
|
|
2764
2803
|
async function fetchDevices(ctx) {
|
|
2765
2804
|
const leaseOut = await executeMikrotikCommand("/ip dhcp-server lease print detail", ctx);
|
|
2766
2805
|
const arpOut = await executeMikrotikCommand("/ip arp print detail", ctx);
|
|
@@ -2840,27 +2879,113 @@ async function sampleDeviceTraffic(ctx, ip) {
|
|
|
2840
2879
|
uploadLimit: limit(upLim)
|
|
2841
2880
|
};
|
|
2842
2881
|
}
|
|
2843
|
-
async function
|
|
2844
|
-
const
|
|
2845
|
-
const out = await executeMikrotikCommand("/queue simple print
|
|
2882
|
+
async function readQueueLimits(ctx) {
|
|
2883
|
+
const limits = {};
|
|
2884
|
+
const out = await executeMikrotikCommand("/queue simple print detail", ctx);
|
|
2846
2885
|
if (isEmpty(out) || looksLikeError(out))
|
|
2847
|
-
return
|
|
2848
|
-
const
|
|
2849
|
-
const limit = (v) => v && v !== "0" ? v : "";
|
|
2886
|
+
return limits;
|
|
2887
|
+
const val = (v) => v && v !== "0" ? v : "";
|
|
2850
2888
|
for (const row of parseRecords(out).rows) {
|
|
2851
2889
|
const ip = (row.target ?? "").split("/")[0]?.trim();
|
|
2852
2890
|
if (!ip)
|
|
2853
2891
|
continue;
|
|
2854
|
-
const [txB, rxB] = (row.bytes ?? "0/0").split("/");
|
|
2855
2892
|
const [upLim, downLim] = (row["max-limit"] ?? "0/0").split("/");
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2893
|
+
limits[ip] = { download: val(downLim), upload: val(upLim) };
|
|
2894
|
+
}
|
|
2895
|
+
return limits;
|
|
2896
|
+
}
|
|
2897
|
+
function parseMagnitude(v) {
|
|
2898
|
+
const m = (v ?? "").trim().match(/^(-?\d+(?:\.\d+)?)\s*([kMGT])?/i);
|
|
2899
|
+
if (!m)
|
|
2900
|
+
return 0;
|
|
2901
|
+
const u = (m[2] ?? "").toLowerCase();
|
|
2902
|
+
const factor = u === "k" ? 1000 : u === "m" ? 1e6 : u === "g" ? 1e9 : u === "t" ? 1000000000000 : 1;
|
|
2903
|
+
return Number(m[1]) * factor;
|
|
2904
|
+
}
|
|
2905
|
+
var acctState = new Map;
|
|
2906
|
+
function aggregateHostTraffic(rows) {
|
|
2907
|
+
const hosts = {};
|
|
2908
|
+
const bump = (ip, key, b) => {
|
|
2909
|
+
if (!ip)
|
|
2910
|
+
return;
|
|
2911
|
+
(hosts[ip] ??= { rxBytes: 0, txBytes: 0 })[key] += b;
|
|
2912
|
+
};
|
|
2913
|
+
for (const row of rows) {
|
|
2914
|
+
const bytes = parseLeadingNumber(row.bytes) ?? 0;
|
|
2915
|
+
if (bytes <= 0)
|
|
2916
|
+
continue;
|
|
2917
|
+
bump((row["dst-address"] ?? "").trim(), "rxBytes", bytes);
|
|
2918
|
+
bump((row["src-address"] ?? "").trim(), "txBytes", bytes);
|
|
2919
|
+
}
|
|
2920
|
+
return hosts;
|
|
2921
|
+
}
|
|
2922
|
+
async function sampleAccounting(ctx, ts) {
|
|
2923
|
+
const key = ctx.device ?? "";
|
|
2924
|
+
await executeMikrotikCommand("/ip accounting snapshot take", ctx);
|
|
2925
|
+
const out = await executeMikrotikCommand("/ip accounting snapshot print", ctx);
|
|
2926
|
+
if (looksLikeError(out))
|
|
2927
|
+
throw new Error(firstLine(out));
|
|
2928
|
+
const deltas = aggregateHostTraffic(parseRecords(out).rows);
|
|
2929
|
+
const st = acctState.get(key) ?? { lastTs: 0, cum: new Map };
|
|
2930
|
+
const dtSec = st.lastTs ? Math.max(0.1, (ts - st.lastTs) / 1000) : 0;
|
|
2931
|
+
const hosts = {};
|
|
2932
|
+
for (const [ip, d] of Object.entries(deltas)) {
|
|
2933
|
+
const c = st.cum.get(ip) ?? { rx: 0, tx: 0 };
|
|
2934
|
+
c.rx += d.rxBytes;
|
|
2935
|
+
c.tx += d.txBytes;
|
|
2936
|
+
st.cum.set(ip, c);
|
|
2937
|
+
hosts[ip] = {
|
|
2938
|
+
rxRate: dtSec ? d.rxBytes / dtSec * 8 : 0,
|
|
2939
|
+
txRate: dtSec ? d.txBytes / dtSec * 8 : 0,
|
|
2940
|
+
rxBytes: c.rx,
|
|
2941
|
+
txBytes: c.tx
|
|
2942
|
+
};
|
|
2943
|
+
}
|
|
2944
|
+
st.lastTs = ts;
|
|
2945
|
+
acctState.set(key, st);
|
|
2946
|
+
return hosts;
|
|
2947
|
+
}
|
|
2948
|
+
function parseKidDevices(rows) {
|
|
2949
|
+
const hosts = {};
|
|
2950
|
+
for (const row of rows) {
|
|
2951
|
+
const ip = (row["ip-address"] ?? "").trim();
|
|
2952
|
+
if (!ip)
|
|
2953
|
+
continue;
|
|
2954
|
+
hosts[ip] = {
|
|
2955
|
+
rxRate: parseMagnitude(row["rate-down"]),
|
|
2956
|
+
txRate: parseMagnitude(row["rate-up"]),
|
|
2957
|
+
rxBytes: parseMagnitude(row["bytes-down"]),
|
|
2958
|
+
txBytes: parseMagnitude(row["bytes-up"])
|
|
2959
|
+
};
|
|
2960
|
+
}
|
|
2961
|
+
return hosts;
|
|
2962
|
+
}
|
|
2963
|
+
async function sampleKidControl(ctx) {
|
|
2964
|
+
const out = await executeMikrotikCommand("/ip kid-control device print detail", ctx);
|
|
2965
|
+
if (looksLikeError(out))
|
|
2966
|
+
throw new Error(firstLine(out));
|
|
2967
|
+
if (isEmpty(out))
|
|
2968
|
+
return {};
|
|
2969
|
+
return parseKidDevices(parseRecords(out).rows);
|
|
2970
|
+
}
|
|
2971
|
+
async function sampleAllTraffic(ctx) {
|
|
2972
|
+
const ts = Date.now();
|
|
2973
|
+
const limits = await readQueueLimits(ctx);
|
|
2974
|
+
const { source, note } = await resolveSource(ctx);
|
|
2975
|
+
if (source === "none")
|
|
2976
|
+
return { ts, source, note, hosts: {}, limits };
|
|
2977
|
+
try {
|
|
2978
|
+
const hosts = source === "accounting" ? await sampleAccounting(ctx, ts) : await sampleKidControl(ctx);
|
|
2979
|
+
return { ts, source, hosts, limits };
|
|
2980
|
+
} catch (e) {
|
|
2981
|
+
return {
|
|
2982
|
+
ts,
|
|
2983
|
+
source: "none",
|
|
2984
|
+
note: e instanceof Error ? e.message : String(e),
|
|
2985
|
+
hosts: {},
|
|
2986
|
+
limits
|
|
2861
2987
|
};
|
|
2862
2988
|
}
|
|
2863
|
-
return { ts, queues };
|
|
2864
2989
|
}
|
|
2865
2990
|
async function setDeviceLimits(ctx, ip, opts) {
|
|
2866
2991
|
const rate = (v) => {
|
|
@@ -8364,7 +8489,7 @@ var cache = null;
|
|
|
8364
8489
|
async function gateway() {
|
|
8365
8490
|
if (cache)
|
|
8366
8491
|
return cache;
|
|
8367
|
-
const { moduleCatalog } = await import("./cli-
|
|
8492
|
+
const { moduleCatalog } = await import("./cli-6xcbh1kn.js");
|
|
8368
8493
|
const forIndex = [];
|
|
8369
8494
|
const byName = new Map;
|
|
8370
8495
|
for (const mod of moduleCatalog) {
|
|
@@ -359,7 +359,8 @@ var DashboardConfigSchema = z.object({
|
|
|
359
359
|
captureBody: z.boolean().default(true),
|
|
360
360
|
redactInput: z.boolean().default(false),
|
|
361
361
|
maxBodyBytes: z.coerce.number().int().nonnegative().default(16384),
|
|
362
|
-
token: z.string().optional()
|
|
362
|
+
token: z.string().optional(),
|
|
363
|
+
feedLimit: z.coerce.number().int().positive().max(1e4).default(200)
|
|
363
364
|
});
|
|
364
365
|
var MemoryConfigSchema = z.object({
|
|
365
366
|
enabled: z.boolean().default(true),
|
|
@@ -569,7 +570,8 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
569
570
|
captureBody: boolOpt(pick("dashboard-capture-body", "MIKROTIK_DASHBOARD__CAPTURE_BODY")),
|
|
570
571
|
redactInput: boolOpt(pick("dashboard-redact-input", "MIKROTIK_DASHBOARD__REDACT_INPUT")),
|
|
571
572
|
maxBodyBytes: pick("dashboard-max-body-bytes", "MIKROTIK_DASHBOARD__MAX_BODY_BYTES"),
|
|
572
|
-
token: pick("dashboard-token", "MIKROTIK_DASHBOARD__TOKEN")
|
|
573
|
+
token: pick("dashboard-token", "MIKROTIK_DASHBOARD__TOKEN"),
|
|
574
|
+
feedLimit: pick("dashboard-feed-limit", "MIKROTIK_DASHBOARD__FEED_LIMIT")
|
|
573
575
|
});
|
|
574
576
|
const ssh = overlay(fileSsh, {
|
|
575
577
|
keepAlive: boolOpt(pick("ssh-keep-alive", "MIKROTIK_SSH__KEEP_ALIVE")),
|
|
@@ -2713,6 +2715,7 @@ var BLOCK_TAG = "mcp-blocked";
|
|
|
2713
2715
|
function normalizeMac(mac) {
|
|
2714
2716
|
return mac.trim().toUpperCase().replace(/-/g, ":");
|
|
2715
2717
|
}
|
|
2718
|
+
var resolvedSource = new Map;
|
|
2716
2719
|
async function fetchDevices(ctx) {
|
|
2717
2720
|
const leaseOut = await executeMikrotikCommand("/ip dhcp-server lease print detail", ctx);
|
|
2718
2721
|
const arpOut = await executeMikrotikCommand("/ip arp print detail", ctx);
|
|
@@ -2792,6 +2795,7 @@ async function sampleDeviceTraffic(ctx, ip) {
|
|
|
2792
2795
|
uploadLimit: limit(upLim)
|
|
2793
2796
|
};
|
|
2794
2797
|
}
|
|
2798
|
+
var acctState = new Map;
|
|
2795
2799
|
async function setDeviceLimits(ctx, ip, opts) {
|
|
2796
2800
|
const rate = (v) => {
|
|
2797
2801
|
const t = (v ?? "").trim();
|
|
@@ -8294,7 +8298,7 @@ var cache = null;
|
|
|
8294
8298
|
async function gateway() {
|
|
8295
8299
|
if (cache)
|
|
8296
8300
|
return cache;
|
|
8297
|
-
const { moduleCatalog } = await import("./library-
|
|
8301
|
+
const { moduleCatalog } = await import("./library-8hq0qqwm.js");
|
|
8298
8302
|
const forIndex = [];
|
|
8299
8303
|
const byName = new Map;
|
|
8300
8304
|
for (const mod of moduleCatalog) {
|