@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,12 +436,34 @@ 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 loadConfig(argv = process.argv.slice(2)) {
|
|
443
458
|
const flags = parseFlags(argv);
|
|
444
459
|
const pick = (flag, ...envNames) => flags[flag] ?? env(...envNames);
|
|
460
|
+
const overlay = (file, over) => {
|
|
461
|
+
const out = { ...file };
|
|
462
|
+
for (const [k, v] of Object.entries(over))
|
|
463
|
+
if (v !== undefined)
|
|
464
|
+
out[k] = v;
|
|
465
|
+
return out;
|
|
466
|
+
};
|
|
445
467
|
const jumpHostName = pick("jump-host", "MIKROTIK_JUMP_HOST");
|
|
446
468
|
const jumpHost = jumpHostName ? {
|
|
447
469
|
host: jumpHostName,
|
|
@@ -482,6 +504,9 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
482
504
|
let fileMcp;
|
|
483
505
|
let fileSsh;
|
|
484
506
|
let fileMemory;
|
|
507
|
+
let fileReadOnly;
|
|
508
|
+
let fileDisableUpdateCheck;
|
|
509
|
+
let fileBackupDir;
|
|
485
510
|
if (configFile || devicesInline) {
|
|
486
511
|
const src = configFile ? parseDevicesSource(configFile, true) : parseDevicesSource(devicesInline, false);
|
|
487
512
|
for (const [name, dc] of Object.entries(src.devices))
|
|
@@ -496,8 +521,11 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
496
521
|
fileMcp = src.mcp;
|
|
497
522
|
fileSsh = src.ssh;
|
|
498
523
|
fileMemory = src.memory;
|
|
524
|
+
fileReadOnly = src.readOnly;
|
|
525
|
+
fileDisableUpdateCheck = src.disableUpdateCheck;
|
|
526
|
+
fileBackupDir = src.backupDir;
|
|
499
527
|
}
|
|
500
|
-
const s3 = {
|
|
528
|
+
const s3 = overlay(fileS3, {
|
|
501
529
|
accessKeyId: pick("s3-access-key-id", "S3_ACCESS_KEY_ID", "AWS_ACCESS_KEY_ID"),
|
|
502
530
|
secretAccessKey: pick("s3-secret-access-key", "S3_SECRET_ACCESS_KEY", "AWS_SECRET_ACCESS_KEY"),
|
|
503
531
|
sessionToken: pick("s3-session-token", "S3_SESSION_TOKEN", "AWS_SESSION_TOKEN"),
|
|
@@ -505,12 +533,11 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
505
533
|
endpoint: pick("s3-endpoint", "S3_ENDPOINT", "AWS_ENDPOINT"),
|
|
506
534
|
bucket: pick("s3-bucket", "S3_BUCKET", "AWS_BUCKET"),
|
|
507
535
|
prefix: pick("s3-prefix", "MIKROTIK_S3_PREFIX"),
|
|
508
|
-
presignExpiresIn: pick("s3-presign-expires-in", "MIKROTIK_S3_PRESIGN_EXPIRES_IN")
|
|
509
|
-
|
|
510
|
-
};
|
|
536
|
+
presignExpiresIn: pick("s3-presign-expires-in", "MIKROTIK_S3_PRESIGN_EXPIRES_IN")
|
|
537
|
+
});
|
|
511
538
|
const appViewsRaw = pick("app-views", "MIKROTIK_MCP__APP_VIEWS");
|
|
512
539
|
const appViewsEnv = appViewsRaw === undefined ? undefined : !/^(0|false|no|off)$/i.test(appViewsRaw);
|
|
513
|
-
const mcp = {
|
|
540
|
+
const mcp = overlay(fileMcp, {
|
|
514
541
|
transport: pick("transport", "MIKROTIK_MCP__TRANSPORT", "MCP_TRANSPORT"),
|
|
515
542
|
host: pick("mcp-host", "MIKROTIK_MCP__HOST"),
|
|
516
543
|
port: pick("mcp-port", "MIKROTIK_MCP__PORT"),
|
|
@@ -518,22 +545,22 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
518
545
|
allowedOrigins: pick("mcp-allowed-origins", "MIKROTIK_MCP__ALLOWED_ORIGINS"),
|
|
519
546
|
corsOrigins: pick("mcp-cors-origins", "MIKROTIK_MCP__CORS_ORIGINS"),
|
|
520
547
|
toolPageSize: pick("tool-page-size", "MIKROTIK_MCP__TOOL_PAGE_SIZE"),
|
|
521
|
-
appViews: appViewsEnv
|
|
522
|
-
|
|
523
|
-
};
|
|
548
|
+
appViews: appViewsEnv
|
|
549
|
+
});
|
|
524
550
|
const isTruthy = (v) => /^(1|true|yes|on)$/i.test(v ?? "");
|
|
525
|
-
const
|
|
526
|
-
const
|
|
551
|
+
const boolFileFlag = (flagVal, fileVal) => flagVal !== undefined ? isTruthy(flagVal) : fileVal;
|
|
552
|
+
const readOnly = boolFileFlag(pick("read-only", "MIKROTIK_READ_ONLY"), fileReadOnly);
|
|
553
|
+
const disableUpdateCheck = boolFileFlag(pick("disable-update-check", "MIKROTIK_DISABLE_UPDATE_CHECK"), fileDisableUpdateCheck);
|
|
554
|
+
const backupDir = pick("backup-dir", "MIKROTIK_BACKUP_DIR") ?? fileBackupDir;
|
|
527
555
|
const csv = (v) => v === undefined ? undefined : v.split(",").map((s) => s.trim()).filter(Boolean);
|
|
528
|
-
const tools = {
|
|
556
|
+
const tools = overlay(fileTools, {
|
|
529
557
|
enabledModules: csv(pick("tools-enabled-modules", "MIKROTIK_TOOLS__ENABLED_MODULES")),
|
|
530
558
|
disabledModules: csv(pick("tools-disabled-modules", "MIKROTIK_TOOLS__DISABLED_MODULES")),
|
|
531
559
|
enabledGroups: csv(pick("tools-enabled-groups", "MIKROTIK_TOOLS__ENABLED_GROUPS")),
|
|
532
|
-
disabledGroups: csv(pick("tools-disabled-groups", "MIKROTIK_TOOLS__DISABLED_GROUPS"))
|
|
533
|
-
|
|
534
|
-
};
|
|
560
|
+
disabledGroups: csv(pick("tools-disabled-groups", "MIKROTIK_TOOLS__DISABLED_GROUPS"))
|
|
561
|
+
});
|
|
535
562
|
const boolOpt = (v) => v === undefined ? undefined : isTruthy(v);
|
|
536
|
-
const dashboard = {
|
|
563
|
+
const dashboard = overlay(fileDashboard, {
|
|
537
564
|
enabled: boolOpt(pick("dashboard", "MIKROTIK_DASHBOARD__ENABLED", "MIKROTIK_DASHBOARD")),
|
|
538
565
|
host: pick("dashboard-host", "MIKROTIK_DASHBOARD__HOST"),
|
|
539
566
|
port: pick("dashboard-port", "MIKROTIK_DASHBOARD__PORT"),
|
|
@@ -542,20 +569,17 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
542
569
|
captureBody: boolOpt(pick("dashboard-capture-body", "MIKROTIK_DASHBOARD__CAPTURE_BODY")),
|
|
543
570
|
redactInput: boolOpt(pick("dashboard-redact-input", "MIKROTIK_DASHBOARD__REDACT_INPUT")),
|
|
544
571
|
maxBodyBytes: pick("dashboard-max-body-bytes", "MIKROTIK_DASHBOARD__MAX_BODY_BYTES"),
|
|
545
|
-
token: pick("dashboard-token", "MIKROTIK_DASHBOARD__TOKEN")
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const ssh = {
|
|
572
|
+
token: pick("dashboard-token", "MIKROTIK_DASHBOARD__TOKEN")
|
|
573
|
+
});
|
|
574
|
+
const ssh = overlay(fileSsh, {
|
|
549
575
|
keepAlive: boolOpt(pick("ssh-keep-alive", "MIKROTIK_SSH__KEEP_ALIVE")),
|
|
550
576
|
keepAliveInterval: pick("ssh-keepalive-interval", "MIKROTIK_SSH__KEEPALIVE_INTERVAL"),
|
|
551
|
-
idleTimeout: pick("ssh-idle-timeout", "MIKROTIK_SSH__IDLE_TIMEOUT")
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
const memory = {
|
|
577
|
+
idleTimeout: pick("ssh-idle-timeout", "MIKROTIK_SSH__IDLE_TIMEOUT")
|
|
578
|
+
});
|
|
579
|
+
const memory = overlay(fileMemory, {
|
|
555
580
|
enabled: boolOpt(pick("memory-enabled", "MIKROTIK_MEMORY__ENABLED")),
|
|
556
|
-
dbPath: pick("memory-db", "MIKROTIK_MEMORY__DB_PATH")
|
|
557
|
-
|
|
558
|
-
};
|
|
581
|
+
dbPath: pick("memory-db", "MIKROTIK_MEMORY__DB_PATH")
|
|
582
|
+
});
|
|
559
583
|
const hasS3 = !!(s3.accessKeyId || s3.bucket || s3.endpoint);
|
|
560
584
|
const raw = {
|
|
561
585
|
devices: Object.keys(devices).length ? devices : { default: {} },
|
|
@@ -564,6 +588,7 @@ function loadConfig(argv = process.argv.slice(2)) {
|
|
|
564
588
|
dashboard,
|
|
565
589
|
readOnly,
|
|
566
590
|
disableUpdateCheck,
|
|
591
|
+
backupDir,
|
|
567
592
|
tools,
|
|
568
593
|
ssh,
|
|
569
594
|
memory,
|
|
@@ -1189,6 +1214,8 @@ function stripAnsi(text) {
|
|
|
1189
1214
|
return text.replace(ANSI_RE, "");
|
|
1190
1215
|
}
|
|
1191
1216
|
var CTRL_X = "\x18";
|
|
1217
|
+
var IDLE_TIMEOUT_MS = 15000;
|
|
1218
|
+
var HARD_CAP_MS = 120000;
|
|
1192
1219
|
function isSafeModeActivated(response) {
|
|
1193
1220
|
if (response.includes("<SAFE>"))
|
|
1194
1221
|
return true;
|
|
@@ -1264,7 +1291,7 @@ class SafeModeManager {
|
|
|
1264
1291
|
`);
|
|
1265
1292
|
const { text, timedOut } = await this.readUntilPrompt();
|
|
1266
1293
|
if (timedOut) {
|
|
1267
|
-
throw new Error(`Safe Mode shell
|
|
1294
|
+
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).");
|
|
1268
1295
|
}
|
|
1269
1296
|
return this.extractOutput(text, command);
|
|
1270
1297
|
});
|
|
@@ -1318,25 +1345,32 @@ class SafeModeManager {
|
|
|
1318
1345
|
status() {
|
|
1319
1346
|
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.";
|
|
1320
1347
|
}
|
|
1321
|
-
readUntilPrompt(
|
|
1348
|
+
readUntilPrompt(idleMs = IDLE_TIMEOUT_MS, isDone = (c) => PROMPT_RE.test(c), hardCapMs = HARD_CAP_MS) {
|
|
1322
1349
|
const channel = this.channel;
|
|
1323
1350
|
if (!channel)
|
|
1324
1351
|
return Promise.resolve({ text: "", timedOut: false });
|
|
1325
1352
|
return new Promise((resolve2) => {
|
|
1326
1353
|
let buf = "";
|
|
1327
|
-
let
|
|
1354
|
+
let idleTimer;
|
|
1355
|
+
const hardTimer = setTimeout(() => finish(stripAnsi(buf), true), hardCapMs);
|
|
1356
|
+
function armIdle2() {
|
|
1357
|
+
clearTimeout(idleTimer);
|
|
1358
|
+
idleTimer = setTimeout(() => finish(stripAnsi(buf), true), idleMs);
|
|
1359
|
+
}
|
|
1328
1360
|
function onData(chunk) {
|
|
1329
1361
|
buf += decodeOutput(chunk);
|
|
1362
|
+
armIdle2();
|
|
1330
1363
|
const cleaned = stripAnsi(buf);
|
|
1331
1364
|
if (isDone(cleaned))
|
|
1332
1365
|
finish(cleaned, false);
|
|
1333
1366
|
}
|
|
1334
1367
|
function finish(result, timedOut) {
|
|
1335
|
-
clearTimeout(
|
|
1368
|
+
clearTimeout(idleTimer);
|
|
1369
|
+
clearTimeout(hardTimer);
|
|
1336
1370
|
channel.removeListener("data", onData);
|
|
1337
1371
|
resolve2({ text: result, timedOut });
|
|
1338
1372
|
}
|
|
1339
|
-
|
|
1373
|
+
armIdle2();
|
|
1340
1374
|
channel.on("data", onData);
|
|
1341
1375
|
});
|
|
1342
1376
|
}
|
|
@@ -5194,7 +5228,7 @@ var changePlanTools = [
|
|
|
5194
5228
|
const enabled2 = await safe.enable();
|
|
5195
5229
|
if (enabled2.startsWith("Error"))
|
|
5196
5230
|
throw new Error(enabled2);
|
|
5197
|
-
const APPLY_BUDGET_MS =
|
|
5231
|
+
const APPLY_BUDGET_MS = 180000;
|
|
5198
5232
|
const startedAt = Date.now();
|
|
5199
5233
|
const overBudget = () => Date.now() - startedAt > APPLY_BUDGET_MS;
|
|
5200
5234
|
const applyAndDiff = async () => {
|
|
@@ -8138,7 +8172,7 @@ var rawCommandTools = [
|
|
|
8138
8172
|
if (!cmd)
|
|
8139
8173
|
return "Provide a RouterOS command to run.";
|
|
8140
8174
|
ctx.info(`Running raw RouterOS command: ${cmd}`);
|
|
8141
|
-
const result = await executeMikrotikCommand(cmd, ctx);
|
|
8175
|
+
const result = await executeMikrotikCommand(cmd, ctx, { maxMs: 30000 });
|
|
8142
8176
|
if (looksLikeError(result))
|
|
8143
8177
|
return `RouterOS command error: ${result}`;
|
|
8144
8178
|
return result.trim() ? result : "(command completed with no output)";
|
|
@@ -8260,7 +8294,7 @@ var cache = null;
|
|
|
8260
8294
|
async function gateway() {
|
|
8261
8295
|
if (cache)
|
|
8262
8296
|
return cache;
|
|
8263
|
-
const { moduleCatalog } = await import("./library-
|
|
8297
|
+
const { moduleCatalog } = await import("./library-4fpy49qb.js");
|
|
8264
8298
|
const forIndex = [];
|
|
8265
8299
|
const byName = new Map;
|
|
8266
8300
|
for (const mod of moduleCatalog) {
|
|
@@ -10457,7 +10491,30 @@ ${results.join(`
|
|
|
10457
10491
|
|
|
10458
10492
|
// src/tools/firewall-nat.ts
|
|
10459
10493
|
import { z as z29 } from "zod";
|
|
10494
|
+
|
|
10495
|
+
// src/tools/_resolve-rule-id.ts
|
|
10496
|
+
function ruleResolver(scope) {
|
|
10497
|
+
return async function resolveRuleId(ruleId, ctx) {
|
|
10498
|
+
if (/^\d+$/.test(ruleId)) {
|
|
10499
|
+
const id = `*${ruleId}`;
|
|
10500
|
+
const byId = await executeMikrotikCommand(`${scope} print count-only where .id=${id}`, ctx);
|
|
10501
|
+
if (byId.trim() !== "0")
|
|
10502
|
+
return id;
|
|
10503
|
+
const idsRaw = await executeMikrotikCommand(`:foreach i in=[${scope} find] do={:put $i}`, ctx);
|
|
10504
|
+
if (isEmpty(idsRaw))
|
|
10505
|
+
return null;
|
|
10506
|
+
const ids = idsRaw.trim().split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
|
10507
|
+
const pos = Number.parseInt(ruleId, 10);
|
|
10508
|
+
return pos >= 0 && pos < ids.length ? ids[pos] : null;
|
|
10509
|
+
}
|
|
10510
|
+
const count = await executeMikrotikCommand(`${scope} print count-only where .id=${ruleId}`, ctx);
|
|
10511
|
+
return count.trim() !== "0" ? ruleId : null;
|
|
10512
|
+
};
|
|
10513
|
+
}
|
|
10514
|
+
|
|
10515
|
+
// src/tools/firewall-nat.ts
|
|
10460
10516
|
var isDigits2 = (s) => /^\d+$/.test(s);
|
|
10517
|
+
var resolveNatRuleId = ruleResolver("/ip firewall nat");
|
|
10461
10518
|
async function updateNatRule(a, ctx) {
|
|
10462
10519
|
ctx.info(`Updating NAT rule: rule_id=${a.rule_id}`);
|
|
10463
10520
|
const updates = [];
|
|
@@ -10533,11 +10590,14 @@ async function updateNatRule(a, ctx) {
|
|
|
10533
10590
|
}
|
|
10534
10591
|
if (updates.length === 0)
|
|
10535
10592
|
return "No updates specified.";
|
|
10536
|
-
const
|
|
10593
|
+
const id = await resolveNatRuleId(a.rule_id, ctx);
|
|
10594
|
+
if (!id)
|
|
10595
|
+
return `NAT rule '${a.rule_id}' not found.`;
|
|
10596
|
+
const cmd = `/ip firewall nat set ${id} ${updates.join(" ")}`;
|
|
10537
10597
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
10538
10598
|
if (looksLikeError(result))
|
|
10539
10599
|
return `Failed to update NAT rule: ${result}`;
|
|
10540
|
-
const details = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${
|
|
10600
|
+
const details = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${id}`, ctx);
|
|
10541
10601
|
return `NAT rule updated successfully:
|
|
10542
10602
|
|
|
10543
10603
|
${details}`;
|
|
@@ -10715,8 +10775,11 @@ ${result}`;
|
|
|
10715
10775
|
},
|
|
10716
10776
|
async handler(a, ctx) {
|
|
10717
10777
|
ctx.info(`Getting NAT rule details: rule_id=${a.rule_id}`);
|
|
10718
|
-
const
|
|
10719
|
-
|
|
10778
|
+
const id = await resolveNatRuleId(a.rule_id, ctx);
|
|
10779
|
+
if (!id)
|
|
10780
|
+
return `NAT rule '${a.rule_id}' not found.`;
|
|
10781
|
+
const result = await executeMikrotikCommand(`/ip firewall nat print detail where .id=${id}`, ctx);
|
|
10782
|
+
return isEmpty(result) ? `NAT rule '${a.rule_id}' not found.` : `NAT RULE DETAILS:
|
|
10720
10783
|
|
|
10721
10784
|
${result}`;
|
|
10722
10785
|
}
|
|
@@ -10798,13 +10861,13 @@ ${result}`;
|
|
|
10798
10861
|
inputSchema: { rule_id: z29.string() },
|
|
10799
10862
|
async handler(a, ctx) {
|
|
10800
10863
|
ctx.info(`Removing NAT rule: rule_id=${a.rule_id}`);
|
|
10801
|
-
const
|
|
10802
|
-
if (
|
|
10803
|
-
return `NAT rule
|
|
10804
|
-
const result = await executeMikrotikCommand(`/ip firewall nat remove ${
|
|
10864
|
+
const id = await resolveNatRuleId(a.rule_id, ctx);
|
|
10865
|
+
if (!id)
|
|
10866
|
+
return `NAT rule '${a.rule_id}' not found.`;
|
|
10867
|
+
const result = await executeMikrotikCommand(`/ip firewall nat remove ${id}`, ctx);
|
|
10805
10868
|
if (looksLikeError(result))
|
|
10806
10869
|
return `Failed to remove NAT rule: ${result}`;
|
|
10807
|
-
return `NAT rule
|
|
10870
|
+
return `NAT rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
10808
10871
|
}
|
|
10809
10872
|
}),
|
|
10810
10873
|
defineTool({
|
|
@@ -10818,13 +10881,13 @@ ${result}`;
|
|
|
10818
10881
|
},
|
|
10819
10882
|
async handler(a, ctx) {
|
|
10820
10883
|
ctx.info(`Moving NAT rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
10821
|
-
const
|
|
10822
|
-
if (
|
|
10823
|
-
return `NAT rule
|
|
10824
|
-
const result = await executeMikrotikCommand(`/ip firewall nat move ${
|
|
10884
|
+
const id = await resolveNatRuleId(a.rule_id, ctx);
|
|
10885
|
+
if (!id)
|
|
10886
|
+
return `NAT rule '${a.rule_id}' not found.`;
|
|
10887
|
+
const result = await executeMikrotikCommand(`/ip firewall nat move ${id} destination=${a.destination}`, ctx);
|
|
10825
10888
|
if (looksLikeError(result))
|
|
10826
10889
|
return `Failed to move NAT rule: ${result}`;
|
|
10827
|
-
return `NAT rule
|
|
10890
|
+
return `NAT rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
10828
10891
|
}
|
|
10829
10892
|
}),
|
|
10830
10893
|
defineTool({
|
|
@@ -10852,6 +10915,7 @@ ${result}`;
|
|
|
10852
10915
|
// src/tools/firewall-mangle.ts
|
|
10853
10916
|
import { z as z30 } from "zod";
|
|
10854
10917
|
var isDigits3 = (s) => /^\d+$/.test(s);
|
|
10918
|
+
var resolveMangleRuleId = ruleResolver("/ip firewall mangle");
|
|
10855
10919
|
async function updateMangleRule(a, ctx) {
|
|
10856
10920
|
ctx.info(`Updating mangle rule: rule_id=${a.rule_id}`);
|
|
10857
10921
|
const updates = [];
|
|
@@ -10937,11 +11001,14 @@ async function updateMangleRule(a, ctx) {
|
|
|
10937
11001
|
}
|
|
10938
11002
|
if (updates.length === 0)
|
|
10939
11003
|
return "No updates specified.";
|
|
10940
|
-
const
|
|
11004
|
+
const id = await resolveMangleRuleId(a.rule_id, ctx);
|
|
11005
|
+
if (!id)
|
|
11006
|
+
return `Mangle rule '${a.rule_id}' not found.`;
|
|
11007
|
+
const cmd = `/ip firewall mangle set ${id} ${updates.join(" ")}`;
|
|
10941
11008
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
10942
11009
|
if (looksLikeError(result))
|
|
10943
11010
|
return `Failed to update mangle rule: ${result}`;
|
|
10944
|
-
const details = await executeMikrotikCommand(`/ip firewall mangle print detail where .id=${
|
|
11011
|
+
const details = await executeMikrotikCommand(`/ip firewall mangle print detail where .id=${id}`, ctx);
|
|
10945
11012
|
return `Mangle rule updated successfully:
|
|
10946
11013
|
|
|
10947
11014
|
${details}`;
|
|
@@ -11116,8 +11183,11 @@ ${result}`;
|
|
|
11116
11183
|
},
|
|
11117
11184
|
async handler(a, ctx) {
|
|
11118
11185
|
ctx.info(`Getting mangle rule details: rule_id=${a.rule_id}`);
|
|
11119
|
-
const
|
|
11120
|
-
|
|
11186
|
+
const id = await resolveMangleRuleId(a.rule_id, ctx);
|
|
11187
|
+
if (!id)
|
|
11188
|
+
return `Mangle rule '${a.rule_id}' not found.`;
|
|
11189
|
+
const result = await executeMikrotikCommand(`/ip firewall mangle print detail where .id=${id}`, ctx);
|
|
11190
|
+
return isEmpty(result) ? `Mangle rule '${a.rule_id}' not found.` : `MANGLE RULE DETAILS:
|
|
11121
11191
|
|
|
11122
11192
|
${result}`;
|
|
11123
11193
|
}
|
|
@@ -11208,13 +11278,13 @@ ${result}`;
|
|
|
11208
11278
|
inputSchema: { rule_id: z30.string() },
|
|
11209
11279
|
async handler(a, ctx) {
|
|
11210
11280
|
ctx.info(`Removing mangle rule: rule_id=${a.rule_id}`);
|
|
11211
|
-
const
|
|
11212
|
-
if (
|
|
11213
|
-
return `Mangle rule
|
|
11214
|
-
const result = await executeMikrotikCommand(`/ip firewall mangle remove ${
|
|
11281
|
+
const id = await resolveMangleRuleId(a.rule_id, ctx);
|
|
11282
|
+
if (!id)
|
|
11283
|
+
return `Mangle rule '${a.rule_id}' not found.`;
|
|
11284
|
+
const result = await executeMikrotikCommand(`/ip firewall mangle remove ${id}`, ctx);
|
|
11215
11285
|
if (looksLikeError(result))
|
|
11216
11286
|
return `Failed to remove mangle rule: ${result}`;
|
|
11217
|
-
return `Mangle rule
|
|
11287
|
+
return `Mangle rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
11218
11288
|
}
|
|
11219
11289
|
}),
|
|
11220
11290
|
defineTool({
|
|
@@ -11228,13 +11298,13 @@ ${result}`;
|
|
|
11228
11298
|
},
|
|
11229
11299
|
async handler(a, ctx) {
|
|
11230
11300
|
ctx.info(`Moving mangle rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
11231
|
-
const
|
|
11232
|
-
if (
|
|
11233
|
-
return `Mangle rule
|
|
11234
|
-
const result = await executeMikrotikCommand(`/ip firewall mangle move ${
|
|
11301
|
+
const id = await resolveMangleRuleId(a.rule_id, ctx);
|
|
11302
|
+
if (!id)
|
|
11303
|
+
return `Mangle rule '${a.rule_id}' not found.`;
|
|
11304
|
+
const result = await executeMikrotikCommand(`/ip firewall mangle move ${id} destination=${a.destination}`, ctx);
|
|
11235
11305
|
if (looksLikeError(result))
|
|
11236
11306
|
return `Failed to move mangle rule: ${result}`;
|
|
11237
|
-
return `Mangle rule
|
|
11307
|
+
return `Mangle rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
11238
11308
|
}
|
|
11239
11309
|
}),
|
|
11240
11310
|
defineTool({
|
|
@@ -13370,6 +13440,7 @@ ${result}`;
|
|
|
13370
13440
|
// src/tools/ipv6-firewall-filter.ts
|
|
13371
13441
|
import { z as z44 } from "zod";
|
|
13372
13442
|
var isDigits4 = (s) => /^\d+$/.test(s);
|
|
13443
|
+
var resolveRuleId = ruleResolver("/ipv6 firewall filter");
|
|
13373
13444
|
async function updateFilterRule2(a, ctx) {
|
|
13374
13445
|
ctx.info(`Updating IPv6 firewall filter rule: rule_id=${a.rule_id}`);
|
|
13375
13446
|
const updates = [];
|
|
@@ -13438,11 +13509,14 @@ async function updateFilterRule2(a, ctx) {
|
|
|
13438
13509
|
}
|
|
13439
13510
|
if (updates.length === 0)
|
|
13440
13511
|
return "No updates specified.";
|
|
13441
|
-
const
|
|
13512
|
+
const id = await resolveRuleId(a.rule_id, ctx);
|
|
13513
|
+
if (!id)
|
|
13514
|
+
return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
|
|
13515
|
+
const cmd = `/ipv6 firewall filter set ${id} ${updates.join(" ")}`;
|
|
13442
13516
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
13443
13517
|
if (looksLikeError(result))
|
|
13444
13518
|
return `Failed to update IPv6 firewall filter rule: ${result}`;
|
|
13445
|
-
const details = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${
|
|
13519
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${id}`, ctx);
|
|
13446
13520
|
return `IPv6 firewall filter rule updated successfully:
|
|
13447
13521
|
|
|
13448
13522
|
${details}`;
|
|
@@ -13599,8 +13673,11 @@ ${result}`;
|
|
|
13599
13673
|
},
|
|
13600
13674
|
async handler(a, ctx) {
|
|
13601
13675
|
ctx.info(`Getting IPv6 firewall filter rule details: rule_id=${a.rule_id}`);
|
|
13602
|
-
const
|
|
13603
|
-
|
|
13676
|
+
const id = await resolveRuleId(a.rule_id, ctx);
|
|
13677
|
+
if (!id)
|
|
13678
|
+
return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
|
|
13679
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall filter print detail where .id=${id}`, ctx);
|
|
13680
|
+
return isEmpty(result) ? `IPv6 firewall filter rule '${a.rule_id}' not found.` : `IPV6 FIREWALL FILTER RULE DETAILS:
|
|
13604
13681
|
|
|
13605
13682
|
${result}`;
|
|
13606
13683
|
}
|
|
@@ -13676,13 +13753,13 @@ ${result}`;
|
|
|
13676
13753
|
inputSchema: { rule_id: z44.string() },
|
|
13677
13754
|
async handler(a, ctx) {
|
|
13678
13755
|
ctx.info(`Removing IPv6 firewall filter rule: rule_id=${a.rule_id}`);
|
|
13679
|
-
const
|
|
13680
|
-
if (
|
|
13681
|
-
return `IPv6 firewall filter rule
|
|
13682
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall filter remove ${
|
|
13756
|
+
const id = await resolveRuleId(a.rule_id, ctx);
|
|
13757
|
+
if (!id)
|
|
13758
|
+
return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
|
|
13759
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall filter remove ${id}`, ctx);
|
|
13683
13760
|
if (looksLikeError(result))
|
|
13684
13761
|
return `Failed to remove IPv6 firewall filter rule: ${result}`;
|
|
13685
|
-
return `IPv6 firewall filter rule
|
|
13762
|
+
return `IPv6 firewall filter rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
13686
13763
|
}
|
|
13687
13764
|
}),
|
|
13688
13765
|
defineTool({
|
|
@@ -13696,13 +13773,13 @@ ${result}`;
|
|
|
13696
13773
|
},
|
|
13697
13774
|
async handler(a, ctx) {
|
|
13698
13775
|
ctx.info(`Moving IPv6 firewall filter rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
13699
|
-
const
|
|
13700
|
-
if (
|
|
13701
|
-
return `IPv6 firewall filter rule
|
|
13702
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall filter move ${
|
|
13776
|
+
const id = await resolveRuleId(a.rule_id, ctx);
|
|
13777
|
+
if (!id)
|
|
13778
|
+
return `IPv6 firewall filter rule '${a.rule_id}' not found.`;
|
|
13779
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall filter move ${id} destination=${a.destination}`, ctx);
|
|
13703
13780
|
if (looksLikeError(result))
|
|
13704
13781
|
return `Failed to move IPv6 firewall filter rule: ${result}`;
|
|
13705
|
-
return `IPv6 firewall filter rule
|
|
13782
|
+
return `IPv6 firewall filter rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
13706
13783
|
}
|
|
13707
13784
|
}),
|
|
13708
13785
|
defineTool({
|
|
@@ -13730,6 +13807,7 @@ ${result}`;
|
|
|
13730
13807
|
// src/tools/ipv6-firewall-nat.ts
|
|
13731
13808
|
import { z as z45 } from "zod";
|
|
13732
13809
|
var isDigits5 = (s) => /^\d+$/.test(s);
|
|
13810
|
+
var resolveRuleId2 = ruleResolver("/ipv6 firewall nat");
|
|
13733
13811
|
async function updateNatRule2(a, ctx) {
|
|
13734
13812
|
ctx.info(`Updating IPv6 firewall NAT rule: rule_id=${a.rule_id}`);
|
|
13735
13813
|
const updates = [];
|
|
@@ -13797,11 +13875,14 @@ async function updateNatRule2(a, ctx) {
|
|
|
13797
13875
|
}
|
|
13798
13876
|
if (updates.length === 0)
|
|
13799
13877
|
return "No updates specified.";
|
|
13800
|
-
const
|
|
13878
|
+
const id = await resolveRuleId2(a.rule_id, ctx);
|
|
13879
|
+
if (!id)
|
|
13880
|
+
return `IPv6 NAT rule '${a.rule_id}' not found.`;
|
|
13881
|
+
const cmd = `/ipv6 firewall nat set ${id} ${updates.join(" ")}`;
|
|
13801
13882
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
13802
13883
|
if (looksLikeError(result))
|
|
13803
13884
|
return `Failed to update IPv6 firewall NAT rule: ${result}`;
|
|
13804
|
-
const details = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${
|
|
13885
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${id}`, ctx);
|
|
13805
13886
|
return `IPv6 firewall NAT rule updated successfully:
|
|
13806
13887
|
|
|
13807
13888
|
${details}`;
|
|
@@ -13952,8 +14033,11 @@ ${result}`;
|
|
|
13952
14033
|
},
|
|
13953
14034
|
async handler(a, ctx) {
|
|
13954
14035
|
ctx.info(`Getting IPv6 firewall NAT rule details: rule_id=${a.rule_id}`);
|
|
13955
|
-
const
|
|
13956
|
-
|
|
14036
|
+
const id = await resolveRuleId2(a.rule_id, ctx);
|
|
14037
|
+
if (!id)
|
|
14038
|
+
return `IPv6 firewall NAT rule '${a.rule_id}' not found.`;
|
|
14039
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall nat print detail where .id=${id}`, ctx);
|
|
14040
|
+
return isEmpty(result) ? `IPv6 firewall NAT rule '${a.rule_id}' not found.` : `IPV6 FIREWALL NAT RULE DETAILS:
|
|
13957
14041
|
|
|
13958
14042
|
${result}`;
|
|
13959
14043
|
}
|
|
@@ -14028,13 +14112,13 @@ ${result}`;
|
|
|
14028
14112
|
inputSchema: { rule_id: z45.string() },
|
|
14029
14113
|
async handler(a, ctx) {
|
|
14030
14114
|
ctx.info(`Removing IPv6 firewall NAT rule: rule_id=${a.rule_id}`);
|
|
14031
|
-
const
|
|
14032
|
-
if (
|
|
14033
|
-
return `IPv6 firewall NAT rule
|
|
14034
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall nat remove ${
|
|
14115
|
+
const id = await resolveRuleId2(a.rule_id, ctx);
|
|
14116
|
+
if (!id)
|
|
14117
|
+
return `IPv6 firewall NAT rule '${a.rule_id}' not found.`;
|
|
14118
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall nat remove ${id}`, ctx);
|
|
14035
14119
|
if (looksLikeError(result))
|
|
14036
14120
|
return `Failed to remove IPv6 firewall NAT rule: ${result}`;
|
|
14037
|
-
return `IPv6 firewall NAT rule
|
|
14121
|
+
return `IPv6 firewall NAT rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
14038
14122
|
}
|
|
14039
14123
|
}),
|
|
14040
14124
|
defineTool({
|
|
@@ -14048,13 +14132,13 @@ ${result}`;
|
|
|
14048
14132
|
},
|
|
14049
14133
|
async handler(a, ctx) {
|
|
14050
14134
|
ctx.info(`Moving IPv6 firewall NAT rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
14051
|
-
const
|
|
14052
|
-
if (
|
|
14053
|
-
return `IPv6 firewall NAT rule
|
|
14054
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall nat move ${
|
|
14135
|
+
const id = await resolveRuleId2(a.rule_id, ctx);
|
|
14136
|
+
if (!id)
|
|
14137
|
+
return `IPv6 firewall NAT rule '${a.rule_id}' not found.`;
|
|
14138
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall nat move ${id} destination=${a.destination}`, ctx);
|
|
14055
14139
|
if (looksLikeError(result))
|
|
14056
14140
|
return `Failed to move IPv6 firewall NAT rule: ${result}`;
|
|
14057
|
-
return `IPv6 firewall NAT rule
|
|
14141
|
+
return `IPv6 firewall NAT rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
14058
14142
|
}
|
|
14059
14143
|
}),
|
|
14060
14144
|
defineTool({
|
|
@@ -14082,6 +14166,7 @@ ${result}`;
|
|
|
14082
14166
|
// src/tools/ipv6-firewall-mangle.ts
|
|
14083
14167
|
import { z as z46 } from "zod";
|
|
14084
14168
|
var isDigits6 = (s) => /^\d+$/.test(s);
|
|
14169
|
+
var resolveRuleId3 = ruleResolver("/ipv6 firewall mangle");
|
|
14085
14170
|
async function updateMangleRule2(a, ctx) {
|
|
14086
14171
|
ctx.info(`Updating IPv6 firewall mangle rule: rule_id=${a.rule_id}`);
|
|
14087
14172
|
const updates = [];
|
|
@@ -14157,11 +14242,14 @@ async function updateMangleRule2(a, ctx) {
|
|
|
14157
14242
|
}
|
|
14158
14243
|
if (updates.length === 0)
|
|
14159
14244
|
return "No updates specified.";
|
|
14160
|
-
const
|
|
14245
|
+
const id = await resolveRuleId3(a.rule_id, ctx);
|
|
14246
|
+
if (!id)
|
|
14247
|
+
return `IPv6 mangle rule '${a.rule_id}' not found.`;
|
|
14248
|
+
const cmd = `/ipv6 firewall mangle set ${id} ${updates.join(" ")}`;
|
|
14161
14249
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
14162
14250
|
if (looksLikeError(result))
|
|
14163
14251
|
return `Failed to update IPv6 firewall mangle rule: ${result}`;
|
|
14164
|
-
const details = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${
|
|
14252
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${id}`, ctx);
|
|
14165
14253
|
return `IPv6 firewall mangle rule updated successfully:
|
|
14166
14254
|
|
|
14167
14255
|
${details}`;
|
|
@@ -14320,8 +14408,11 @@ ${result}`;
|
|
|
14320
14408
|
},
|
|
14321
14409
|
async handler(a, ctx) {
|
|
14322
14410
|
ctx.info(`Getting IPv6 firewall mangle rule details: rule_id=${a.rule_id}`);
|
|
14323
|
-
const
|
|
14324
|
-
|
|
14411
|
+
const id = await resolveRuleId3(a.rule_id, ctx);
|
|
14412
|
+
if (!id)
|
|
14413
|
+
return `IPv6 mangle rule '${a.rule_id}' not found.`;
|
|
14414
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall mangle print detail where .id=${id}`, ctx);
|
|
14415
|
+
return isEmpty(result) ? `IPv6 mangle rule '${a.rule_id}' not found.` : `IPV6 FIREWALL MANGLE RULE DETAILS:
|
|
14325
14416
|
|
|
14326
14417
|
${result}`;
|
|
14327
14418
|
}
|
|
@@ -14403,13 +14494,13 @@ ${result}`;
|
|
|
14403
14494
|
inputSchema: { rule_id: z46.string() },
|
|
14404
14495
|
async handler(a, ctx) {
|
|
14405
14496
|
ctx.info(`Removing IPv6 firewall mangle rule: rule_id=${a.rule_id}`);
|
|
14406
|
-
const
|
|
14407
|
-
if (
|
|
14408
|
-
return `IPv6
|
|
14409
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall mangle remove ${
|
|
14497
|
+
const id = await resolveRuleId3(a.rule_id, ctx);
|
|
14498
|
+
if (!id)
|
|
14499
|
+
return `IPv6 mangle rule '${a.rule_id}' not found.`;
|
|
14500
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall mangle remove ${id}`, ctx);
|
|
14410
14501
|
if (looksLikeError(result))
|
|
14411
14502
|
return `Failed to remove IPv6 firewall mangle rule: ${result}`;
|
|
14412
|
-
return `IPv6
|
|
14503
|
+
return `IPv6 mangle rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
14413
14504
|
}
|
|
14414
14505
|
}),
|
|
14415
14506
|
defineTool({
|
|
@@ -14423,13 +14514,13 @@ ${result}`;
|
|
|
14423
14514
|
},
|
|
14424
14515
|
async handler(a, ctx) {
|
|
14425
14516
|
ctx.info(`Moving IPv6 firewall mangle rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
14426
|
-
const
|
|
14427
|
-
if (
|
|
14428
|
-
return `IPv6
|
|
14429
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall mangle move ${
|
|
14517
|
+
const id = await resolveRuleId3(a.rule_id, ctx);
|
|
14518
|
+
if (!id)
|
|
14519
|
+
return `IPv6 mangle rule '${a.rule_id}' not found.`;
|
|
14520
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall mangle move ${id} destination=${a.destination}`, ctx);
|
|
14430
14521
|
if (looksLikeError(result))
|
|
14431
14522
|
return `Failed to move IPv6 firewall mangle rule: ${result}`;
|
|
14432
|
-
return `IPv6
|
|
14523
|
+
return `IPv6 mangle rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
14433
14524
|
}
|
|
14434
14525
|
}),
|
|
14435
14526
|
defineTool({
|
|
@@ -14457,6 +14548,7 @@ ${result}`;
|
|
|
14457
14548
|
// src/tools/ipv6-firewall-raw.ts
|
|
14458
14549
|
import { z as z47 } from "zod";
|
|
14459
14550
|
var isDigits7 = (s) => /^\d+$/.test(s);
|
|
14551
|
+
var resolveRuleId4 = ruleResolver("/ipv6 firewall raw");
|
|
14460
14552
|
async function updateRawRule(a, ctx) {
|
|
14461
14553
|
ctx.info(`Updating IPv6 firewall raw rule: rule_id=${a.rule_id}`);
|
|
14462
14554
|
const updates = [];
|
|
@@ -14502,11 +14594,14 @@ async function updateRawRule(a, ctx) {
|
|
|
14502
14594
|
}
|
|
14503
14595
|
if (updates.length === 0)
|
|
14504
14596
|
return "No updates specified.";
|
|
14505
|
-
const
|
|
14597
|
+
const id = await resolveRuleId4(a.rule_id, ctx);
|
|
14598
|
+
if (!id)
|
|
14599
|
+
return `IPv6 raw rule '${a.rule_id}' not found.`;
|
|
14600
|
+
const cmd = `/ipv6 firewall raw set ${id} ${updates.join(" ")}`;
|
|
14506
14601
|
const result = await executeMikrotikCommand(cmd, ctx);
|
|
14507
14602
|
if (looksLikeError(result))
|
|
14508
14603
|
return `Failed to update IPv6 firewall raw rule: ${result}`;
|
|
14509
|
-
const details = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${
|
|
14604
|
+
const details = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${id}`, ctx);
|
|
14510
14605
|
return `IPv6 firewall raw rule updated successfully:
|
|
14511
14606
|
|
|
14512
14607
|
${details}`;
|
|
@@ -14621,7 +14716,10 @@ ${result}`;
|
|
|
14621
14716
|
},
|
|
14622
14717
|
async handler(a, ctx) {
|
|
14623
14718
|
ctx.info(`Getting IPv6 firewall raw rule details: rule_id=${a.rule_id}`);
|
|
14624
|
-
const
|
|
14719
|
+
const id = await resolveRuleId4(a.rule_id, ctx);
|
|
14720
|
+
if (!id)
|
|
14721
|
+
return `IPv6 firewall raw rule with ID '${a.rule_id}' not found.`;
|
|
14722
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall raw print detail where .id=${id}`, ctx);
|
|
14625
14723
|
return isEmpty(result) ? `IPv6 firewall raw rule with ID '${a.rule_id}' not found.` : `IPV6 FIREWALL RAW RULE DETAILS:
|
|
14626
14724
|
|
|
14627
14725
|
${result}`;
|
|
@@ -14675,13 +14773,13 @@ ${result}`;
|
|
|
14675
14773
|
inputSchema: { rule_id: z47.string() },
|
|
14676
14774
|
async handler(a, ctx) {
|
|
14677
14775
|
ctx.info(`Removing IPv6 firewall raw rule: rule_id=${a.rule_id}`);
|
|
14678
|
-
const
|
|
14679
|
-
if (
|
|
14776
|
+
const id = await resolveRuleId4(a.rule_id, ctx);
|
|
14777
|
+
if (!id)
|
|
14680
14778
|
return `IPv6 firewall raw rule with ID '${a.rule_id}' not found.`;
|
|
14681
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall raw remove ${
|
|
14779
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall raw remove ${id}`, ctx);
|
|
14682
14780
|
if (looksLikeError(result))
|
|
14683
14781
|
return `Failed to remove IPv6 firewall raw rule: ${result}`;
|
|
14684
|
-
return `IPv6 firewall raw rule
|
|
14782
|
+
return `IPv6 firewall raw rule '${a.rule_id}' (${id}) removed successfully.`;
|
|
14685
14783
|
}
|
|
14686
14784
|
}),
|
|
14687
14785
|
defineTool({
|
|
@@ -14695,13 +14793,13 @@ ${result}`;
|
|
|
14695
14793
|
},
|
|
14696
14794
|
async handler(a, ctx) {
|
|
14697
14795
|
ctx.info(`Moving IPv6 firewall raw rule: rule_id=${a.rule_id} to position ${a.destination}`);
|
|
14698
|
-
const
|
|
14699
|
-
if (
|
|
14796
|
+
const id = await resolveRuleId4(a.rule_id, ctx);
|
|
14797
|
+
if (!id)
|
|
14700
14798
|
return `IPv6 firewall raw rule with ID '${a.rule_id}' not found.`;
|
|
14701
|
-
const result = await executeMikrotikCommand(`/ipv6 firewall raw move ${
|
|
14799
|
+
const result = await executeMikrotikCommand(`/ipv6 firewall raw move ${id} destination=${a.destination}`, ctx);
|
|
14702
14800
|
if (looksLikeError(result))
|
|
14703
14801
|
return `Failed to move IPv6 firewall raw rule: ${result}`;
|
|
14704
|
-
return `IPv6 firewall raw rule
|
|
14802
|
+
return `IPv6 firewall raw rule '${a.rule_id}' (${id}) moved to position ${a.destination}.`;
|
|
14705
14803
|
}
|
|
14706
14804
|
}),
|
|
14707
14805
|
defineTool({
|
|
@@ -22047,7 +22145,7 @@ ${result}`;
|
|
|
22047
22145
|
name: "check_route_path",
|
|
22048
22146
|
title: "Check IPv4 Route Path",
|
|
22049
22147
|
annotations: READ,
|
|
22050
|
-
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
|
|
22148
|
+
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.",
|
|
22051
22149
|
inputSchema: {
|
|
22052
22150
|
destination: z88.string(),
|
|
22053
22151
|
routing_table: z88.string().optional().describe('Policy-routing table name (v7) or routing-mark (v6), e.g. "VPN"')
|
|
@@ -22062,8 +22160,8 @@ ${result}`;
|
|
|
22062
22160
|
|
|
22063
22161
|
${v6Result}`;
|
|
22064
22162
|
}
|
|
22065
|
-
const where = [
|
|
22066
|
-
if (table)
|
|
22163
|
+
const where = [`${a.destination} in dst-address`, "active=yes"];
|
|
22164
|
+
if (table && table !== "main")
|
|
22067
22165
|
where.push(`routing-table=${quoteValue(table)}`);
|
|
22068
22166
|
const v7Cmd = `/ip route print detail where ${where.join(" ")}`;
|
|
22069
22167
|
const v7Result = await executeMikrotikCommand(v7Cmd, ctx);
|
|
@@ -24856,13 +24954,15 @@ async function fetchLatestRelease() {
|
|
|
24856
24954
|
throw new Error(`GitHub API ${res.status}`);
|
|
24857
24955
|
const gh = await res.json();
|
|
24858
24956
|
const latestVersion = gh.tag_name.replace(/^v/, "");
|
|
24957
|
+
const cmp = compareVersions(latestVersion, VERSION);
|
|
24859
24958
|
const data = {
|
|
24860
24959
|
version: latestVersion,
|
|
24861
24960
|
name: gh.name || `v${latestVersion}`,
|
|
24862
24961
|
body: gh.body || "",
|
|
24863
24962
|
publishedAt: gh.published_at,
|
|
24864
24963
|
url: gh.html_url,
|
|
24865
|
-
isNewer:
|
|
24964
|
+
isNewer: cmp > 0,
|
|
24965
|
+
isAhead: cmp < 0,
|
|
24866
24966
|
currentVersion: VERSION
|
|
24867
24967
|
};
|
|
24868
24968
|
memoryCache = { data, fetchedAt: Date.now() };
|
|
@@ -24970,14 +25070,18 @@ var serverPulseTools = [
|
|
|
24970
25070
|
const sep = "\u2500".repeat(50);
|
|
24971
25071
|
sections.push(`SERVER PULSE \u2014 ${SERVER_TITLE} v${VERSION}`, sep, "Package: @usex/mikrotik-mcp", `Version: ${VERSION}`, `Uptime: ${formatUptime(uptime)}`, `Website: ${WEBSITE_URL}`);
|
|
24972
25072
|
if (result.release) {
|
|
24973
|
-
const freshness = assessFreshness(VERSION, result.release.version);
|
|
24974
|
-
const label = freshnessLabel(freshness);
|
|
24975
25073
|
const age = timeAgo(result.release.publishedAt);
|
|
24976
|
-
|
|
24977
|
-
|
|
24978
|
-
sections.push("", ">>> A newer version is available! <<<");
|
|
25074
|
+
if (result.release.isAhead) {
|
|
25075
|
+
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.");
|
|
24979
25076
|
} else {
|
|
24980
|
-
|
|
25077
|
+
const freshness = assessFreshness(VERSION, result.release.version);
|
|
25078
|
+
const label = freshnessLabel(freshness);
|
|
25079
|
+
sections.push("", `UPDATE STATUS: ${label}`, sep, `Current: v${VERSION}`, `Latest: v${result.release.version} (${result.release.name})`, `Published: ${age}`, `Freshness: ${freshness.toUpperCase()}`);
|
|
25080
|
+
if (result.release.isNewer) {
|
|
25081
|
+
sections.push("", ">>> A newer version is available! <<<");
|
|
25082
|
+
} else {
|
|
25083
|
+
sections.push("", "You are running the latest version.");
|
|
25084
|
+
}
|
|
24981
25085
|
}
|
|
24982
25086
|
if (result.release.isNewer) {
|
|
24983
25087
|
sections.push("", "UPGRADE", sep);
|