@usex/mikrotik-mcp 3.59.0 → 4.0.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 +7 -2
- package/dist/index.d.ts +12 -2
- package/dist/index.js +1 -1
- package/dist/shared/{cli-jq168zrj.js → cli-athwegwy.js} +230 -126
- package/dist/shared/{cli-2dyvnv9v.js → cli-cjrk8g04.js} +1 -1
- package/dist/shared/{library-0zd2tej9.js → library-4fpy49qb.js} +1 -1
- package/dist/shared/{library-21t1cncr.js → library-f5kwsqv2.js} +230 -126
- package/dist/ui/aaa.html +1 -1
- package/dist/ui/connected-devices.html +1 -1
- package/dist/ui/dashboard.html +1 -1
- package/dist/ui/firewall-audit.html +1 -1
- package/dist/ui/firewall.html +1 -1
- package/dist/ui/interfaces.html +1 -1
- package/dist/ui/observability.html +47 -47
- package/dist/ui/records.html +1 -1
- package/package.json +19 -16
|
@@ -436,7 +436,22 @@ function parseDevicesSource(raw, fromFile) {
|
|
|
436
436
|
const mcp = structured && obj.mcp && typeof obj.mcp === "object" ? obj.mcp : undefined;
|
|
437
437
|
const ssh = structured && obj.ssh && typeof obj.ssh === "object" ? obj.ssh : undefined;
|
|
438
438
|
const memory = structured && obj.memory && typeof obj.memory === "object" ? obj.memory : undefined;
|
|
439
|
-
|
|
439
|
+
const readOnly = structured && typeof obj.readOnly === "boolean" ? obj.readOnly : undefined;
|
|
440
|
+
const disableUpdateCheck = structured && typeof obj.disableUpdateCheck === "boolean" ? obj.disableUpdateCheck : undefined;
|
|
441
|
+
const backupDir = structured && typeof obj.backupDir === "string" ? obj.backupDir : undefined;
|
|
442
|
+
return {
|
|
443
|
+
devices,
|
|
444
|
+
defaultDevice,
|
|
445
|
+
s3,
|
|
446
|
+
dashboard,
|
|
447
|
+
tools,
|
|
448
|
+
mcp,
|
|
449
|
+
ssh,
|
|
450
|
+
memory,
|
|
451
|
+
readOnly,
|
|
452
|
+
disableUpdateCheck,
|
|
453
|
+
backupDir
|
|
454
|
+
};
|
|
440
455
|
}
|
|
441
456
|
var configSource = { path: DEFAULT_CONFIG_FILE, fromFile: false };
|
|
442
457
|
function getConfigSource() {
|
|
@@ -445,6 +460,13 @@ function getConfigSource() {
|
|
|
445
460
|
function loadConfig(argv = process.argv.slice(2)) {
|
|
446
461
|
const flags = parseFlags(argv);
|
|
447
462
|
const pick = (flag, ...envNames) => flags[flag] ?? env(...envNames);
|
|
463
|
+
const overlay = (file, over) => {
|
|
464
|
+
const out = { ...file };
|
|
465
|
+
for (const [k, v] of Object.entries(over))
|
|
466
|
+
if (v !== undefined)
|
|
467
|
+
out[k] = v;
|
|
468
|
+
return out;
|
|
469
|
+
};
|
|
448
470
|
const jumpHostName = pick("jump-host", "MIKROTIK_JUMP_HOST");
|
|
449
471
|
const jumpHost = jumpHostName ? {
|
|
450
472
|
host: jumpHostName,
|
|
@@ -485,6 +507,9 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
485
507
|
let fileMcp;
|
|
486
508
|
let fileSsh;
|
|
487
509
|
let fileMemory;
|
|
510
|
+
let fileReadOnly;
|
|
511
|
+
let fileDisableUpdateCheck;
|
|
512
|
+
let fileBackupDir;
|
|
488
513
|
if (configFile || devicesInline) {
|
|
489
514
|
const src = configFile ? parseDevicesSource(configFile, true) : parseDevicesSource(devicesInline, false);
|
|
490
515
|
for (const [name, dc] of Object.entries(src.devices))
|
|
@@ -499,8 +524,11 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
499
524
|
fileMcp = src.mcp;
|
|
500
525
|
fileSsh = src.ssh;
|
|
501
526
|
fileMemory = src.memory;
|
|
527
|
+
fileReadOnly = src.readOnly;
|
|
528
|
+
fileDisableUpdateCheck = src.disableUpdateCheck;
|
|
529
|
+
fileBackupDir = src.backupDir;
|
|
502
530
|
}
|
|
503
|
-
const s3 = {
|
|
531
|
+
const s3 = overlay(fileS3, {
|
|
504
532
|
accessKeyId: pick("s3-access-key-id", "S3_ACCESS_KEY_ID", "AWS_ACCESS_KEY_ID"),
|
|
505
533
|
secretAccessKey: pick("s3-secret-access-key", "S3_SECRET_ACCESS_KEY", "AWS_SECRET_ACCESS_KEY"),
|
|
506
534
|
sessionToken: pick("s3-session-token", "S3_SESSION_TOKEN", "AWS_SESSION_TOKEN"),
|
|
@@ -508,12 +536,11 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
508
536
|
endpoint: pick("s3-endpoint", "S3_ENDPOINT", "AWS_ENDPOINT"),
|
|
509
537
|
bucket: pick("s3-bucket", "S3_BUCKET", "AWS_BUCKET"),
|
|
510
538
|
prefix: pick("s3-prefix", "MIKROTIK_S3_PREFIX"),
|
|
511
|
-
presignExpiresIn: pick("s3-presign-expires-in", "MIKROTIK_S3_PRESIGN_EXPIRES_IN")
|
|
512
|
-
|
|
513
|
-
};
|
|
539
|
+
presignExpiresIn: pick("s3-presign-expires-in", "MIKROTIK_S3_PRESIGN_EXPIRES_IN")
|
|
540
|
+
});
|
|
514
541
|
const appViewsRaw = pick("app-views", "MIKROTIK_MCP__APP_VIEWS");
|
|
515
542
|
const appViewsEnv = appViewsRaw === undefined ? undefined : !/^(0|false|no|off)$/i.test(appViewsRaw);
|
|
516
|
-
const mcp = {
|
|
543
|
+
const mcp = overlay(fileMcp, {
|
|
517
544
|
transport: pick("transport", "MIKROTIK_MCP__TRANSPORT", "MCP_TRANSPORT"),
|
|
518
545
|
host: pick("mcp-host", "MIKROTIK_MCP__HOST"),
|
|
519
546
|
port: pick("mcp-port", "MIKROTIK_MCP__PORT"),
|
|
@@ -521,22 +548,22 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
521
548
|
allowedOrigins: pick("mcp-allowed-origins", "MIKROTIK_MCP__ALLOWED_ORIGINS"),
|
|
522
549
|
corsOrigins: pick("mcp-cors-origins", "MIKROTIK_MCP__CORS_ORIGINS"),
|
|
523
550
|
toolPageSize: pick("tool-page-size", "MIKROTIK_MCP__TOOL_PAGE_SIZE"),
|
|
524
|
-
appViews: appViewsEnv
|
|
525
|
-
|
|
526
|
-
};
|
|
551
|
+
appViews: appViewsEnv
|
|
552
|
+
});
|
|
527
553
|
const isTruthy = (v) => /^(1|true|yes|on)$/i.test(v ?? "");
|
|
528
|
-
const
|
|
529
|
-
const
|
|
554
|
+
const boolFileFlag = (flagVal, fileVal) => flagVal !== undefined ? isTruthy(flagVal) : fileVal;
|
|
555
|
+
const readOnly = boolFileFlag(pick("read-only", "MIKROTIK_READ_ONLY"), fileReadOnly);
|
|
556
|
+
const disableUpdateCheck = boolFileFlag(pick("disable-update-check", "MIKROTIK_DISABLE_UPDATE_CHECK"), fileDisableUpdateCheck);
|
|
557
|
+
const backupDir = pick("backup-dir", "MIKROTIK_BACKUP_DIR") ?? fileBackupDir;
|
|
530
558
|
const csv = (v) => v === undefined ? undefined : v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
531
|
-
const tools = {
|
|
559
|
+
const tools = overlay(fileTools, {
|
|
532
560
|
enabledModules: csv(pick("tools-enabled-modules", "MIKROTIK_TOOLS__ENABLED_MODULES")),
|
|
533
561
|
disabledModules: csv(pick("tools-disabled-modules", "MIKROTIK_TOOLS__DISABLED_MODULES")),
|
|
534
562
|
enabledGroups: csv(pick("tools-enabled-groups", "MIKROTIK_TOOLS__ENABLED_GROUPS")),
|
|
535
|
-
disabledGroups: csv(pick("tools-disabled-groups", "MIKROTIK_TOOLS__DISABLED_GROUPS"))
|
|
536
|
-
|
|
537
|
-
};
|
|
563
|
+
disabledGroups: csv(pick("tools-disabled-groups", "MIKROTIK_TOOLS__DISABLED_GROUPS"))
|
|
564
|
+
});
|
|
538
565
|
const boolOpt = (v) => v === undefined ? undefined : isTruthy(v);
|
|
539
|
-
const dashboard = {
|
|
566
|
+
const dashboard = overlay(fileDashboard, {
|
|
540
567
|
enabled: boolOpt(pick("dashboard", "MIKROTIK_DASHBOARD__ENABLED", "MIKROTIK_DASHBOARD")),
|
|
541
568
|
host: pick("dashboard-host", "MIKROTIK_DASHBOARD__HOST"),
|
|
542
569
|
port: pick("dashboard-port", "MIKROTIK_DASHBOARD__PORT"),
|
|
@@ -545,20 +572,17 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
545
572
|
captureBody: boolOpt(pick("dashboard-capture-body", "MIKROTIK_DASHBOARD__CAPTURE_BODY")),
|
|
546
573
|
redactInput: boolOpt(pick("dashboard-redact-input", "MIKROTIK_DASHBOARD__REDACT_INPUT")),
|
|
547
574
|
maxBodyBytes: pick("dashboard-max-body-bytes", "MIKROTIK_DASHBOARD__MAX_BODY_BYTES"),
|
|
548
|
-
token: pick("dashboard-token", "MIKROTIK_DASHBOARD__TOKEN")
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
const ssh = {
|
|
575
|
+
token: pick("dashboard-token", "MIKROTIK_DASHBOARD__TOKEN")
|
|
576
|
+
});
|
|
577
|
+
const ssh = overlay(fileSsh, {
|
|
552
578
|
keepAlive: boolOpt(pick("ssh-keep-alive", "MIKROTIK_SSH__KEEP_ALIVE")),
|
|
553
579
|
keepAliveInterval: pick("ssh-keepalive-interval", "MIKROTIK_SSH__KEEPALIVE_INTERVAL"),
|
|
554
|
-
idleTimeout: pick("ssh-idle-timeout", "MIKROTIK_SSH__IDLE_TIMEOUT")
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
const memory = {
|
|
580
|
+
idleTimeout: pick("ssh-idle-timeout", "MIKROTIK_SSH__IDLE_TIMEOUT")
|
|
581
|
+
});
|
|
582
|
+
const memory = overlay(fileMemory, {
|
|
558
583
|
enabled: boolOpt(pick("memory-enabled", "MIKROTIK_MEMORY__ENABLED")),
|
|
559
|
-
dbPath: pick("memory-db", "MIKROTIK_MEMORY__DB_PATH")
|
|
560
|
-
|
|
561
|
-
};
|
|
584
|
+
dbPath: pick("memory-db", "MIKROTIK_MEMORY__DB_PATH")
|
|
585
|
+
});
|
|
562
586
|
const hasS3 = !!(s3.accessKeyId || s3.bucket || s3.endpoint);
|
|
563
587
|
const raw = {
|
|
564
588
|
devices: Object.keys(devices).length ? devices : { default: {} },
|
|
@@ -567,6 +591,7 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
567
591
|
dashboard,
|
|
568
592
|
readOnly,
|
|
569
593
|
disableUpdateCheck,
|
|
594
|
+
backupDir,
|
|
570
595
|
tools,
|
|
571
596
|
ssh,
|
|
572
597
|
memory,
|
|
@@ -1208,6 +1233,8 @@ function stripAnsi(text) {
|
|
|
1208
1233
|
return text.replace(ANSI_RE, "");
|
|
1209
1234
|
}
|
|
1210
1235
|
var CTRL_X = "\x18";
|
|
1236
|
+
var IDLE_TIMEOUT_MS = 15000;
|
|
1237
|
+
var HARD_CAP_MS = 120000;
|
|
1211
1238
|
function isSafeModeActivated(response) {
|
|
1212
1239
|
if (response.includes("<SAFE>"))
|
|
1213
1240
|
return true;
|
|
@@ -1283,7 +1310,7 @@ class SafeModeManager {
|
|
|
1283
1310
|
`);
|
|
1284
1311
|
const { text, timedOut } = await this.readUntilPrompt();
|
|
1285
1312
|
if (timedOut) {
|
|
1286
|
-
throw new Error(`Safe Mode shell
|
|
1313
|
+
throw new Error(`Safe Mode shell went silent for ${IDLE_TIMEOUT_MS / 1000}s (command: ${command}). The ` + "interactive session appears wedged \u2014 some RouterOS builds/terminals don't support Safe " + "Mode over SSH. Apply the change with the direct write tools instead (verify each with a read).");
|
|
1287
1314
|
}
|
|
1288
1315
|
return this.extractOutput(text, command);
|
|
1289
1316
|
});
|
|
@@ -1337,25 +1364,32 @@ class SafeModeManager {
|
|
|
1337
1364
|
status() {
|
|
1338
1365
|
return this.active ? "Safe mode is ACTIVE. Changes are pending \u2014 they are NOT yet persisted. " + "Call commit_safe_mode to persist or rollback_safe_mode to revert." : "Safe mode is NOT active. Changes take effect and persist immediately.";
|
|
1339
1366
|
}
|
|
1340
|
-
readUntilPrompt(
|
|
1367
|
+
readUntilPrompt(idleMs = IDLE_TIMEOUT_MS, isDone = (c) => PROMPT_RE.test(c), hardCapMs = HARD_CAP_MS) {
|
|
1341
1368
|
const channel = this.channel;
|
|
1342
1369
|
if (!channel)
|
|
1343
1370
|
return Promise.resolve({ text: "", timedOut: false });
|
|
1344
1371
|
return new Promise((resolve2) => {
|
|
1345
1372
|
let buf = "";
|
|
1346
|
-
let
|
|
1373
|
+
let idleTimer;
|
|
1374
|
+
const hardTimer = setTimeout(() => finish(stripAnsi(buf), true), hardCapMs);
|
|
1375
|
+
function armIdle2() {
|
|
1376
|
+
clearTimeout(idleTimer);
|
|
1377
|
+
idleTimer = setTimeout(() => finish(stripAnsi(buf), true), idleMs);
|
|
1378
|
+
}
|
|
1347
1379
|
function onData(chunk) {
|
|
1348
1380
|
buf += decodeOutput(chunk);
|
|
1381
|
+
armIdle2();
|
|
1349
1382
|
const cleaned = stripAnsi(buf);
|
|
1350
1383
|
if (isDone(cleaned))
|
|
1351
1384
|
finish(cleaned, false);
|
|
1352
1385
|
}
|
|
1353
1386
|
function finish(result, timedOut) {
|
|
1354
|
-
clearTimeout(
|
|
1387
|
+
clearTimeout(idleTimer);
|
|
1388
|
+
clearTimeout(hardTimer);
|
|
1355
1389
|
channel.removeListener("data", onData);
|
|
1356
1390
|
resolve2({ text: result, timedOut });
|
|
1357
1391
|
}
|
|
1358
|
-
|
|
1392
|
+
armIdle2();
|
|
1359
1393
|
channel.on("data", onData);
|
|
1360
1394
|
});
|
|
1361
1395
|
}
|
|
@@ -5264,7 +5298,7 @@ var changePlanTools = [
|
|
|
5264
5298
|
const enabled2 = await safe.enable();
|
|
5265
5299
|
if (enabled2.startsWith("Error"))
|
|
5266
5300
|
throw new Error(enabled2);
|
|
5267
|
-
const APPLY_BUDGET_MS =
|
|
5301
|
+
const APPLY_BUDGET_MS = 180000;
|
|
5268
5302
|
const startedAt = Date.now();
|
|
5269
5303
|
const overBudget = () => Date.now() - startedAt > APPLY_BUDGET_MS;
|
|
5270
5304
|
const applyAndDiff = async () => {
|
|
@@ -8208,7 +8242,7 @@ var rawCommandTools = [
|
|
|
8208
8242
|
if (!cmd)
|
|
8209
8243
|
return "Provide a RouterOS command to run.";
|
|
8210
8244
|
ctx.info(`Running raw RouterOS command: ${cmd}`);
|
|
8211
|
-
const result = await executeMikrotikCommand(cmd, ctx);
|
|
8245
|
+
const result = await executeMikrotikCommand(cmd, ctx, { maxMs: 30000 });
|
|
8212
8246
|
if (looksLikeError(result))
|
|
8213
8247
|
return `RouterOS command error: ${result}`;
|
|
8214
8248
|
return result.trim() ? result : "(command completed with no output)";
|
|
@@ -8330,7 +8364,7 @@ var cache = null;
|
|
|
8330
8364
|
async function gateway() {
|
|
8331
8365
|
if (cache)
|
|
8332
8366
|
return cache;
|
|
8333
|
-
const { moduleCatalog } = await import("./cli-
|
|
8367
|
+
const { moduleCatalog } = await import("./cli-cjrk8g04.js");
|
|
8334
8368
|
const forIndex = [];
|
|
8335
8369
|
const byName = new Map;
|
|
8336
8370
|
for (const mod of moduleCatalog) {
|
|
@@ -10527,7 +10561,30 @@ ${results.join(`
|
|
|
10527
10561
|
|
|
10528
10562
|
// src/tools/firewall-nat.ts
|
|
10529
10563
|
import { z as z29 } from "zod";
|
|
10564
|
+
|
|
10565
|
+
// src/tools/_resolve-rule-id.ts
|
|
10566
|
+
function ruleResolver(scope) {
|
|
10567
|
+
return async function resolveRuleId(ruleId, ctx) {
|
|
10568
|
+
if (/^\d+$/.test(ruleId)) {
|
|
10569
|
+
const id = `*${ruleId}`;
|
|
10570
|
+
const byId = await executeMikrotikCommand(`${scope} print count-only where .id=${id}`, ctx);
|
|
10571
|
+
if (byId.trim() !== "0")
|
|
10572
|
+
return id;
|
|
10573
|
+
const idsRaw = await executeMikrotikCommand(`:foreach i in=[${scope} find] do={:put $i}`, ctx);
|
|
10574
|
+
if (isEmpty(idsRaw))
|
|
10575
|
+
return null;
|
|
10576
|
+
const ids = idsRaw.trim().split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
10577
|
+
const pos = Number.parseInt(ruleId, 10);
|
|
10578
|
+
return pos >= 0 && pos < ids.length ? ids[pos] : null;
|
|
10579
|
+
}
|
|
10580
|
+
const count = await executeMikrotikCommand(`${scope} print count-only where .id=${ruleId}`, ctx);
|
|
10581
|
+
return count.trim() !== "0" ? ruleId : null;
|
|
10582
|
+
};
|
|
10583
|
+
}
|
|
10584
|
+
|
|
10585
|
+
// src/tools/firewall-nat.ts
|
|
10530
10586
|
var isDigits2 = (s) => /^\d+$/.test(s);
|
|
10587
|
+
var resolveNatRuleId = ruleResolver("/ip firewall nat");
|
|
10531
10588
|
async function updateNatRule(a, ctx) {
|
|
10532
10589
|
ctx.info(`Updating NAT rule: rule_id=${a.rule_id}`);
|
|
10533
10590
|
const updates = [];
|
|
@@ -10603,11 +10660,14 @@ async function updateNatRule(a, ctx) {
|
|
|
10603
10660
|
}
|
|
10604
10661
|
if (updates.length === 0)
|
|
10605
10662
|
return "No updates specified.";
|
|
10606
|
-
const
|
|
10663
|
+
const id = await resolveNatRuleId(a.rule_id, ctx);
|
|
10664
|
+
if (!id)
|
|
10665
|
+
return `NAT rule '${a.rule_id}' not found.`;
|
|
10666
|
+
const cmd = `/ip firewall nat set ${id} ${updates.join(" ")}`;
|
|
10607
10667
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
10608
10668
|
if (looksLikeError(result))
|
|
10609
10669
|
return `Failed to update NAT rule: ${result}`;
|
|
10610
|
-
const details = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${
|
|
10670
|
+
const details = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${id}`, ctx);
|
|
10611
10671
|
return `NAT rule updated successfully:
|
|
10612
10672
|
|
|
10613
10673
|
${details}`;
|
|
@@ -10785,8 +10845,11 @@ ${result}`;
|
|
|
10785
10845
|
},
|
|
10786
10846
|
async handler(a, ctx) {
|
|
10787
10847
|
ctx.info(`Getting NAT rule details: rule_id=${a.rule_id}`);
|
|
10788
|
-
const
|
|
10789
|
-
|
|
10848
|
+
const id = await resolveNatRuleId(a.rule_id, ctx);
|
|
10849
|
+
if (!id)
|
|
10850
|
+
return `NAT rule '${a.rule_id}' not found.`;
|
|
10851
|
+
const result = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${id}`, ctx);
|
|
10852
|
+
return isEmpty(result) ? `NAT rule '${a.rule_id}' not found.` : `NAT RULE DETAILS:
|
|
10790
10853
|
|
|
10791
10854
|
${result}`;
|
|
10792
10855
|
}
|
|
@@ -10868,13 +10931,13 @@ ${result}`;
|
|
|
10868
10931
|
inputSchema: { rule_id: z29.string() },
|
|
10869
10932
|
async handler(a, ctx) {
|
|
10870
10933
|
ctx.info(`Removing NAT rule: rule_id=${a.rule_id}`);
|
|
10871
|
-
const
|
|
10872
|
-
if (
|
|
10873
|
-
return `NAT rule
|
|
10874
|
-
const result = await executeMikrotikCommand(`/ip firewall nat remove ${
|
|
10934
|
+
const id = await resolveNatRuleId(a.rule_id, ctx);
|
|
10935
|
+
if (!id)
|
|
10936
|
+
return `NAT rule '${a.rule_id}' not found.`;
|
|
10937
|
+
const result = await executeMikrotikCommand(`/ip firewall nat remove ${id}`, ctx);
|
|
10875
10938
|
if (looksLikeError(result))
|
|
10876
10939
|
return `Failed to remove NAT rule: ${result}`;
|
|
10877
|
-
return `NAT rule
|
|
10940
|
+
return `NAT rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
10878
10941
|
}
|
|
10879
10942
|
}),
|
|
10880
10943
|
defineTool({
|
|
@@ -10888,13 +10951,13 @@ ${result}`;
|
|
|
10888
10951
|
},
|
|
10889
10952
|
async handler(a, ctx) {
|
|
10890
10953
|
ctx.info(`Moving NAT rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
10891
|
-
const
|
|
10892
|
-
if (
|
|
10893
|
-
return `NAT rule
|
|
10894
|
-
const result = await executeMikrotikCommand(`/ip firewall nat move ${
|
|
10954
|
+
const id = await resolveNatRuleId(a.rule_id, ctx);
|
|
10955
|
+
if (!id)
|
|
10956
|
+
return `NAT rule '${a.rule_id}' not found.`;
|
|
10957
|
+
const result = await executeMikrotikCommand(`/ip firewall nat move ${id} destination=${a.destination}`, ctx);
|
|
10895
10958
|
if (looksLikeError(result))
|
|
10896
10959
|
return `Failed to move NAT rule: ${result}`;
|
|
10897
|
-
return `NAT rule
|
|
10960
|
+
return `NAT rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
10898
10961
|
}
|
|
10899
10962
|
}),
|
|
10900
10963
|
defineTool({
|
|
@@ -10922,6 +10985,7 @@ ${result}`;
|
|
|
10922
10985
|
// src/tools/firewall-mangle.ts
|
|
10923
10986
|
import { z as z30 } from "zod";
|
|
10924
10987
|
var isDigits3 = (s) => /^\d+$/.test(s);
|
|
10988
|
+
var resolveMangleRuleId = ruleResolver("/ip firewall mangle");
|
|
10925
10989
|
async function updateMangleRule(a, ctx) {
|
|
10926
10990
|
ctx.info(`Updating mangle rule: rule_id=${a.rule_id}`);
|
|
10927
10991
|
const updates = [];
|
|
@@ -11007,11 +11071,14 @@ async function updateMangleRule(a, ctx) {
|
|
|
11007
11071
|
}
|
|
11008
11072
|
if (updates.length === 0)
|
|
11009
11073
|
return "No updates specified.";
|
|
11010
|
-
const
|
|
11074
|
+
const id = await resolveMangleRuleId(a.rule_id, ctx);
|
|
11075
|
+
if (!id)
|
|
11076
|
+
return `Mangle rule '${a.rule_id}' not found.`;
|
|
11077
|
+
const cmd = `/ip firewall mangle set ${id} ${updates.join(" ")}`;
|
|
11011
11078
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
11012
11079
|
if (looksLikeError(result))
|
|
11013
11080
|
return `Failed to update mangle rule: ${result}`;
|
|
11014
|
-
const details = await executeMikrotikCommand(`/ip firewall mangle print detail where .id=${
|
|
11081
|
+
const details = await executeMikrotikCommand(`/ip firewall mangle print detail where .id=${id}`, ctx);
|
|
11015
11082
|
return `Mangle rule updated successfully:
|
|
11016
11083
|
|
|
11017
11084
|
${details}`;
|
|
@@ -11186,8 +11253,11 @@ ${result}`;
|
|
|
11186
11253
|
},
|
|
11187
11254
|
async handler(a, ctx) {
|
|
11188
11255
|
ctx.info(`Getting mangle rule details: rule_id=${a.rule_id}`);
|
|
11189
|
-
const
|
|
11190
|
-
|
|
11256
|
+
const id = await resolveMangleRuleId(a.rule_id, ctx);
|
|
11257
|
+
if (!id)
|
|
11258
|
+
return `Mangle rule '${a.rule_id}' not found.`;
|
|
11259
|
+
const result = await executeMikrotikCommand(`/ip firewall mangle print detail where .id=${id}`, ctx);
|
|
11260
|
+
return isEmpty(result) ? `Mangle rule '${a.rule_id}' not found.` : `MANGLE RULE DETAILS:
|
|
11191
11261
|
|
|
11192
11262
|
${result}`;
|
|
11193
11263
|
}
|
|
@@ -11278,13 +11348,13 @@ ${result}`;
|
|
|
11278
11348
|
inputSchema: { rule_id: z30.string() },
|
|
11279
11349
|
async handler(a, ctx) {
|
|
11280
11350
|
ctx.info(`Removing mangle rule: rule_id=${a.rule_id}`);
|
|
11281
|
-
const
|
|
11282
|
-
if (
|
|
11283
|
-
return `Mangle rule
|
|
11284
|
-
const result = await executeMikrotikCommand(`/ip firewall mangle remove ${
|
|
11351
|
+
const id = await resolveMangleRuleId(a.rule_id, ctx);
|
|
11352
|
+
if (!id)
|
|
11353
|
+
return `Mangle rule '${a.rule_id}' not found.`;
|
|
11354
|
+
const result = await executeMikrotikCommand(`/ip firewall mangle remove ${id}`, ctx);
|
|
11285
11355
|
if (looksLikeError(result))
|
|
11286
11356
|
return `Failed to remove mangle rule: ${result}`;
|
|
11287
|
-
return `Mangle rule
|
|
11357
|
+
return `Mangle rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
11288
11358
|
}
|
|
11289
11359
|
}),
|
|
11290
11360
|
defineTool({
|
|
@@ -11298,13 +11368,13 @@ ${result}`;
|
|
|
11298
11368
|
},
|
|
11299
11369
|
async handler(a, ctx) {
|
|
11300
11370
|
ctx.info(`Moving mangle rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
11301
|
-
const
|
|
11302
|
-
if (
|
|
11303
|
-
return `Mangle rule
|
|
11304
|
-
const result = await executeMikrotikCommand(`/ip firewall mangle move ${
|
|
11371
|
+
const id = await resolveMangleRuleId(a.rule_id, ctx);
|
|
11372
|
+
if (!id)
|
|
11373
|
+
return `Mangle rule '${a.rule_id}' not found.`;
|
|
11374
|
+
const result = await executeMikrotikCommand(`/ip firewall mangle move ${id} destination=${a.destination}`, ctx);
|
|
11305
11375
|
if (looksLikeError(result))
|
|
11306
11376
|
return `Failed to move mangle rule: ${result}`;
|
|
11307
|
-
return `Mangle rule
|
|
11377
|
+
return `Mangle rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
11308
11378
|
}
|
|
11309
11379
|
}),
|
|
11310
11380
|
defineTool({
|
|
@@ -13440,6 +13510,7 @@ ${result}`;
|
|
|
13440
13510
|
// src/tools/ipv6-firewall-filter.ts
|
|
13441
13511
|
import { z as z44 } from "zod";
|
|
13442
13512
|
var isDigits4 = (s) => /^\d+$/.test(s);
|
|
13513
|
+
var resolveRuleId = ruleResolver("/ipv6 firewall filter");
|
|
13443
13514
|
async function updateFilterRule2(a, ctx) {
|
|
13444
13515
|
ctx.info(`Updating IPv6 firewall filter rule: rule_id=${a.rule_id}`);
|
|
13445
13516
|
const updates = [];
|
|
@@ -13508,11 +13579,14 @@ async function updateFilterRule2(a, ctx) {
|
|
|
13508
13579
|
}
|
|
13509
13580
|
if (updates.length === 0)
|
|
13510
13581
|
return "No updates specified.";
|
|
13511
|
-
const
|
|
13582
|
+
const id = await resolveRuleId(a.rule_id, ctx);
|
|
13583
|
+
if (!id)
|
|
13584
|
+
return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
|
|
13585
|
+
const cmd = `/ipv6 firewall filter set ${id} ${updates.join(" ")}`;
|
|
13512
13586
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
13513
13587
|
if (looksLikeError(result))
|
|
13514
13588
|
return `Failed to update IPv6 firewall filter rule: ${result}`;
|
|
13515
|
-
const details = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${
|
|
13589
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${id}`, ctx);
|
|
13516
13590
|
return `IPv6 firewall filter rule updated successfully:
|
|
13517
13591
|
|
|
13518
13592
|
${details}`;
|
|
@@ -13669,8 +13743,11 @@ ${result}`;
|
|
|
13669
13743
|
},
|
|
13670
13744
|
async handler(a, ctx) {
|
|
13671
13745
|
ctx.info(`Getting IPv6 firewall filter rule details: rule_id=${a.rule_id}`);
|
|
13672
|
-
const
|
|
13673
|
-
|
|
13746
|
+
const id = await resolveRuleId(a.rule_id, ctx);
|
|
13747
|
+
if (!id)
|
|
13748
|
+
return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
|
|
13749
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${id}`, ctx);
|
|
13750
|
+
return isEmpty(result) ? `IPv6 firewall filter rule '${a.rule_id}' not found.` : `IPV6 FIREWALL FILTER RULE DETAILS:
|
|
13674
13751
|
|
|
13675
13752
|
${result}`;
|
|
13676
13753
|
}
|
|
@@ -13746,13 +13823,13 @@ ${result}`;
|
|
|
13746
13823
|
inputSchema: { rule_id: z44.string() },
|
|
13747
13824
|
async handler(a, ctx) {
|
|
13748
13825
|
ctx.info(`Removing IPv6 firewall filter rule: rule_id=${a.rule_id}`);
|
|
13749
|
-
const
|
|
13750
|
-
if (
|
|
13751
|
-
return `IPv6 firewall filter rule
|
|
13752
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall filter remove ${
|
|
13826
|
+
const id = await resolveRuleId(a.rule_id, ctx);
|
|
13827
|
+
if (!id)
|
|
13828
|
+
return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
|
|
13829
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall filter remove ${id}`, ctx);
|
|
13753
13830
|
if (looksLikeError(result))
|
|
13754
13831
|
return `Failed to remove IPv6 firewall filter rule: ${result}`;
|
|
13755
|
-
return `IPv6 firewall filter rule
|
|
13832
|
+
return `IPv6 firewall filter rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
13756
13833
|
}
|
|
13757
13834
|
}),
|
|
13758
13835
|
defineTool({
|
|
@@ -13766,13 +13843,13 @@ ${result}`;
|
|
|
13766
13843
|
},
|
|
13767
13844
|
async handler(a, ctx) {
|
|
13768
13845
|
ctx.info(`Moving IPv6 firewall filter rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
13769
|
-
const
|
|
13770
|
-
if (
|
|
13771
|
-
return `IPv6 firewall filter rule
|
|
13772
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall filter move ${
|
|
13846
|
+
const id = await resolveRuleId(a.rule_id, ctx);
|
|
13847
|
+
if (!id)
|
|
13848
|
+
return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
|
|
13849
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall filter move ${id} destination=${a.destination}`, ctx);
|
|
13773
13850
|
if (looksLikeError(result))
|
|
13774
13851
|
return `Failed to move IPv6 firewall filter rule: ${result}`;
|
|
13775
|
-
return `IPv6 firewall filter rule
|
|
13852
|
+
return `IPv6 firewall filter rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
13776
13853
|
}
|
|
13777
13854
|
}),
|
|
13778
13855
|
defineTool({
|
|
@@ -13800,6 +13877,7 @@ ${result}`;
|
|
|
13800
13877
|
// src/tools/ipv6-firewall-nat.ts
|
|
13801
13878
|
import { z as z45 } from "zod";
|
|
13802
13879
|
var isDigits5 = (s) => /^\d+$/.test(s);
|
|
13880
|
+
var resolveRuleId2 = ruleResolver("/ipv6 firewall nat");
|
|
13803
13881
|
async function updateNatRule2(a, ctx) {
|
|
13804
13882
|
ctx.info(`Updating IPv6 firewall NAT rule: rule_id=${a.rule_id}`);
|
|
13805
13883
|
const updates = [];
|
|
@@ -13867,11 +13945,14 @@ async function updateNatRule2(a, ctx) {
|
|
|
13867
13945
|
}
|
|
13868
13946
|
if (updates.length === 0)
|
|
13869
13947
|
return "No updates specified.";
|
|
13870
|
-
const
|
|
13948
|
+
const id = await resolveRuleId2(a.rule_id, ctx);
|
|
13949
|
+
if (!id)
|
|
13950
|
+
return `IPv6 NAT rule '${a.rule_id}' not found.`;
|
|
13951
|
+
const cmd = `/ipv6 firewall nat set ${id} ${updates.join(" ")}`;
|
|
13871
13952
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
13872
13953
|
if (looksLikeError(result))
|
|
13873
13954
|
return `Failed to update IPv6 firewall NAT rule: ${result}`;
|
|
13874
|
-
const details = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${
|
|
13955
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${id}`, ctx);
|
|
13875
13956
|
return `IPv6 firewall NAT rule updated successfully:
|
|
13876
13957
|
|
|
13877
13958
|
${details}`;
|
|
@@ -14022,8 +14103,11 @@ ${result}`;
|
|
|
14022
14103
|
},
|
|
14023
14104
|
async handler(a, ctx) {
|
|
14024
14105
|
ctx.info(`Getting IPv6 firewall NAT rule details: rule_id=${a.rule_id}`);
|
|
14025
|
-
const
|
|
14026
|
-
|
|
14106
|
+
const id = await resolveRuleId2(a.rule_id, ctx);
|
|
14107
|
+
if (!id)
|
|
14108
|
+
return `IPv6 firewall NAT rule '${a.rule_id}' not found.`;
|
|
14109
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${id}`, ctx);
|
|
14110
|
+
return isEmpty(result) ? `IPv6 firewall NAT rule '${a.rule_id}' not found.` : `IPV6 FIREWALL NAT RULE DETAILS:
|
|
14027
14111
|
|
|
14028
14112
|
${result}`;
|
|
14029
14113
|
}
|
|
@@ -14098,13 +14182,13 @@ ${result}`;
|
|
|
14098
14182
|
inputSchema: { rule_id: z45.string() },
|
|
14099
14183
|
async handler(a, ctx) {
|
|
14100
14184
|
ctx.info(`Removing IPv6 firewall NAT rule: rule_id=${a.rule_id}`);
|
|
14101
|
-
const
|
|
14102
|
-
if (
|
|
14103
|
-
return `IPv6 firewall NAT rule
|
|
14104
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall nat remove ${
|
|
14185
|
+
const id = await resolveRuleId2(a.rule_id, ctx);
|
|
14186
|
+
if (!id)
|
|
14187
|
+
return `IPv6 firewall NAT rule '${a.rule_id}' not found.`;
|
|
14188
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall nat remove ${id}`, ctx);
|
|
14105
14189
|
if (looksLikeError(result))
|
|
14106
14190
|
return `Failed to remove IPv6 firewall NAT rule: ${result}`;
|
|
14107
|
-
return `IPv6 firewall NAT rule
|
|
14191
|
+
return `IPv6 firewall NAT rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
14108
14192
|
}
|
|
14109
14193
|
}),
|
|
14110
14194
|
defineTool({
|
|
@@ -14118,13 +14202,13 @@ ${result}`;
|
|
|
14118
14202
|
},
|
|
14119
14203
|
async handler(a, ctx) {
|
|
14120
14204
|
ctx.info(`Moving IPv6 firewall NAT rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
14121
|
-
const
|
|
14122
|
-
if (
|
|
14123
|
-
return `IPv6 firewall NAT rule
|
|
14124
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall nat move ${
|
|
14205
|
+
const id = await resolveRuleId2(a.rule_id, ctx);
|
|
14206
|
+
if (!id)
|
|
14207
|
+
return `IPv6 firewall NAT rule '${a.rule_id}' not found.`;
|
|
14208
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall nat move ${id} destination=${a.destination}`, ctx);
|
|
14125
14209
|
if (looksLikeError(result))
|
|
14126
14210
|
return `Failed to move IPv6 firewall NAT rule: ${result}`;
|
|
14127
|
-
return `IPv6 firewall NAT rule
|
|
14211
|
+
return `IPv6 firewall NAT rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
14128
14212
|
}
|
|
14129
14213
|
}),
|
|
14130
14214
|
defineTool({
|
|
@@ -14152,6 +14236,7 @@ ${result}`;
|
|
|
14152
14236
|
// src/tools/ipv6-firewall-mangle.ts
|
|
14153
14237
|
import { z as z46 } from "zod";
|
|
14154
14238
|
var isDigits6 = (s) => /^\d+$/.test(s);
|
|
14239
|
+
var resolveRuleId3 = ruleResolver("/ipv6 firewall mangle");
|
|
14155
14240
|
async function updateMangleRule2(a, ctx) {
|
|
14156
14241
|
ctx.info(`Updating IPv6 firewall mangle rule: rule_id=${a.rule_id}`);
|
|
14157
14242
|
const updates = [];
|
|
@@ -14227,11 +14312,14 @@ async function updateMangleRule2(a, ctx) {
|
|
|
14227
14312
|
}
|
|
14228
14313
|
if (updates.length === 0)
|
|
14229
14314
|
return "No updates specified.";
|
|
14230
|
-
const
|
|
14315
|
+
const id = await resolveRuleId3(a.rule_id, ctx);
|
|
14316
|
+
if (!id)
|
|
14317
|
+
return `IPv6 mangle rule '${a.rule_id}' not found.`;
|
|
14318
|
+
const cmd = `/ipv6 firewall mangle set ${id} ${updates.join(" ")}`;
|
|
14231
14319
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
14232
14320
|
if (looksLikeError(result))
|
|
14233
14321
|
return `Failed to update IPv6 firewall mangle rule: ${result}`;
|
|
14234
|
-
const details = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${
|
|
14322
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${id}`, ctx);
|
|
14235
14323
|
return `IPv6 firewall mangle rule updated successfully:
|
|
14236
14324
|
|
|
14237
14325
|
${details}`;
|
|
@@ -14390,8 +14478,11 @@ ${result}`;
|
|
|
14390
14478
|
},
|
|
14391
14479
|
async handler(a, ctx) {
|
|
14392
14480
|
ctx.info(`Getting IPv6 firewall mangle rule details: rule_id=${a.rule_id}`);
|
|
14393
|
-
const
|
|
14394
|
-
|
|
14481
|
+
const id = await resolveRuleId3(a.rule_id, ctx);
|
|
14482
|
+
if (!id)
|
|
14483
|
+
return `IPv6 mangle rule '${a.rule_id}' not found.`;
|
|
14484
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${id}`, ctx);
|
|
14485
|
+
return isEmpty(result) ? `IPv6 mangle rule '${a.rule_id}' not found.` : `IPV6 FIREWALL MANGLE RULE DETAILS:
|
|
14395
14486
|
|
|
14396
14487
|
${result}`;
|
|
14397
14488
|
}
|
|
@@ -14473,13 +14564,13 @@ ${result}`;
|
|
|
14473
14564
|
inputSchema: { rule_id: z46.string() },
|
|
14474
14565
|
async handler(a, ctx) {
|
|
14475
14566
|
ctx.info(`Removing IPv6 firewall mangle rule: rule_id=${a.rule_id}`);
|
|
14476
|
-
const
|
|
14477
|
-
if (
|
|
14478
|
-
return `IPv6
|
|
14479
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall mangle remove ${
|
|
14567
|
+
const id = await resolveRuleId3(a.rule_id, ctx);
|
|
14568
|
+
if (!id)
|
|
14569
|
+
return `IPv6 mangle rule '${a.rule_id}' not found.`;
|
|
14570
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall mangle remove ${id}`, ctx);
|
|
14480
14571
|
if (looksLikeError(result))
|
|
14481
14572
|
return `Failed to remove IPv6 firewall mangle rule: ${result}`;
|
|
14482
|
-
return `IPv6
|
|
14573
|
+
return `IPv6 mangle rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
14483
14574
|
}
|
|
14484
14575
|
}),
|
|
14485
14576
|
defineTool({
|
|
@@ -14493,13 +14584,13 @@ ${result}`;
|
|
|
14493
14584
|
},
|
|
14494
14585
|
async handler(a, ctx) {
|
|
14495
14586
|
ctx.info(`Moving IPv6 firewall mangle rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
14496
|
-
const
|
|
14497
|
-
if (
|
|
14498
|
-
return `IPv6
|
|
14499
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall mangle move ${
|
|
14587
|
+
const id = await resolveRuleId3(a.rule_id, ctx);
|
|
14588
|
+
if (!id)
|
|
14589
|
+
return `IPv6 mangle rule '${a.rule_id}' not found.`;
|
|
14590
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall mangle move ${id} destination=${a.destination}`, ctx);
|
|
14500
14591
|
if (looksLikeError(result))
|
|
14501
14592
|
return `Failed to move IPv6 firewall mangle rule: ${result}`;
|
|
14502
|
-
return `IPv6
|
|
14593
|
+
return `IPv6 mangle rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
14503
14594
|
}
|
|
14504
14595
|
}),
|
|
14505
14596
|
defineTool({
|
|
@@ -14527,6 +14618,7 @@ ${result}`;
|
|
|
14527
14618
|
// src/tools/ipv6-firewall-raw.ts
|
|
14528
14619
|
import { z as z47 } from "zod";
|
|
14529
14620
|
var isDigits7 = (s) => /^\d+$/.test(s);
|
|
14621
|
+
var resolveRuleId4 = ruleResolver("/ipv6 firewall raw");
|
|
14530
14622
|
async function updateRawRule(a, ctx) {
|
|
14531
14623
|
ctx.info(`Updating IPv6 firewall raw rule: rule_id=${a.rule_id}`);
|
|
14532
14624
|
const updates = [];
|
|
@@ -14572,11 +14664,14 @@ async function updateRawRule(a, ctx) {
|
|
|
14572
14664
|
}
|
|
14573
14665
|
if (updates.length === 0)
|
|
14574
14666
|
return "No updates specified.";
|
|
14575
|
-
const
|
|
14667
|
+
const id = await resolveRuleId4(a.rule_id, ctx);
|
|
14668
|
+
if (!id)
|
|
14669
|
+
return `IPv6 raw rule '${a.rule_id}' not found.`;
|
|
14670
|
+
const cmd = `/ipv6 firewall raw set ${id} ${updates.join(" ")}`;
|
|
14576
14671
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
14577
14672
|
if (looksLikeError(result))
|
|
14578
14673
|
return `Failed to update IPv6 firewall raw rule: ${result}`;
|
|
14579
|
-
const details = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${
|
|
14674
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${id}`, ctx);
|
|
14580
14675
|
return `IPv6 firewall raw rule updated successfully:
|
|
14581
14676
|
|
|
14582
14677
|
${details}`;
|
|
@@ -14691,7 +14786,10 @@ ${result}`;
|
|
|
14691
14786
|
},
|
|
14692
14787
|
async handler(a, ctx) {
|
|
14693
14788
|
ctx.info(`Getting IPv6 firewall raw rule details: rule_id=${a.rule_id}`);
|
|
14694
|
-
const
|
|
14789
|
+
const id = await resolveRuleId4(a.rule_id, ctx);
|
|
14790
|
+
if (!id)
|
|
14791
|
+
return `IPv6 firewall raw rule with ID '${a.rule_id}' not found.`;
|
|
14792
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${id}`, ctx);
|
|
14695
14793
|
return isEmpty(result) ? `IPv6 firewall raw rule with ID '${a.rule_id}' not found.` : `IPV6 FIREWALL RAW RULE DETAILS:
|
|
14696
14794
|
|
|
14697
14795
|
${result}`;
|
|
@@ -14745,13 +14843,13 @@ ${result}`;
|
|
|
14745
14843
|
inputSchema: { rule_id: z47.string() },
|
|
14746
14844
|
async handler(a, ctx) {
|
|
14747
14845
|
ctx.info(`Removing IPv6 firewall raw rule: rule_id=${a.rule_id}`);
|
|
14748
|
-
const
|
|
14749
|
-
if (
|
|
14846
|
+
const id = await resolveRuleId4(a.rule_id, ctx);
|
|
14847
|
+
if (!id)
|
|
14750
14848
|
return `IPv6 firewall raw rule with ID '${a.rule_id}' not found.`;
|
|
14751
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall raw remove ${
|
|
14849
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall raw remove ${id}`, ctx);
|
|
14752
14850
|
if (looksLikeError(result))
|
|
14753
14851
|
return `Failed to remove IPv6 firewall raw rule: ${result}`;
|
|
14754
|
-
return `IPv6 firewall raw rule
|
|
14852
|
+
return `IPv6 firewall raw rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
14755
14853
|
}
|
|
14756
14854
|
}),
|
|
14757
14855
|
defineTool({
|
|
@@ -14765,13 +14863,13 @@ ${result}`;
|
|
|
14765
14863
|
},
|
|
14766
14864
|
async handler(a, ctx) {
|
|
14767
14865
|
ctx.info(`Moving IPv6 firewall raw rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
14768
|
-
const
|
|
14769
|
-
if (
|
|
14866
|
+
const id = await resolveRuleId4(a.rule_id, ctx);
|
|
14867
|
+
if (!id)
|
|
14770
14868
|
return `IPv6 firewall raw rule with ID '${a.rule_id}' not found.`;
|
|
14771
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall raw move ${
|
|
14869
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall raw move ${id} destination=${a.destination}`, ctx);
|
|
14772
14870
|
if (looksLikeError(result))
|
|
14773
14871
|
return `Failed to move IPv6 firewall raw rule: ${result}`;
|
|
14774
|
-
return `IPv6 firewall raw rule
|
|
14872
|
+
return `IPv6 firewall raw rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
14775
14873
|
}
|
|
14776
14874
|
}),
|
|
14777
14875
|
defineTool({
|
|
@@ -22117,7 +22215,7 @@ ${result}`;
|
|
|
22117
22215
|
name: "check_route_path",
|
|
22118
22216
|
title: "Check IPv4 Route Path",
|
|
22119
22217
|
annotations: READ,
|
|
22120
|
-
description: "Resolves which nexthop RouterOS would use for a given IPv4 destination " + '\u2014 answers "which gateway will this packet take?" without sending any traffic. ' + "Version-aware: uses `/ip route check` on v6, falls back to " + "`/ip route print where dst-address
|
|
22218
|
+
description: "Resolves which nexthop RouterOS would use for a given IPv4 destination " + '\u2014 answers "which gateway will this packet take?" without sending any traffic. ' + "Version-aware: uses `/ip route check` on v6, falls back to " + "`/ip route print where <dest> in dst-address active=yes` on v7+ where the check " + "command was removed. Optionally scoped by `routing_table` (v7) / `routing_mark` (v6) " + "for policy-routing table lookups. " + "For listing all known routes use list_routes; for a named-table view use get_routing_table. " + "Returns the resolved nexthop and interface detail.",
|
|
22121
22219
|
inputSchema: {
|
|
22122
22220
|
destination: z88.string(),
|
|
22123
22221
|
routing_table: z88.string().optional().describe('Policy-routing table name (v7) or routing-mark (v6), e.g. "VPN"')
|
|
@@ -22132,8 +22230,8 @@ ${result}`;
|
|
|
22132
22230
|
|
|
22133
22231
|
${v6Result}`;
|
|
22134
22232
|
}
|
|
22135
|
-
const where = [
|
|
22136
|
-
if (table)
|
|
22233
|
+
const where = [`${a.destination} in dst-address`, "active=yes"];
|
|
22234
|
+
if (table && table !== "main")
|
|
22137
22235
|
where.push(`routing-table=${quoteValue(table)}`);
|
|
22138
22236
|
const v7Cmd = `/ip route print detail where ${where.join(" ")}`;
|
|
22139
22237
|
const v7Result = await executeMikrotikCommand(v7Cmd, ctx);
|
|
@@ -24927,13 +25025,15 @@ async function fetchLatestRelease() {
|
|
|
24927
25025
|
throw new Error(`GitHub API ${res.status}`);
|
|
24928
25026
|
const gh = await res.json();
|
|
24929
25027
|
const latestVersion = gh.tag_name.replace(/^v/, "");
|
|
25028
|
+
const cmp = compareVersions(latestVersion, VERSION);
|
|
24930
25029
|
const data = {
|
|
24931
25030
|
version: latestVersion,
|
|
24932
25031
|
name: gh.name || `v${latestVersion}`,
|
|
24933
25032
|
body: gh.body || "",
|
|
24934
25033
|
publishedAt: gh.published_at,
|
|
24935
25034
|
url: gh.html_url,
|
|
24936
|
-
isNewer:
|
|
25035
|
+
isNewer: cmp > 0,
|
|
25036
|
+
isAhead: cmp < 0,
|
|
24937
25037
|
currentVersion: VERSION
|
|
24938
25038
|
};
|
|
24939
25039
|
memoryCache = { data, fetchedAt: Date.now() };
|
|
@@ -25041,14 +25141,18 @@ var serverPulseTools = [
|
|
|
25041
25141
|
const sep = "\u2500".repeat(50);
|
|
25042
25142
|
sections.push(`SERVER PULSE \u2014 ${SERVER_TITLE} v${VERSION}`, sep, "Package: @usex/mikrotik-mcp", `Version: ${VERSION}`, `Uptime: ${formatUptime(uptime)}`, `Website: ${WEBSITE_URL}`);
|
|
25043
25143
|
if (result.release) {
|
|
25044
|
-
const freshness = assessFreshness(VERSION, result.release.version);
|
|
25045
|
-
const label = freshnessLabel(freshness);
|
|
25046
25144
|
const age = timeAgo(result.release.publishedAt);
|
|
25047
|
-
|
|
25048
|
-
|
|
25049
|
-
sections.push("", ">>> A newer version is available! <<<");
|
|
25145
|
+
if (result.release.isAhead) {
|
|
25146
|
+
sections.push("", "UPDATE STATUS: AHEAD OF LATEST RELEASE", sep, `Current: v${VERSION}`, `Published: v${result.release.version} (${result.release.name})`, `Released: ${age}`, `Freshness: DEV BUILD`, "", "You are running ahead of the latest published release.");
|
|
25050
25147
|
} else {
|
|
25051
|
-
|
|
25148
|
+
const freshness = assessFreshness(VERSION, result.release.version);
|
|
25149
|
+
const label = freshnessLabel(freshness);
|
|
25150
|
+
sections.push("", `UPDATE STATUS: ${label}`, sep, `Current: v${VERSION}`, `Latest: v${result.release.version} (${result.release.name})`, `Published: ${age}`, `Freshness: ${freshness.toUpperCase()}`);
|
|
25151
|
+
if (result.release.isNewer) {
|
|
25152
|
+
sections.push("", ">>> A newer version is available! <<<");
|
|
25153
|
+
} else {
|
|
25154
|
+
sections.push("", "You are running the latest version.");
|
|
25155
|
+
}
|
|
25052
25156
|
}
|
|
25053
25157
|
if (result.release.isNewer) {
|
|
25054
25158
|
sections.push("", "UPGRADE", sep);
|