@usex/mikrotik-mcp 3.50.0 → 3.51.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.
Files changed (606) hide show
  1. package/dist/cli.js +434 -273
  2. package/dist/index.js +10 -141
  3. package/dist/shared/{cli-z6dyvbeb.js → cli-22wrpg71.js} +7935 -4199
  4. package/dist/shared/{cli-p080cnxa.js → cli-dq28zbns.js} +1 -1
  5. package/dist/shared/{library-5yx3nvtf.js → library-510csx1a.js} +1 -1
  6. package/dist/shared/{library-bpa5fz2e.js → library-mwcpnqqg.js} +7937 -4207
  7. package/dist/ui/observability.html +48 -47
  8. package/package.json +1 -1
  9. package/schemas/README.md +1 -1
  10. package/schemas/config.schema.json +29 -1
  11. package/schemas/tool-catalog.json +940 -47
  12. package/schemas/tools/aaa_mutate.json +10 -2
  13. package/schemas/tools/add_address_list_entry.json +5 -1
  14. package/schemas/tools/add_bfd_configuration.json +4 -1
  15. package/schemas/tools/add_bgp_connection.json +5 -1
  16. package/schemas/tools/add_bgp_template.json +4 -1
  17. package/schemas/tools/add_blackhole_route.json +4 -1
  18. package/schemas/tools/add_bridge_port.json +31 -6
  19. package/schemas/tools/add_bridge_vlan.json +4 -1
  20. package/schemas/tools/add_container_env.json +5 -1
  21. package/schemas/tools/add_container_mount.json +5 -1
  22. package/schemas/tools/add_default_route.json +5 -1
  23. package/schemas/tools/add_dhcp_client.json +7 -1
  24. package/schemas/tools/add_disk.json +3 -1
  25. package/schemas/tools/add_dns_regexp.json +6 -1
  26. package/schemas/tools/add_dns_static.json +4 -1
  27. package/schemas/tools/add_dot1x_client.json +5 -1
  28. package/schemas/tools/add_dot1x_server.json +13 -3
  29. package/schemas/tools/add_graphing_interface.json +3 -1
  30. package/schemas/tools/add_graphing_queue.json +3 -1
  31. package/schemas/tools/add_igmp_proxy_interface.json +5 -1
  32. package/schemas/tools/add_ip_address.json +5 -1
  33. package/schemas/tools/add_ipv6_address.json +5 -1
  34. package/schemas/tools/add_ipv6_address_list_entry.json +5 -1
  35. package/schemas/tools/add_ipv6_default_route.json +4 -1
  36. package/schemas/tools/add_ipv6_dhcp_binding.json +4 -1
  37. package/schemas/tools/add_ipv6_dhcp_client.json +15 -3
  38. package/schemas/tools/add_ipv6_dhcp_option.json +5 -1
  39. package/schemas/tools/add_ipv6_dhcp_relay.json +6 -1
  40. package/schemas/tools/add_ipv6_nd.json +9 -2
  41. package/schemas/tools/add_ipv6_nd_prefix.json +4 -1
  42. package/schemas/tools/add_ipv6_route.json +10 -2
  43. package/schemas/tools/add_logging_action.json +12 -2
  44. package/schemas/tools/add_logging_rule.json +4 -1
  45. package/schemas/tools/add_netwatch.json +10 -2
  46. package/schemas/tools/add_ospf_area.json +11 -2
  47. package/schemas/tools/add_ospf_area_range.json +5 -1
  48. package/schemas/tools/add_ospf_instance.json +5 -1
  49. package/schemas/tools/add_ospf_interface_template.json +19 -3
  50. package/schemas/tools/add_ovpn_server.json +17 -4
  51. package/schemas/tools/add_pimsm_instance.json +9 -2
  52. package/schemas/tools/add_pimsm_interface_template.json +5 -1
  53. package/schemas/tools/add_pimsm_rp.json +5 -1
  54. package/schemas/tools/add_radius_server.json +4 -1
  55. package/schemas/tools/add_rip_instance.json +4 -1
  56. package/schemas/tools/add_rip_interface_template.json +5 -1
  57. package/schemas/tools/add_rip_static_neighbor.json +4 -1
  58. package/schemas/tools/add_romon_port.json +4 -1
  59. package/schemas/tools/add_route.json +5 -1
  60. package/schemas/tools/add_routing_filter_num_list.json +4 -1
  61. package/schemas/tools/add_routing_filter_rule.json +5 -1
  62. package/schemas/tools/add_routing_id.json +4 -1
  63. package/schemas/tools/add_routing_rule.json +10 -2
  64. package/schemas/tools/add_routing_table.json +5 -1
  65. package/schemas/tools/add_rpki_session.json +6 -1
  66. package/schemas/tools/add_script.json +5 -1
  67. package/schemas/tools/add_switch_port_isolation.json +4 -1
  68. package/schemas/tools/add_switch_rule.json +10 -2
  69. package/schemas/tools/add_traffic_generator_port.json +5 -1
  70. package/schemas/tools/add_traffic_generator_stream.json +5 -1
  71. package/schemas/tools/add_traffic_monitor.json +17 -3
  72. package/schemas/tools/add_user.json +6 -1
  73. package/schemas/tools/add_user_group.json +4 -1
  74. package/schemas/tools/add_user_manager_limitation.json +3 -1
  75. package/schemas/tools/add_user_manager_profile.json +7 -2
  76. package/schemas/tools/add_user_manager_router.json +6 -1
  77. package/schemas/tools/add_user_manager_user.json +5 -1
  78. package/schemas/tools/add_wireguard_peer.json +6 -1
  79. package/schemas/tools/allow_device.json +3 -1
  80. package/schemas/tools/apply_plan.json +3 -1
  81. package/schemas/tools/apply_traffic_shaping.json +10 -2
  82. package/schemas/tools/assign_user_manager_profile.json +4 -1
  83. package/schemas/tools/audit_certificate_expiry.json +3 -1
  84. package/schemas/tools/audit_fleet.json +33 -0
  85. package/schemas/tools/audit_remediate.json +46 -0
  86. package/schemas/tools/backup_info.json +3 -1
  87. package/schemas/tools/bandwidth_test.json +15 -3
  88. package/schemas/tools/block_device.json +3 -1
  89. package/schemas/tools/build_guest_hotspot.json +7 -1
  90. package/schemas/tools/build_wireguard_mesh.json +4 -1
  91. package/schemas/tools/capture_config_snapshot.json +5 -1
  92. package/schemas/tools/change_password.json +4 -1
  93. package/schemas/tools/check_route_path.json +3 -1
  94. package/schemas/tools/config_check_drift.json +26 -0
  95. package/schemas/tools/config_promote_drift.json +16 -0
  96. package/schemas/tools/config_reconcile.json +14 -0
  97. package/schemas/tools/config_set_baseline.json +30 -0
  98. package/schemas/tools/correlate_events.json +29 -0
  99. package/schemas/tools/create_backup.json +4 -1
  100. package/schemas/tools/create_bridge.json +28 -5
  101. package/schemas/tools/create_certificate.json +6 -1
  102. package/schemas/tools/create_dhcp_network.json +4 -1
  103. package/schemas/tools/create_dhcp_pool.json +4 -1
  104. package/schemas/tools/create_dhcp_server.json +22 -4
  105. package/schemas/tools/create_eoip_tunnel.json +17 -3
  106. package/schemas/tools/create_export.json +17 -3
  107. package/schemas/tools/create_filter_rule.json +11 -2
  108. package/schemas/tools/create_gre_tunnel.json +9 -2
  109. package/schemas/tools/create_ip_pool.json +4 -1
  110. package/schemas/tools/create_ipip_tunnel.json +9 -2
  111. package/schemas/tools/create_ipsec_identity.json +21 -4
  112. package/schemas/tools/create_ipsec_peer.json +10 -2
  113. package/schemas/tools/create_ipsec_policy.json +15 -3
  114. package/schemas/tools/create_ipsec_profile.json +5 -1
  115. package/schemas/tools/create_ipsec_proposal.json +7 -1
  116. package/schemas/tools/create_ipv6_dhcp_server.json +7 -1
  117. package/schemas/tools/create_ipv6_filter_rule.json +11 -2
  118. package/schemas/tools/create_ipv6_mangle_rule.json +13 -2
  119. package/schemas/tools/create_ipv6_nat_rule.json +10 -2
  120. package/schemas/tools/create_ipv6_pool.json +5 -1
  121. package/schemas/tools/create_ipv6_raw_rule.json +19 -3
  122. package/schemas/tools/create_l2tp_client.json +12 -2
  123. package/schemas/tools/create_local_backup.json +6 -1
  124. package/schemas/tools/create_mangle_rule.json +13 -2
  125. package/schemas/tools/create_nat_rule.json +10 -2
  126. package/schemas/tools/create_ovpn_client.json +18 -4
  127. package/schemas/tools/create_ppp_profile.json +30 -6
  128. package/schemas/tools/create_ppp_secret.json +14 -2
  129. package/schemas/tools/create_pptp_client.json +7 -1
  130. package/schemas/tools/create_queue_tree.json +5 -1
  131. package/schemas/tools/create_queue_type.json +28 -5
  132. package/schemas/tools/create_scheduler.json +5 -1
  133. package/schemas/tools/create_simple_queue.json +5 -1
  134. package/schemas/tools/create_sstp_client.json +11 -2
  135. package/schemas/tools/create_vlan_interface.json +19 -3
  136. package/schemas/tools/create_vxlan_tunnel.json +17 -3
  137. package/schemas/tools/create_wireguard_interface.json +4 -1
  138. package/schemas/tools/create_wireless_interface.json +37 -6
  139. package/schemas/tools/create_wireless_security_profile.json +3 -1
  140. package/schemas/tools/delete_local_backup.json +3 -1
  141. package/schemas/tools/delete_s3_backup.json +3 -1
  142. package/schemas/tools/describe_tool.json +3 -1
  143. package/schemas/tools/design_network_segment.json +10 -1
  144. package/schemas/tools/diagnose.json +37 -0
  145. package/schemas/tools/diff_config_snapshots.json +6 -1
  146. package/schemas/tools/disable_address_list_entry.json +3 -1
  147. package/schemas/tools/disable_dns_static.json +3 -1
  148. package/schemas/tools/disable_filter_rule.json +3 -1
  149. package/schemas/tools/disable_interface.json +3 -1
  150. package/schemas/tools/disable_ip_service.json +3 -1
  151. package/schemas/tools/disable_ipv6_address_list_entry.json +3 -1
  152. package/schemas/tools/disable_ipv6_dhcp_relay.json +3 -1
  153. package/schemas/tools/disable_ipv6_filter_rule.json +3 -1
  154. package/schemas/tools/disable_ipv6_mangle_rule.json +3 -1
  155. package/schemas/tools/disable_ipv6_nat_rule.json +3 -1
  156. package/schemas/tools/disable_ipv6_raw_rule.json +3 -1
  157. package/schemas/tools/disable_ipv6_route.json +3 -1
  158. package/schemas/tools/disable_l2tp_client.json +3 -1
  159. package/schemas/tools/disable_mangle_rule.json +3 -1
  160. package/schemas/tools/disable_nat_rule.json +3 -1
  161. package/schemas/tools/disable_ovpn_client.json +3 -1
  162. package/schemas/tools/disable_ovpn_server.json +3 -1
  163. package/schemas/tools/disable_queue_tree.json +3 -1
  164. package/schemas/tools/disable_radius_server.json +3 -1
  165. package/schemas/tools/disable_route.json +3 -1
  166. package/schemas/tools/disable_scheduler.json +3 -1
  167. package/schemas/tools/disable_simple_queue.json +3 -1
  168. package/schemas/tools/disable_sstp_client.json +3 -1
  169. package/schemas/tools/disable_switch_rule.json +3 -1
  170. package/schemas/tools/disable_traffic_monitor.json +3 -1
  171. package/schemas/tools/disable_user.json +3 -1
  172. package/schemas/tools/disable_wireguard_interface.json +3 -1
  173. package/schemas/tools/disable_wireguard_peer.json +3 -1
  174. package/schemas/tools/disable_wireless_interface.json +3 -1
  175. package/schemas/tools/disconnect_ppp_active.json +3 -1
  176. package/schemas/tools/disconnect_user.json +3 -1
  177. package/schemas/tools/download_backup_from_s3.json +3 -1
  178. package/schemas/tools/download_file.json +3 -1
  179. package/schemas/tools/eject_disk.json +3 -1
  180. package/schemas/tools/enable_address_list_entry.json +3 -1
  181. package/schemas/tools/enable_dns_static.json +3 -1
  182. package/schemas/tools/enable_filter_rule.json +3 -1
  183. package/schemas/tools/enable_interface.json +3 -1
  184. package/schemas/tools/enable_ip_service.json +3 -1
  185. package/schemas/tools/enable_ipv6_address_list_entry.json +3 -1
  186. package/schemas/tools/enable_ipv6_dhcp_relay.json +3 -1
  187. package/schemas/tools/enable_ipv6_filter_rule.json +3 -1
  188. package/schemas/tools/enable_ipv6_mangle_rule.json +3 -1
  189. package/schemas/tools/enable_ipv6_nat_rule.json +3 -1
  190. package/schemas/tools/enable_ipv6_raw_rule.json +3 -1
  191. package/schemas/tools/enable_ipv6_route.json +3 -1
  192. package/schemas/tools/enable_l2tp_client.json +3 -1
  193. package/schemas/tools/enable_mangle_rule.json +3 -1
  194. package/schemas/tools/enable_nat_rule.json +3 -1
  195. package/schemas/tools/enable_ovpn_client.json +3 -1
  196. package/schemas/tools/enable_ovpn_server.json +3 -1
  197. package/schemas/tools/enable_queue_tree.json +3 -1
  198. package/schemas/tools/enable_radius_server.json +3 -1
  199. package/schemas/tools/enable_route.json +3 -1
  200. package/schemas/tools/enable_scheduler.json +3 -1
  201. package/schemas/tools/enable_simple_queue.json +3 -1
  202. package/schemas/tools/enable_sstp_client.json +3 -1
  203. package/schemas/tools/enable_switch_rule.json +3 -1
  204. package/schemas/tools/enable_traffic_monitor.json +3 -1
  205. package/schemas/tools/enable_user.json +3 -1
  206. package/schemas/tools/enable_wireguard_interface.json +3 -1
  207. package/schemas/tools/enable_wireguard_peer.json +3 -1
  208. package/schemas/tools/enable_wireless_interface.json +3 -1
  209. package/schemas/tools/expand_ip_pool.json +4 -1
  210. package/schemas/tools/export_logs.json +7 -2
  211. package/schemas/tools/export_section.json +5 -1
  212. package/schemas/tools/export_ssh_host_key.json +3 -1
  213. package/schemas/tools/find_tools.json +3 -1
  214. package/schemas/tools/firewall_audit.json +4 -1
  215. package/schemas/tools/firmware_check.json +32 -0
  216. package/schemas/tools/firmware_stage.json +17 -0
  217. package/schemas/tools/firmware_status.json +20 -0
  218. package/schemas/tools/firmware_upgrade.json +35 -0
  219. package/schemas/tools/flood_ping.json +4 -1
  220. package/schemas/tools/forecast_link_saturation.json +5 -1
  221. package/schemas/tools/format_disk.json +10 -2
  222. package/schemas/tools/forward_port.json +10 -2
  223. package/schemas/tools/generate_hotspot_vouchers.json +5 -1
  224. package/schemas/tools/get_aaa_section.json +3 -1
  225. package/schemas/tools/get_address_list_entry.json +3 -1
  226. package/schemas/tools/get_bgp_connection.json +3 -1
  227. package/schemas/tools/get_bridge.json +3 -1
  228. package/schemas/tools/get_certificate.json +3 -1
  229. package/schemas/tools/get_config_snapshot.json +4 -1
  230. package/schemas/tools/get_device_traffic.json +3 -1
  231. package/schemas/tools/get_dhcp_client.json +3 -1
  232. package/schemas/tools/get_dhcp_server.json +3 -1
  233. package/schemas/tools/get_disk.json +3 -1
  234. package/schemas/tools/get_dns_static.json +3 -1
  235. package/schemas/tools/get_dot1x_client.json +3 -1
  236. package/schemas/tools/get_dot1x_server.json +3 -1
  237. package/schemas/tools/get_eoip_tunnel.json +3 -1
  238. package/schemas/tools/get_filter_rule.json +3 -1
  239. package/schemas/tools/get_gre_tunnel.json +3 -1
  240. package/schemas/tools/get_interface.json +3 -1
  241. package/schemas/tools/get_ip_address.json +3 -1
  242. package/schemas/tools/get_ip_pool.json +3 -1
  243. package/schemas/tools/get_ip_service.json +3 -1
  244. package/schemas/tools/get_ipip_tunnel.json +3 -1
  245. package/schemas/tools/get_ipsec_peer.json +3 -1
  246. package/schemas/tools/get_ipsec_profile.json +3 -1
  247. package/schemas/tools/get_ipsec_proposal.json +3 -1
  248. package/schemas/tools/get_ipv6_address.json +3 -1
  249. package/schemas/tools/get_ipv6_address_list_entry.json +3 -1
  250. package/schemas/tools/get_ipv6_dhcp_client.json +3 -1
  251. package/schemas/tools/get_ipv6_dhcp_relay.json +3 -1
  252. package/schemas/tools/get_ipv6_dhcp_server.json +3 -1
  253. package/schemas/tools/get_ipv6_filter_rule.json +3 -1
  254. package/schemas/tools/get_ipv6_mangle_rule.json +3 -1
  255. package/schemas/tools/get_ipv6_nat_rule.json +3 -1
  256. package/schemas/tools/get_ipv6_nd.json +3 -1
  257. package/schemas/tools/get_ipv6_neighbor.json +3 -1
  258. package/schemas/tools/get_ipv6_pool.json +3 -1
  259. package/schemas/tools/get_ipv6_raw_rule.json +3 -1
  260. package/schemas/tools/get_ipv6_route.json +3 -1
  261. package/schemas/tools/get_l2tp_client.json +3 -1
  262. package/schemas/tools/get_local_backup.json +3 -1
  263. package/schemas/tools/get_logs.json +9 -2
  264. package/schemas/tools/get_logs_by_severity.json +10 -2
  265. package/schemas/tools/get_logs_by_topic.json +3 -1
  266. package/schemas/tools/get_mangle_rule.json +3 -1
  267. package/schemas/tools/get_nat_rule.json +3 -1
  268. package/schemas/tools/get_netwatch.json +3 -1
  269. package/schemas/tools/get_ovpn_client.json +3 -1
  270. package/schemas/tools/get_poe_monitor.json +3 -1
  271. package/schemas/tools/get_poe_settings.json +3 -1
  272. package/schemas/tools/get_port.json +3 -1
  273. package/schemas/tools/get_ppp_profile.json +3 -1
  274. package/schemas/tools/get_ppp_secret.json +3 -1
  275. package/schemas/tools/get_pptp_client.json +3 -1
  276. package/schemas/tools/get_queue_interface.json +3 -1
  277. package/schemas/tools/get_queue_tree.json +3 -1
  278. package/schemas/tools/get_queue_type.json +3 -1
  279. package/schemas/tools/get_radius_server.json +3 -1
  280. package/schemas/tools/get_route.json +3 -1
  281. package/schemas/tools/get_routing_id.json +3 -1
  282. package/schemas/tools/get_routing_rule.json +3 -1
  283. package/schemas/tools/get_routing_table.json +4 -1
  284. package/schemas/tools/get_routing_table_def.json +3 -1
  285. package/schemas/tools/get_scheduler.json +3 -1
  286. package/schemas/tools/get_simple_queue.json +3 -1
  287. package/schemas/tools/get_sstp_client.json +3 -1
  288. package/schemas/tools/get_switch.json +3 -1
  289. package/schemas/tools/get_switch_port.json +3 -1
  290. package/schemas/tools/get_switch_port_isolation.json +3 -1
  291. package/schemas/tools/get_switch_rule.json +3 -1
  292. package/schemas/tools/get_traffic_monitor.json +3 -1
  293. package/schemas/tools/get_user.json +3 -1
  294. package/schemas/tools/get_user_group.json +3 -1
  295. package/schemas/tools/get_user_manager_user.json +3 -1
  296. package/schemas/tools/get_vlan_interface.json +3 -1
  297. package/schemas/tools/get_vxlan_tunnel.json +3 -1
  298. package/schemas/tools/get_wireguard_interface.json +3 -1
  299. package/schemas/tools/get_wireguard_peer.json +3 -1
  300. package/schemas/tools/get_wireless_interface.json +3 -1
  301. package/schemas/tools/get_wireless_security_profile.json +3 -1
  302. package/schemas/tools/harden_firewall.json +6 -1
  303. package/schemas/tools/import_certificate.json +3 -1
  304. package/schemas/tools/import_configuration.json +5 -1
  305. package/schemas/tools/import_ssh_host_key.json +3 -1
  306. package/schemas/tools/invoke_tool.json +3 -1
  307. package/schemas/tools/ip_scan.json +3 -1
  308. package/schemas/tools/list_address_lists.json +3 -1
  309. package/schemas/tools/list_backups.json +3 -1
  310. package/schemas/tools/list_bfd_sessions.json +3 -1
  311. package/schemas/tools/list_bgp_sessions.json +3 -1
  312. package/schemas/tools/list_compliance_checks.json +24 -0
  313. package/schemas/tools/list_config_snapshots.json +3 -1
  314. package/schemas/tools/list_connected_devices.json +4 -1
  315. package/schemas/tools/list_containers.json +3 -1
  316. package/schemas/tools/list_dhcp_servers.json +4 -1
  317. package/schemas/tools/list_disks.json +3 -1
  318. package/schemas/tools/list_dns_static.json +4 -1
  319. package/schemas/tools/list_dot1x_clients.json +3 -1
  320. package/schemas/tools/list_dot1x_servers.json +3 -1
  321. package/schemas/tools/list_filter_rules.json +5 -1
  322. package/schemas/tools/list_graphing.json +8 -2
  323. package/schemas/tools/list_interfaces.json +4 -1
  324. package/schemas/tools/list_ip_addresses.json +4 -1
  325. package/schemas/tools/list_ip_pools.json +3 -1
  326. package/schemas/tools/list_ipv6_address_lists.json +3 -1
  327. package/schemas/tools/list_ipv6_addresses.json +5 -1
  328. package/schemas/tools/list_ipv6_dhcp_bindings.json +3 -1
  329. package/schemas/tools/list_ipv6_dhcp_clients.json +4 -1
  330. package/schemas/tools/list_ipv6_dhcp_relays.json +3 -1
  331. package/schemas/tools/list_ipv6_dhcp_servers.json +4 -1
  332. package/schemas/tools/list_ipv6_filter_rules.json +5 -1
  333. package/schemas/tools/list_ipv6_mangle_rules.json +5 -1
  334. package/schemas/tools/list_ipv6_nat_rules.json +5 -1
  335. package/schemas/tools/list_ipv6_nd.json +3 -1
  336. package/schemas/tools/list_ipv6_nd_prefixes.json +3 -1
  337. package/schemas/tools/list_ipv6_neighbors.json +3 -1
  338. package/schemas/tools/list_ipv6_raw_rules.json +5 -1
  339. package/schemas/tools/list_ipv6_routes.json +5 -1
  340. package/schemas/tools/list_mangle_rules.json +5 -1
  341. package/schemas/tools/list_nat_rules.json +4 -1
  342. package/schemas/tools/list_ppp_secrets.json +8 -1
  343. package/schemas/tools/list_queue_trees.json +4 -1
  344. package/schemas/tools/list_routes.json +6 -1
  345. package/schemas/tools/list_routing_nexthops.json +3 -1
  346. package/schemas/tools/list_routing_rules.json +3 -1
  347. package/schemas/tools/list_s3_backups.json +3 -1
  348. package/schemas/tools/list_simple_queues.json +4 -1
  349. package/schemas/tools/list_switch_rules.json +3 -1
  350. package/schemas/tools/list_traffic_monitors.json +3 -1
  351. package/schemas/tools/list_user_manager_sessions.json +3 -1
  352. package/schemas/tools/list_user_ssh_keys.json +3 -1
  353. package/schemas/tools/list_users.json +4 -1
  354. package/schemas/tools/list_vlan_interfaces.json +3 -1
  355. package/schemas/tools/list_wireguard_interfaces.json +4 -1
  356. package/schemas/tools/list_wireguard_peers.json +3 -1
  357. package/schemas/tools/list_wireless_interfaces.json +4 -1
  358. package/schemas/tools/make_device_static.json +3 -1
  359. package/schemas/tools/memory_add_observations.json +32 -0
  360. package/schemas/tools/memory_create_entities.json +36 -0
  361. package/schemas/tools/memory_create_relations.json +33 -0
  362. package/schemas/tools/memory_delete_entities.json +17 -0
  363. package/schemas/tools/memory_delete_observations.json +32 -0
  364. package/schemas/tools/memory_delete_relations.json +33 -0
  365. package/schemas/tools/memory_open_nodes.json +17 -0
  366. package/schemas/tools/memory_read_graph.json +7 -0
  367. package/schemas/tools/memory_search_nodes.json +19 -0
  368. package/schemas/tools/mirror_traffic_to_capture.json +12 -2
  369. package/schemas/tools/monitor_logs.json +3 -1
  370. package/schemas/tools/move_filter_rule.json +4 -1
  371. package/schemas/tools/move_ipv6_filter_rule.json +4 -1
  372. package/schemas/tools/move_ipv6_mangle_rule.json +4 -1
  373. package/schemas/tools/move_ipv6_nat_rule.json +4 -1
  374. package/schemas/tools/move_ipv6_raw_rule.json +4 -1
  375. package/schemas/tools/move_mangle_rule.json +4 -1
  376. package/schemas/tools/move_nat_rule.json +4 -1
  377. package/schemas/tools/onboard_wireguard_user.json +8 -1
  378. package/schemas/tools/packet_capture_status.json +3 -1
  379. package/schemas/tools/ping.json +4 -1
  380. package/schemas/tools/profile_cpu.json +3 -1
  381. package/schemas/tools/reboot_system.json +3 -1
  382. package/schemas/tools/release_ipv6_dhcp_client.json +3 -1
  383. package/schemas/tools/remove_address_list_entry.json +3 -1
  384. package/schemas/tools/remove_bfd_configuration.json +3 -1
  385. package/schemas/tools/remove_bgp_connection.json +3 -1
  386. package/schemas/tools/remove_bgp_template.json +3 -1
  387. package/schemas/tools/remove_bridge.json +3 -1
  388. package/schemas/tools/remove_bridge_port.json +3 -1
  389. package/schemas/tools/remove_certificate.json +3 -1
  390. package/schemas/tools/remove_config_snapshot.json +3 -1
  391. package/schemas/tools/remove_container_env.json +4 -1
  392. package/schemas/tools/remove_container_mount.json +3 -1
  393. package/schemas/tools/remove_device_lease.json +3 -1
  394. package/schemas/tools/remove_dhcp_client.json +3 -1
  395. package/schemas/tools/remove_dhcp_server.json +3 -1
  396. package/schemas/tools/remove_disk.json +3 -1
  397. package/schemas/tools/remove_dns_static.json +3 -1
  398. package/schemas/tools/remove_dot1x_client.json +3 -1
  399. package/schemas/tools/remove_dot1x_server.json +3 -1
  400. package/schemas/tools/remove_eoip_tunnel.json +3 -1
  401. package/schemas/tools/remove_file.json +3 -1
  402. package/schemas/tools/remove_filter_rule.json +3 -1
  403. package/schemas/tools/remove_graphing.json +9 -2
  404. package/schemas/tools/remove_gre_tunnel.json +3 -1
  405. package/schemas/tools/remove_igmp_proxy_interface.json +3 -1
  406. package/schemas/tools/remove_ip_address.json +3 -1
  407. package/schemas/tools/remove_ip_pool.json +3 -1
  408. package/schemas/tools/remove_ipip_tunnel.json +3 -1
  409. package/schemas/tools/remove_ipsec_identity.json +3 -1
  410. package/schemas/tools/remove_ipsec_peer.json +3 -1
  411. package/schemas/tools/remove_ipsec_policy.json +3 -1
  412. package/schemas/tools/remove_ipsec_profile.json +3 -1
  413. package/schemas/tools/remove_ipsec_proposal.json +3 -1
  414. package/schemas/tools/remove_ipv6_address.json +3 -1
  415. package/schemas/tools/remove_ipv6_address_list_entry.json +3 -1
  416. package/schemas/tools/remove_ipv6_dhcp_binding.json +3 -1
  417. package/schemas/tools/remove_ipv6_dhcp_client.json +3 -1
  418. package/schemas/tools/remove_ipv6_dhcp_option.json +3 -1
  419. package/schemas/tools/remove_ipv6_dhcp_relay.json +3 -1
  420. package/schemas/tools/remove_ipv6_dhcp_server.json +3 -1
  421. package/schemas/tools/remove_ipv6_filter_rule.json +3 -1
  422. package/schemas/tools/remove_ipv6_mangle_rule.json +3 -1
  423. package/schemas/tools/remove_ipv6_nat_rule.json +3 -1
  424. package/schemas/tools/remove_ipv6_nd.json +3 -1
  425. package/schemas/tools/remove_ipv6_nd_prefix.json +3 -1
  426. package/schemas/tools/remove_ipv6_neighbor.json +3 -1
  427. package/schemas/tools/remove_ipv6_pool.json +3 -1
  428. package/schemas/tools/remove_ipv6_raw_rule.json +3 -1
  429. package/schemas/tools/remove_ipv6_route.json +3 -1
  430. package/schemas/tools/remove_l2tp_client.json +3 -1
  431. package/schemas/tools/remove_logging_action.json +3 -1
  432. package/schemas/tools/remove_logging_rule.json +3 -1
  433. package/schemas/tools/remove_mangle_rule.json +3 -1
  434. package/schemas/tools/remove_nat_rule.json +3 -1
  435. package/schemas/tools/remove_netwatch.json +3 -1
  436. package/schemas/tools/remove_ospf_area.json +3 -1
  437. package/schemas/tools/remove_ospf_area_range.json +3 -1
  438. package/schemas/tools/remove_ospf_instance.json +3 -1
  439. package/schemas/tools/remove_ospf_interface_template.json +3 -1
  440. package/schemas/tools/remove_ovpn_client.json +3 -1
  441. package/schemas/tools/remove_ovpn_server.json +3 -1
  442. package/schemas/tools/remove_pimsm_instance.json +3 -1
  443. package/schemas/tools/remove_pimsm_interface_template.json +3 -1
  444. package/schemas/tools/remove_pimsm_rp.json +3 -1
  445. package/schemas/tools/remove_ppp_profile.json +3 -1
  446. package/schemas/tools/remove_ppp_secret.json +3 -1
  447. package/schemas/tools/remove_pptp_client.json +3 -1
  448. package/schemas/tools/remove_queue_tree.json +3 -1
  449. package/schemas/tools/remove_queue_type.json +3 -1
  450. package/schemas/tools/remove_radius_server.json +3 -1
  451. package/schemas/tools/remove_rip_instance.json +3 -1
  452. package/schemas/tools/remove_rip_interface_template.json +3 -1
  453. package/schemas/tools/remove_rip_static_neighbor.json +3 -1
  454. package/schemas/tools/remove_romon_port.json +3 -1
  455. package/schemas/tools/remove_route.json +3 -1
  456. package/schemas/tools/remove_routing_filter_num_list.json +3 -1
  457. package/schemas/tools/remove_routing_filter_rule.json +3 -1
  458. package/schemas/tools/remove_routing_id.json +3 -1
  459. package/schemas/tools/remove_routing_rule.json +3 -1
  460. package/schemas/tools/remove_routing_table.json +3 -1
  461. package/schemas/tools/remove_rpki_session.json +3 -1
  462. package/schemas/tools/remove_scheduler.json +3 -1
  463. package/schemas/tools/remove_script.json +3 -1
  464. package/schemas/tools/remove_simple_queue.json +3 -1
  465. package/schemas/tools/remove_sstp_client.json +3 -1
  466. package/schemas/tools/remove_switch_port_isolation.json +3 -1
  467. package/schemas/tools/remove_switch_rule.json +3 -1
  468. package/schemas/tools/remove_threat_feed.json +3 -1
  469. package/schemas/tools/remove_time_policy.json +3 -1
  470. package/schemas/tools/remove_traffic_generator_port.json +3 -1
  471. package/schemas/tools/remove_traffic_generator_stream.json +3 -1
  472. package/schemas/tools/remove_traffic_monitor.json +3 -1
  473. package/schemas/tools/remove_user.json +3 -1
  474. package/schemas/tools/remove_user_group.json +3 -1
  475. package/schemas/tools/remove_user_manager_limitation.json +3 -1
  476. package/schemas/tools/remove_user_manager_profile.json +3 -1
  477. package/schemas/tools/remove_user_manager_router.json +3 -1
  478. package/schemas/tools/remove_user_manager_user.json +3 -1
  479. package/schemas/tools/remove_user_ssh_key.json +3 -1
  480. package/schemas/tools/remove_vlan_interface.json +3 -1
  481. package/schemas/tools/remove_vxlan_tunnel.json +3 -1
  482. package/schemas/tools/remove_wireguard_interface.json +3 -1
  483. package/schemas/tools/remove_wireguard_peer.json +3 -1
  484. package/schemas/tools/remove_wireless_access_list_entry.json +3 -1
  485. package/schemas/tools/remove_wireless_interface.json +3 -1
  486. package/schemas/tools/remove_wireless_security_profile.json +3 -1
  487. package/schemas/tools/rename_local_backup.json +4 -1
  488. package/schemas/tools/renew_dhcp_client.json +4 -1
  489. package/schemas/tools/renew_ipv6_dhcp_client.json +3 -1
  490. package/schemas/tools/request_letsencrypt_certificate.json +3 -1
  491. package/schemas/tools/reset_configuration.json +3 -1
  492. package/schemas/tools/resolve_dns.json +3 -1
  493. package/schemas/tools/restore_backup.json +3 -1
  494. package/schemas/tools/restore_local_backup.json +4 -1
  495. package/schemas/tools/revoke_wireguard_user.json +3 -1
  496. package/schemas/tools/run_compliance_audit.json +46 -0
  497. package/schemas/tools/run_failover_drill.json +11 -2
  498. package/schemas/tools/run_routeros_command.json +3 -1
  499. package/schemas/tools/run_script.json +3 -1
  500. package/schemas/tools/s3_backup_info.json +3 -1
  501. package/schemas/tools/save_sniffer.json +3 -1
  502. package/schemas/tools/scan_wireless_networks.json +4 -1
  503. package/schemas/tools/search_logs.json +4 -1
  504. package/schemas/tools/send_sms.json +5 -1
  505. package/schemas/tools/set_aaa_settings.json +8 -2
  506. package/schemas/tools/set_bfd_configuration_enabled.json +4 -1
  507. package/schemas/tools/set_bgp_connection_enabled.json +4 -1
  508. package/schemas/tools/set_device_ip.json +4 -1
  509. package/schemas/tools/set_device_label.json +4 -1
  510. package/schemas/tools/set_device_limits.json +3 -1
  511. package/schemas/tools/set_dhcp_client.json +3 -1
  512. package/schemas/tools/set_disk.json +3 -1
  513. package/schemas/tools/set_dns_servers.json +6 -1
  514. package/schemas/tools/set_igmp_proxy_interface_enabled.json +4 -1
  515. package/schemas/tools/set_ip_cloud_advanced.json +3 -1
  516. package/schemas/tools/set_ip_service.json +3 -1
  517. package/schemas/tools/set_l2tp_server.json +9 -2
  518. package/schemas/tools/set_leds_settings.json +6 -1
  519. package/schemas/tools/set_neighbor_discovery_settings.json +5 -1
  520. package/schemas/tools/set_ntp_client.json +6 -1
  521. package/schemas/tools/set_ovpn_server.json +12 -3
  522. package/schemas/tools/set_port.json +13 -3
  523. package/schemas/tools/set_routing_filter_rule_enabled.json +4 -1
  524. package/schemas/tools/set_routing_id_enabled.json +4 -1
  525. package/schemas/tools/set_routing_rule_enabled.json +4 -1
  526. package/schemas/tools/set_routing_table_enabled.json +4 -1
  527. package/schemas/tools/set_rpki_session_enabled.json +4 -1
  528. package/schemas/tools/set_ssh_settings.json +10 -2
  529. package/schemas/tools/set_sstp_server.json +4 -1
  530. package/schemas/tools/set_system_identity.json +3 -1
  531. package/schemas/tools/set_time_policy.json +7 -1
  532. package/schemas/tools/set_user_ssh_keys.json +4 -1
  533. package/schemas/tools/set_wireless_security_profile.json +4 -1
  534. package/schemas/tools/setup_port_knock.json +7 -1
  535. package/schemas/tools/setup_wan_failover.json +11 -2
  536. package/schemas/tools/setup_wan_loadbalance.json +10 -2
  537. package/schemas/tools/share_disk.json +3 -1
  538. package/schemas/tools/shutdown_system.json +3 -1
  539. package/schemas/tools/sign_certificate.json +3 -1
  540. package/schemas/tools/speed_test.json +10 -2
  541. package/schemas/tools/start_packet_capture.json +4 -1
  542. package/schemas/tools/subscribe_threat_feed.json +8 -1
  543. package/schemas/tools/suggest_fix.json +33 -0
  544. package/schemas/tools/test_dns_query.json +4 -1
  545. package/schemas/tools/test_ssh_from_device.json +6 -1
  546. package/schemas/tools/test_ssh_to_host.json +6 -1
  547. package/schemas/tools/trace_path.json +25 -0
  548. package/schemas/tools/traceroute.json +4 -1
  549. package/schemas/tools/tune_wifi_channel.json +5 -1
  550. package/schemas/tools/update_bfd_configuration.json +3 -1
  551. package/schemas/tools/update_bgp_connection.json +3 -1
  552. package/schemas/tools/update_bridge.json +26 -5
  553. package/schemas/tools/update_dns_static.json +3 -1
  554. package/schemas/tools/update_dot1x_client.json +3 -1
  555. package/schemas/tools/update_dot1x_server.json +12 -3
  556. package/schemas/tools/update_filter_rule.json +3 -1
  557. package/schemas/tools/update_igmp_proxy_interface.json +3 -1
  558. package/schemas/tools/update_ip_pool.json +3 -1
  559. package/schemas/tools/update_ipsec_peer.json +8 -2
  560. package/schemas/tools/update_ipsec_profile.json +3 -1
  561. package/schemas/tools/update_ipsec_proposal.json +3 -1
  562. package/schemas/tools/update_ipv6_filter_rule.json +3 -1
  563. package/schemas/tools/update_ipv6_mangle_rule.json +3 -1
  564. package/schemas/tools/update_ipv6_nat_rule.json +3 -1
  565. package/schemas/tools/update_ipv6_nd.json +8 -2
  566. package/schemas/tools/update_ipv6_pool.json +3 -1
  567. package/schemas/tools/update_ipv6_raw_rule.json +3 -1
  568. package/schemas/tools/update_ipv6_settings.json +15 -3
  569. package/schemas/tools/update_mac_ping.json +3 -1
  570. package/schemas/tools/update_mac_server.json +3 -1
  571. package/schemas/tools/update_mac_winbox.json +3 -1
  572. package/schemas/tools/update_mangle_rule.json +3 -1
  573. package/schemas/tools/update_nat_rule.json +3 -1
  574. package/schemas/tools/update_ospf_instance.json +3 -1
  575. package/schemas/tools/update_ovpn_server.json +15 -4
  576. package/schemas/tools/update_ppp_profile.json +30 -6
  577. package/schemas/tools/update_ppp_secret.json +11 -2
  578. package/schemas/tools/update_queue_interface.json +4 -1
  579. package/schemas/tools/update_queue_tree.json +3 -1
  580. package/schemas/tools/update_queue_type.json +3 -1
  581. package/schemas/tools/update_radius_server.json +7 -2
  582. package/schemas/tools/update_rip_instance.json +3 -1
  583. package/schemas/tools/update_route.json +3 -1
  584. package/schemas/tools/update_routing_filter_rule.json +3 -1
  585. package/schemas/tools/update_routing_id.json +3 -1
  586. package/schemas/tools/update_routing_rule.json +9 -2
  587. package/schemas/tools/update_routing_settings.json +10 -2
  588. package/schemas/tools/update_routing_table.json +3 -1
  589. package/schemas/tools/update_rpki_session.json +3 -1
  590. package/schemas/tools/update_simple_queue.json +3 -1
  591. package/schemas/tools/update_sniffer_settings.json +9 -2
  592. package/schemas/tools/update_switch.json +3 -1
  593. package/schemas/tools/update_switch_port.json +14 -3
  594. package/schemas/tools/update_switch_port_isolation.json +3 -1
  595. package/schemas/tools/update_switch_rule.json +8 -2
  596. package/schemas/tools/update_traffic_monitor.json +12 -3
  597. package/schemas/tools/update_user.json +3 -1
  598. package/schemas/tools/update_user_group.json +3 -1
  599. package/schemas/tools/update_user_manager_user.json +3 -1
  600. package/schemas/tools/update_vlan_interface.json +14 -3
  601. package/schemas/tools/update_wireguard_interface.json +3 -1
  602. package/schemas/tools/update_wireguard_peer.json +3 -1
  603. package/schemas/tools/update_wireless_interface.json +36 -6
  604. package/schemas/tools/upload_backup_to_s3.json +3 -1
  605. package/schemas/tools/upload_file.json +4 -1
  606. package/schemas/tools/wake_on_lan.json +3 -1
package/dist/cli.js CHANGED
@@ -3,11 +3,13 @@
3
3
  import {
4
4
  AAA_ENTITIES,
5
5
  ALWAYS_ON_MODULES,
6
+ Cmd,
6
7
  DEFAULT_CONFIG_HISTORY_DIR,
7
8
  DEFAULT_SNAPSHOT_DB,
8
9
  DEFAULT_TZSP_PORT,
9
10
  DeviceConfigSchema,
10
11
  MikrotikConfigSchema,
12
+ PROJECT_ROOT,
11
13
  PROMPTS_DIR,
12
14
  REDACTED,
13
15
  ToolFilterSchema,
@@ -16,6 +18,8 @@ import {
16
18
  addAaaEntity,
17
19
  allToolModules,
18
20
  allowDevice,
21
+ analyzeDrift,
22
+ attributeChanges,
19
23
  backupDir,
20
24
  blockDevice,
21
25
  buildChangePlan,
@@ -53,6 +57,7 @@ import {
53
57
  makeDeviceStatic,
54
58
  moduleCatalog,
55
59
  normalizeExport,
60
+ openMemoryStore,
56
61
  openSnapshotStore,
57
62
  parseKeyValues,
58
63
  parseLeadingNumber,
@@ -70,6 +75,7 @@ import {
70
75
  removeDeviceLease,
71
76
  renameBackup,
72
77
  renderPlan,
78
+ resetMemoryStore,
73
79
  resetRadiusCounters,
74
80
  resolveDeviceName,
75
81
  restoreLocalBackup,
@@ -89,15 +95,15 @@ import {
89
95
  toggleAaaEntity,
90
96
  updateAaaEntity,
91
97
  writeBackup
92
- } from "./shared/cli-z6dyvbeb.js";
98
+ } from "./shared/cli-22wrpg71.js";
93
99
 
94
100
  // src/cli.ts
95
101
  import { existsSync as existsSync2 } from "fs";
96
102
 
97
103
  // src/observability/dashboard.ts
98
- import { readFileSync as readFileSync2 } from "fs";
104
+ import { readFileSync as readFileSync3 } from "fs";
99
105
  import { homedir, networkInterfaces } from "os";
100
- import { dirname as dirname4, join as join2 } from "path";
106
+ import { dirname as dirname4, join as join3 } from "path";
101
107
  var {serve } = globalThis.Bun;
102
108
  import { z } from "zod";
103
109
 
@@ -1140,149 +1146,297 @@ function computeStats(events, opts) {
1140
1146
  windowMs: span
1141
1147
  };
1142
1148
  }
1143
- // package.json
1144
- var package_default = {
1145
- name: "@usex/mikrotik-mcp",
1146
- version: "3.50.0",
1147
- description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
1148
- keywords: [
1149
- "ai",
1150
- "bun",
1151
- "claude",
1152
- "mcp",
1153
- "mikrotik",
1154
- "mikrotik mcp"
1155
- ],
1156
- homepage: "https://mikrotik-mcp.usestrict.dev",
1157
- bugs: {
1158
- url: "https://github.com/ali-master/mikrotik-mcp/issues"
1159
- },
1160
- license: "MIT",
1161
- author: {
1162
- name: "Ali Torki",
1163
- email: "ali_4286@live.com",
1164
- url: "https://github.com/ali-master"
1165
- },
1166
- repository: {
1167
- type: "git",
1168
- url: "https://github.com/ali-master/mikrotik-mcp.git"
1169
- },
1170
- bin: {
1171
- "mikrotik-mcp": "dist/cli.js"
1172
- },
1173
- files: [
1174
- "dist",
1175
- "prompts",
1176
- "schemas"
1177
- ],
1178
- type: "module",
1179
- main: "dist/index.js",
1180
- module: "dist/index.js",
1181
- types: "dist/index.d.ts",
1182
- exports: {
1183
- ".": {
1184
- types: "./dist/index.d.ts",
1185
- import: "./dist/index.js"
1186
- }
1187
- },
1188
- publishConfig: {
1189
- access: "public"
1190
- },
1191
- scripts: {
1192
- build: "bunup && chmod +x dist/cli.js && bun run build:ui",
1193
- "build:ui": "bun run scripts/build-ui.ts",
1194
- dev: "bunup --watch",
1195
- prepack: "bunup && chmod +x dist/cli.js && bun run build:ui",
1196
- "auth-check": "bun run src/cli.ts auth-check",
1197
- "gen:schemas": "bun run scripts/gen-schemas.ts",
1198
- "gen:docs": "bun run scripts/gen-tool-docs.ts",
1199
- gen: "bun run gen:schemas && bun run gen:docs",
1200
- start: "bun run src/cli.ts serve",
1201
- discover: "bun run scripts/discover-macs.ts",
1202
- inspect: "bunx @modelcontextprotocol/inspector bun run src/cli.ts serve",
1203
- "inspect:built": "bun run build && bunx @modelcontextprotocol/inspector bun dist/cli.js serve",
1204
- "inspect:config": "bunx @modelcontextprotocol/inspector --config mcp-inspector.config.json --server mikrotik",
1205
- "inspect:cli": "bunx @modelcontextprotocol/inspector --cli bun run src/cli.ts serve --method tools/list",
1206
- "inspect:config:local": "bunx @modelcontextprotocol/inspector --config /var/open-source/mikrotik-mcp/mcp-inspector.config.local.json --server mikrotik",
1207
- test: "vp test run",
1208
- "test:watch": "vp test",
1209
- release: "release-it",
1210
- "test:types": "tsc --noEmit",
1211
- check: "vp check",
1212
- lint: "vp lint",
1213
- "lint:fix": "vp lint --fix",
1214
- format: "vp fmt --check",
1215
- "format:fix": "vp fmt --write",
1216
- prepare: "vp config"
1217
- },
1218
- dependencies: {
1219
- "@modelcontextprotocol/ext-apps": "^1.7.4",
1220
- "@modelcontextprotocol/sdk": "^1.29.0",
1221
- "@tikoci/centrs": "^0.1.0",
1222
- figlet: "^1.11.0",
1223
- "gradient-string": "^3.0.0",
1224
- "ipaddr.js": "^2.4.0",
1225
- ssh2: "^1.17.0",
1226
- zod: "^4.4.3"
1227
- },
1228
- devDependencies: {
1229
- "@resvg/resvg-js": "^2.6.2",
1230
- "@tailwindcss/vite": "^4.3.2",
1231
- "@types/bun": "latest",
1232
- "@types/figlet": "^1.7.0",
1233
- "@types/node": "^26.1.0",
1234
- "@types/react": "^19.2.17",
1235
- "@types/react-dom": "^19.2.3",
1236
- "@types/ssh2": "^1.15.5",
1237
- "@types/update-notifier": "^6.0.8",
1238
- bunup: "^0.16.32",
1239
- "class-variance-authority": "^0.7.1",
1240
- clsx: "^2.1.1",
1241
- gsap: "^3.15.0",
1242
- prettier: "^3.9.4",
1243
- react: "^19.2.7",
1244
- "react-dom": "^19.2.7",
1245
- recharts: "^3.9.1",
1246
- "release-it": "^20.2.1",
1247
- "tailwind-merge": "^3.6.0",
1248
- tailwindcss: "^4.3.2",
1249
- vite: "npm:@voidzero-dev/vite-plus-core@latest",
1250
- "vite-plus": "^0.2.2"
1251
- },
1252
- peerDependencies: {
1253
- typescript: "^6.0.3"
1254
- },
1255
- overrides: {
1256
- vite: "npm:@voidzero-dev/vite-plus-core@latest",
1257
- vitest: "4.1.9"
1258
- },
1259
- engines: {
1260
- bun: ">=1.3.0"
1261
- },
1262
- packageManager: "bun@1.3.14",
1263
- changelog: {
1264
- labels: {
1265
- feature: "Features",
1266
- bug: "Bug fixes",
1267
- enhancement: "Enhancements",
1268
- docs: "Docs",
1269
- dependencies: "Dependencies",
1270
- "type: code style": "Code style tweaks",
1271
- "status: blocked": "Breaking changes",
1272
- "breaking change": "Breaking changes"
1273
- }
1274
- },
1275
- logo: "https://raw.githubusercontent.com/ali-master/mikrotik-mcp/master/assets/logo.svg",
1276
- logoIcon: "https://raw.githubusercontent.com/ali-master/mikrotik-mcp/master/assets/logo-icon.svg"
1277
- };
1278
1149
 
1279
1150
  // src/version.ts
1280
- var VERSION = package_default.version;
1281
- var WEBSITE_URL = package_default.homepage;
1282
- var LOGO_URL = package_default.logoIcon;
1283
- var SERVER_TITLE = package_default.name;
1284
- var SERVER_DESCRIPTION = package_default.description;
1151
+ import { readFileSync as readFileSync2 } from "fs";
1152
+ import { join as join2 } from "path";
1153
+ var pkg = JSON.parse(readFileSync2(join2(PROJECT_ROOT, "package.json"), "utf-8"));
1154
+ var VERSION = pkg.version ?? "0.0.0";
1155
+ var WEBSITE_URL = pkg.homepage ?? "";
1156
+ var LOGO_URL = pkg.logoIcon ?? "";
1157
+ var SERVER_TITLE = pkg.name ?? "mcp-mikrotik";
1158
+ var SERVER_DESCRIPTION = pkg.description ?? "";
1285
1159
  var SERVER_NAME = "mcp-mikrotik";
1160
+ var PKG_META = pkg;
1161
+
1162
+ // src/observability/drift-routes.ts
1163
+ var storePromise = null;
1164
+ function getStore() {
1165
+ if (!storePromise)
1166
+ storePromise = openSnapshotStore(DEFAULT_SNAPSHOT_DB);
1167
+ return storePromise;
1168
+ }
1169
+ var JSON_HEADERS = { "content-type": "application/json; charset=utf-8" };
1170
+ function json(body, status = 200) {
1171
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
1172
+ }
1173
+ async function bodyJson(req) {
1174
+ return await req.json();
1175
+ }
1176
+ async function driftRoutes(req, url) {
1177
+ const p = url.pathname;
1178
+ if (!p.startsWith("/api/drift"))
1179
+ return null;
1180
+ const store = await getStore();
1181
+ if (p === "/api/drift/status" && req.method === "GET") {
1182
+ const cfg = getConfig();
1183
+ const deviceNames = Object.keys(cfg.devices);
1184
+ const devices = deviceNames.map((name) => {
1185
+ const baseline = store.getBaseline(name);
1186
+ if (!baseline) {
1187
+ return { device: name, status: "no-baseline", baseline: null };
1188
+ }
1189
+ const baselineSnap = store.get(baseline.snapshotId);
1190
+ if (!baselineSnap) {
1191
+ return {
1192
+ device: name,
1193
+ status: "unknown",
1194
+ baseline,
1195
+ error: "baseline snapshot deleted"
1196
+ };
1197
+ }
1198
+ const latest = store.latest(name);
1199
+ if (!latest || latest.id === baselineSnap.id) {
1200
+ return { device: name, status: "in-sync", baseline };
1201
+ }
1202
+ const identical = latest.sha === baselineSnap.sha;
1203
+ return {
1204
+ device: name,
1205
+ status: identical ? "in-sync" : "drifted",
1206
+ baseline,
1207
+ latestSnapshotId: latest.id,
1208
+ latestSnapshotTs: latest.ts
1209
+ };
1210
+ });
1211
+ return json({ devices });
1212
+ }
1213
+ if (p === "/api/drift/baselines" && req.method === "GET") {
1214
+ const baselines = store.listBaselines();
1215
+ const enriched = baselines.map((b) => {
1216
+ const snap = store.get(b.snapshotId);
1217
+ return {
1218
+ ...b,
1219
+ snapshot: snap ? { lines: snap.lines, bytes: snap.bytes, sha: snap.sha, rosVersion: snap.rosVersion } : null
1220
+ };
1221
+ });
1222
+ return json({ baselines: enriched });
1223
+ }
1224
+ const baselineMatch = p.match(/^\/api\/drift\/baseline\/(.+)$/);
1225
+ if (baselineMatch && req.method === "GET") {
1226
+ const device = decodeURIComponent(baselineMatch[1]);
1227
+ const baseline = store.getBaseline(device);
1228
+ if (!baseline)
1229
+ return json({ error: "no baseline set" }, 404);
1230
+ const snap = store.get(baseline.snapshotId);
1231
+ return json({
1232
+ ...baseline,
1233
+ snapshot: snap ? { lines: snap.lines, bytes: snap.bytes, sha: snap.sha, rosVersion: snap.rosVersion } : null
1234
+ });
1235
+ }
1236
+ if (p === "/api/drift/baseline" && req.method === "POST") {
1237
+ const b = await bodyJson(req);
1238
+ if (!b.device || !b.snapshotId) {
1239
+ return json({ error: "device and snapshotId are required" }, 400);
1240
+ }
1241
+ const snap = store.get(b.snapshotId);
1242
+ if (!snap)
1243
+ return json({ error: `snapshot '${b.snapshotId}' not found` }, 404);
1244
+ store.setBaseline(b.device, b.snapshotId, "dashboard", b.label, b.notes);
1245
+ return json({ ok: true, device: b.device, snapshotId: b.snapshotId });
1246
+ }
1247
+ if (baselineMatch && req.method === "DELETE") {
1248
+ const device = decodeURIComponent(baselineMatch[1]);
1249
+ const removed = store.removeBaseline(device);
1250
+ return json({ ok: removed, device });
1251
+ }
1252
+ const historyMatch = p.match(/^\/api\/drift\/history\/(.+)$/);
1253
+ if (historyMatch && req.method === "GET") {
1254
+ const device = decodeURIComponent(historyMatch[1]);
1255
+ const limit = Number(url.searchParams.get("limit") ?? "20");
1256
+ const snapshots = store.list(device, limit, false);
1257
+ return json({ device, snapshots });
1258
+ }
1259
+ const checkMatch = p.match(/^\/api\/drift\/check\/(.+)$/);
1260
+ if (checkMatch && req.method === "GET") {
1261
+ const deviceKey = decodeURIComponent(checkMatch[1]);
1262
+ let device;
1263
+ try {
1264
+ device = resolveDeviceName(deviceKey);
1265
+ } catch {
1266
+ return json({ error: `unknown device '${deviceKey}'` }, 404);
1267
+ }
1268
+ const baseline = store.getBaseline(device);
1269
+ if (!baseline)
1270
+ return json({ error: "no baseline set" }, 404);
1271
+ const baselineSnap = store.get(baseline.snapshotId);
1272
+ if (!baselineSnap)
1273
+ return json({ error: "baseline snapshot deleted" }, 404);
1274
+ try {
1275
+ const ctx = createContext(undefined, device);
1276
+ const exportCmd = new Cmd("/export").raw("terse").build();
1277
+ const liveBody = await executeMikrotikCommand(exportCmd, ctx);
1278
+ if (isEmpty(liveBody) || looksLikeError(liveBody)) {
1279
+ return json({ error: `export failed: ${liveBody.trim() || "(empty)"}` }, 502);
1280
+ }
1281
+ const diff = diffLines(normalizeExport(baselineSnap.body), normalizeExport(liveBody), {
1282
+ contextLines: 3,
1283
+ fromLabel: `baseline:${baselineSnap.id}`,
1284
+ toLabel: `${device}@live`
1285
+ });
1286
+ const report = analyzeDrift(diff, device, baseline.snapshotId, baseline.setAt);
1287
+ try {
1288
+ const logCmd = `/log print where topics~"system" and time > ([:timestamp] - 7d)`;
1289
+ const logText = await executeMikrotikCommand(logCmd, ctx);
1290
+ if (!isEmpty(logText) && !looksLikeError(logText)) {
1291
+ report.attributions = attributeChanges(report.sections, logText);
1292
+ }
1293
+ } catch {}
1294
+ return json(report);
1295
+ } catch (e) {
1296
+ return json({ error: e instanceof Error ? e.message : String(e) }, 502);
1297
+ }
1298
+ }
1299
+ return null;
1300
+ }
1301
+
1302
+ // src/observability/memory-routes.ts
1303
+ var storePromise2 = null;
1304
+ function getStore2() {
1305
+ if (!storePromise2) {
1306
+ const cfg = getConfig();
1307
+ storePromise2 = openMemoryStore(cfg.memory.dbPath);
1308
+ }
1309
+ return storePromise2;
1310
+ }
1311
+ function closeMemoryStore() {
1312
+ if (storePromise2) {
1313
+ storePromise2.then((s) => s.close()).catch(() => {});
1314
+ storePromise2 = null;
1315
+ }
1316
+ }
1317
+ var JSON_HEADERS2 = { "content-type": "application/json; charset=utf-8" };
1318
+ function json2(body, status = 200) {
1319
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS2 });
1320
+ }
1321
+ async function bodyJson2(req) {
1322
+ return await req.json();
1323
+ }
1324
+ async function memoryRoutes(req, url) {
1325
+ const p = url.pathname;
1326
+ if (!p.startsWith("/api/memory"))
1327
+ return null;
1328
+ const cfg = getConfig();
1329
+ if (!cfg.memory.enabled) {
1330
+ return json2({ error: "Knowledge-graph memory is disabled" }, 503);
1331
+ }
1332
+ if (p === "/api/memory/config" && req.method === "GET") {
1333
+ let stats = null;
1334
+ try {
1335
+ const store2 = await getStore2();
1336
+ stats = store2.stats();
1337
+ } catch {}
1338
+ return json2({
1339
+ enabled: cfg.memory.enabled,
1340
+ dbPath: cfg.memory.dbPath,
1341
+ stats
1342
+ });
1343
+ }
1344
+ if (p === "/api/memory/config" && req.method === "POST") {
1345
+ const body = await bodyJson2(req);
1346
+ const updates = {};
1347
+ if (body.dbPath !== undefined && typeof body.dbPath === "string" && body.dbPath !== cfg.memory.dbPath) {
1348
+ closeMemoryStore();
1349
+ resetMemoryStore();
1350
+ updates.dbPath = body.dbPath;
1351
+ storePromise2 = openMemoryStore(body.dbPath);
1352
+ try {
1353
+ await storePromise2;
1354
+ } catch (e) {
1355
+ storePromise2 = null;
1356
+ return json2({ error: `Failed to open memory DB at ${body.dbPath}: ${e instanceof Error ? e.message : String(e)}` }, 400);
1357
+ }
1358
+ }
1359
+ if (body.enabled !== undefined && typeof body.enabled === "boolean") {
1360
+ updates.enabled = body.enabled;
1361
+ }
1362
+ if (Object.keys(updates).length > 0) {
1363
+ const newMemory = { ...cfg.memory, ...updates };
1364
+ const newCfg = { ...cfg, memory: newMemory };
1365
+ setConfig(newCfg);
1366
+ const src = getConfigSource();
1367
+ try {
1368
+ atomicWrite(src.path, serializeConfig(newCfg));
1369
+ } catch {}
1370
+ }
1371
+ let stats = null;
1372
+ try {
1373
+ const store2 = await getStore2();
1374
+ stats = store2.stats();
1375
+ } catch {}
1376
+ return json2({
1377
+ ok: true,
1378
+ enabled: getConfig().memory.enabled,
1379
+ dbPath: getConfig().memory.dbPath,
1380
+ stats
1381
+ });
1382
+ }
1383
+ const store = await getStore2();
1384
+ if (p === "/api/memory/graph" && req.method === "GET") {
1385
+ return json2(store.readGraph());
1386
+ }
1387
+ if (p === "/api/memory/stats" && req.method === "GET") {
1388
+ return json2(store.stats());
1389
+ }
1390
+ if (p === "/api/memory/search" && req.method === "GET") {
1391
+ const q = url.searchParams.get("q") || "";
1392
+ const limit = Number(url.searchParams.get("limit") ?? 50);
1393
+ return json2(store.searchNodes(q, limit));
1394
+ }
1395
+ if (p === "/api/memory/activity" && req.method === "GET") {
1396
+ const limit = Number(url.searchParams.get("limit") ?? 50);
1397
+ const since = url.searchParams.get("since");
1398
+ return json2(store.activity(limit, since ? Number(since) : undefined));
1399
+ }
1400
+ const entityMatch = p.match(/^\/api\/memory\/entity\/(.+)$/);
1401
+ if (entityMatch && req.method === "GET") {
1402
+ const name = decodeURIComponent(entityMatch[1]);
1403
+ const graph = store.openNodes([name]);
1404
+ if (graph.entities.length === 0)
1405
+ return json2({ error: "Entity not found" }, 404);
1406
+ return json2({ entity: graph.entities[0], relations: graph.relations });
1407
+ }
1408
+ if (p === "/api/memory/entities" && req.method === "POST") {
1409
+ const body = await bodyJson2(req);
1410
+ const created = store.createEntities(body.entities ?? []);
1411
+ return json2({ created, count: created.length });
1412
+ }
1413
+ if (p === "/api/memory/relations" && req.method === "POST") {
1414
+ const body = await bodyJson2(req);
1415
+ const created = store.createRelations(body.relations ?? []);
1416
+ return json2({ created, count: created.length });
1417
+ }
1418
+ if (p === "/api/memory/observations" && req.method === "POST") {
1419
+ const body = await bodyJson2(req);
1420
+ const results = store.addObservations(body.observations ?? []);
1421
+ return json2({ results });
1422
+ }
1423
+ if (p === "/api/memory/entities" && req.method === "DELETE") {
1424
+ const body = await bodyJson2(req);
1425
+ const removed = store.deleteEntities(body.names ?? []);
1426
+ return json2({ removed });
1427
+ }
1428
+ if (p === "/api/memory/relations" && req.method === "DELETE") {
1429
+ const body = await bodyJson2(req);
1430
+ const removed = store.deleteRelations(body.relations ?? []);
1431
+ return json2({ removed });
1432
+ }
1433
+ if (p === "/api/memory/observations" && req.method === "DELETE") {
1434
+ const body = await bodyJson2(req);
1435
+ const removed = store.deleteObservations(body.deletions ?? []);
1436
+ return json2({ removed });
1437
+ }
1438
+ return null;
1439
+ }
1286
1440
 
1287
1441
  // src/observability/dashboard.ts
1288
1442
  var SERVER_TAG2 = "mikrotik-mcp";
@@ -1324,13 +1478,13 @@ async function fetchLatestRelease() {
1324
1478
  releaseCache = { data, fetchedAt: Date.now() };
1325
1479
  return data;
1326
1480
  }
1327
- var JSON_HEADERS = { "content-type": "application/json; charset=utf-8" };
1328
- function json(body, status = 200) {
1329
- return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS });
1481
+ var JSON_HEADERS3 = { "content-type": "application/json; charset=utf-8" };
1482
+ function json3(body, status = 200) {
1483
+ return new Response(JSON.stringify(body), { status, headers: JSON_HEADERS3 });
1330
1484
  }
1331
1485
  function dashboardHtml() {
1332
1486
  try {
1333
- return readFileSync2(join2(UI_DIST_DIR, "observability.html"), "utf8");
1487
+ return readFileSync3(join3(UI_DIST_DIR, "observability.html"), "utf8");
1334
1488
  } catch {
1335
1489
  return `<!doctype html><meta charset=utf-8><body style="font:14px system-ui;padding:24px;background:#0b0d10;color:#e8eaed">
1336
1490
  <h2>MikroTik MCP \u2014 Observability Dashboard</h2>
@@ -1530,11 +1684,11 @@ function issues(error) {
1530
1684
  async function configRoutes(req, url, admin) {
1531
1685
  const p = url.pathname;
1532
1686
  if (p === "/api/config-schema" && req.method === "GET") {
1533
- return json(configSchemaJson());
1687
+ return json3(configSchemaJson());
1534
1688
  }
1535
1689
  if (p === "/api/config/validate" && req.method === "POST") {
1536
1690
  const merged = mergeSecrets(await readJson(req), getConfig());
1537
- return json(validateConfig(merged));
1691
+ return json3(validateConfig(merged));
1538
1692
  }
1539
1693
  if (p === "/api/config/test-device" && req.method === "POST") {
1540
1694
  const body = await readJson(req);
@@ -1543,22 +1697,22 @@ async function configRoutes(req, url, admin) {
1543
1697
  const merged = mergeSecrets(body?.config, currentDc);
1544
1698
  const parsed = DeviceConfigSchema.safeParse(merged);
1545
1699
  if (!parsed.success)
1546
- return json({ ok: false, errors: issues(parsed.error) }, 400);
1700
+ return json3({ ok: false, errors: issues(parsed.error) }, 400);
1547
1701
  const status = await probeDevice(`config-test:${name}`, parsed.data);
1548
- return json({ ok: true, status });
1702
+ return json3({ ok: true, status });
1549
1703
  }
1550
1704
  if (p === "/api/config/preview" && req.method === "POST") {
1551
1705
  const before = JSON.stringify(redact(getConfig()), null, 2);
1552
1706
  const after = JSON.stringify(await readJson(req) ?? {}, null, 2);
1553
1707
  const d = diffLines(before, after, { fromLabel: "current", toLabel: "edited" });
1554
- return json({ summary: d.summary, unified: d.unified });
1708
+ return json3({ summary: d.summary, unified: d.unified });
1555
1709
  }
1556
1710
  if (p === "/api/config" && req.method === "POST") {
1557
1711
  const body = await readJson(req);
1558
1712
  const merged = mergeSecrets(body?.config, getConfig());
1559
1713
  const v = validateConfig(merged);
1560
1714
  if (!v.ok || !v.value)
1561
- return json({ ok: false, errors: v.errors }, 400);
1715
+ return json3({ ok: false, errors: v.errors }, 400);
1562
1716
  const prev = getConfig();
1563
1717
  const before = JSON.stringify(redact(prev), null, 2);
1564
1718
  const after = JSON.stringify(redact(v.value), null, 2);
@@ -1567,19 +1721,19 @@ async function configRoutes(req, url, admin) {
1567
1721
  const nextDevs = Object.keys(v.value.devices);
1568
1722
  const devicesChanged = prevDevs.length !== nextDevs.length || prevDevs.some((d) => !nextDevs.includes(d));
1569
1723
  const res = admin.applyConfig(v.value, clampRollback(body?.rollbackMs));
1570
- return json({ ok: true, ...res, devicesChanged, summary: diff.summary, unified: diff.unified });
1724
+ return json3({ ok: true, ...res, devicesChanged, summary: diff.summary, unified: diff.unified });
1571
1725
  }
1572
1726
  if (p === "/api/config/keep" && req.method === "POST") {
1573
1727
  const body = await readJson(req);
1574
1728
  const kept = admin.keepConfig(String(body?.pendingId ?? ""));
1575
1729
  if (kept)
1576
1730
  recordVersion(getConfig(), "auto", Date.now());
1577
- return json({ kept });
1731
+ return json3({ kept });
1578
1732
  }
1579
1733
  if (p === "/api/config/rollback" && req.method === "POST") {
1580
1734
  const body = await readJson(req);
1581
1735
  const rolledBack = admin.rollback(String(body?.pendingId ?? ""));
1582
- return json({ rolledBack, config: redact(getConfig()) });
1736
+ return json3({ rolledBack, config: redact(getConfig()) });
1583
1737
  }
1584
1738
  if (p === "/api/config/history" && req.method === "GET") {
1585
1739
  const current = JSON.stringify(redact(getConfig()), null, 2);
@@ -1594,55 +1748,55 @@ async function configRoutes(req, url, admin) {
1594
1748
  } catch {}
1595
1749
  return { ...v, drift: { added, removed } };
1596
1750
  });
1597
- return json({ versions, bytes: historyBytes(), retention: AUTO_RETENTION });
1751
+ return json3({ versions, bytes: historyBytes(), retention: AUTO_RETENTION });
1598
1752
  }
1599
1753
  if (p === "/api/config/history/get" && req.method === "GET") {
1600
1754
  const id = url.searchParams.get("id");
1601
1755
  if (!id)
1602
- return json({ error: "id required" }, 400);
1756
+ return json3({ error: "id required" }, 400);
1603
1757
  try {
1604
1758
  const v = readVersion(id);
1605
- return json({
1759
+ return json3({
1606
1760
  ts: v.ts,
1607
1761
  kind: v.kind,
1608
1762
  label: v.label,
1609
1763
  config: JSON.stringify(redact(v.config), null, 2)
1610
1764
  });
1611
1765
  } catch {
1612
- return json({ error: "not found" }, 404);
1766
+ return json3({ error: "not found" }, 404);
1613
1767
  }
1614
1768
  }
1615
1769
  if (p === "/api/config/history/diff" && req.method === "GET") {
1616
1770
  const id = url.searchParams.get("id");
1617
1771
  if (!id)
1618
- return json({ error: "id required" }, 400);
1772
+ return json3({ error: "id required" }, 400);
1619
1773
  try {
1620
1774
  const before = JSON.stringify(redact(readVersion(id).config), null, 2);
1621
1775
  const after = JSON.stringify(redact(getConfig()), null, 2);
1622
1776
  const d = diffLines(before, after, { fromLabel: "this version", toLabel: "current" });
1623
- return json({ summary: d.summary, unified: d.unified });
1777
+ return json3({ summary: d.summary, unified: d.unified });
1624
1778
  } catch {
1625
- return json({ error: "not found" }, 404);
1779
+ return json3({ error: "not found" }, 404);
1626
1780
  }
1627
1781
  }
1628
1782
  if (p === "/api/config/history/checkpoint" && req.method === "POST") {
1629
1783
  const b = await readJson(req);
1630
1784
  const label = b?.label?.trim() || "checkpoint";
1631
- return json({ ok: true, version: recordVersion(getConfig(), "checkpoint", Date.now(), label) });
1785
+ return json3({ ok: true, version: recordVersion(getConfig(), "checkpoint", Date.now(), label) });
1632
1786
  }
1633
1787
  if (p === "/api/config/history/restore" && req.method === "POST") {
1634
1788
  const b = await readJson(req);
1635
1789
  if (!b?.id)
1636
- return json({ error: "id required" }, 400);
1790
+ return json3({ error: "id required" }, 400);
1637
1791
  let target;
1638
1792
  try {
1639
1793
  target = readVersion(b.id);
1640
1794
  } catch {
1641
- return json({ error: "not found" }, 404);
1795
+ return json3({ error: "not found" }, 404);
1642
1796
  }
1643
1797
  const v = validateConfig(target.config);
1644
1798
  if (!v.ok || !v.value)
1645
- return json({ ok: false, errors: v.errors }, 400);
1799
+ return json3({ ok: false, errors: v.errors }, 400);
1646
1800
  recordVersion(getConfig(), "auto", Date.now(), "before restore");
1647
1801
  setConfig(v.value);
1648
1802
  let persisted = true;
@@ -1653,13 +1807,13 @@ async function configRoutes(req, url, admin) {
1653
1807
  }
1654
1808
  const label = target.label ? `restored "${target.label}"` : `restored ${b.id}`;
1655
1809
  recordVersion(getConfig(), "auto", Date.now(), label);
1656
- return json({ ok: true, persisted, restored: b.id, config: redact(getConfig()) });
1810
+ return json3({ ok: true, persisted, restored: b.id, config: redact(getConfig()) });
1657
1811
  }
1658
1812
  if (p === "/api/config/history/delete" && req.method === "POST") {
1659
1813
  const b = await readJson(req);
1660
1814
  if (!b?.id)
1661
- return json({ error: "id required" }, 400);
1662
- return deleteVersion(b.id) ? json({ ok: true }) : json({ error: "not found" }, 404);
1815
+ return json3({ error: "id required" }, 400);
1816
+ return deleteVersion(b.id) ? json3({ ok: true }) : json3({ error: "not found" }, 404);
1663
1817
  }
1664
1818
  return null;
1665
1819
  }
@@ -1670,16 +1824,16 @@ async function modulesRoutes(req, url) {
1670
1824
  if (p === "/api/modules" && req.method === "GET") {
1671
1825
  const cfg = getConfig();
1672
1826
  const src = getConfigSource();
1673
- return json({ ...moduleSurface(cfg.tools), filter: cfg.tools, source: src });
1827
+ return json3({ ...moduleSurface(cfg.tools), filter: cfg.tools, source: src });
1674
1828
  }
1675
1829
  if (p === "/api/modules/toggle" && req.method === "POST") {
1676
1830
  const b = await readJson(req);
1677
1831
  if (typeof b?.slug !== "string" || typeof b?.enabled !== "boolean") {
1678
- return json({ error: "slug (string) and enabled (boolean) are required" }, 400);
1832
+ return json3({ error: "slug (string) and enabled (boolean) are required" }, 400);
1679
1833
  }
1680
1834
  const mod = moduleCatalog.find((m) => m.slug.toLowerCase() === b.slug.toLowerCase());
1681
1835
  if (!mod)
1682
- return json({ error: `unknown module: ${b.slug}` }, 404);
1836
+ return json3({ error: `unknown module: ${b.slug}` }, 404);
1683
1837
  const cfg = getConfig();
1684
1838
  const nextTools = ToolFilterSchema.parse(applyModuleToggle(cfg.tools, mod.slug, b.enabled));
1685
1839
  const next = { ...cfg, tools: nextTools };
@@ -1695,7 +1849,7 @@ async function modulesRoutes(req, url) {
1695
1849
  if (persisted) {
1696
1850
  recordVersion(getConfig(), "auto", Date.now(), `module ${b.enabled ? "enabled" : "disabled"}: ${mod.slug}`);
1697
1851
  }
1698
- return json({
1852
+ return json3({
1699
1853
  ok: true,
1700
1854
  persisted,
1701
1855
  requiresReconnect: true,
@@ -1712,11 +1866,11 @@ async function modulesRoutes(req, url) {
1712
1866
  async function captureRoutes(req, url) {
1713
1867
  const p = url.pathname;
1714
1868
  if (p === "/api/capture/status" && req.method === "GET") {
1715
- return json(capture.stats());
1869
+ return json3(capture.stats());
1716
1870
  }
1717
1871
  if (p === "/api/capture/packets" && req.method === "GET") {
1718
1872
  const limit = Number(url.searchParams.get("limit") ?? 200) || 200;
1719
- return json({ packets: capture.recent(limit), stats: capture.stats() });
1873
+ return json3({ packets: capture.recent(limit), stats: capture.stats() });
1720
1874
  }
1721
1875
  if (p === "/api/capture/pcap" && req.method === "GET") {
1722
1876
  return new Response(capture.pcap(), {
@@ -1728,11 +1882,11 @@ async function captureRoutes(req, url) {
1728
1882
  }
1729
1883
  if (p === "/api/capture/start" && req.method === "POST") {
1730
1884
  const body = await readJson(req);
1731
- return json(await capture.start(typeof body?.port === "number" ? body.port : DEFAULT_TZSP_PORT));
1885
+ return json3(await capture.start(typeof body?.port === "number" ? body.port : DEFAULT_TZSP_PORT));
1732
1886
  }
1733
1887
  if (p === "/api/capture/stop" && req.method === "POST") {
1734
1888
  capture.stop();
1735
- return json({ ok: true, ...capture.stats() });
1889
+ return json3({ ok: true, ...capture.stats() });
1736
1890
  }
1737
1891
  return null;
1738
1892
  }
@@ -1743,32 +1897,32 @@ async function clientsRoutes(req, url) {
1743
1897
  const deviceFromQuery = () => url.searchParams.get("device") ?? undefined;
1744
1898
  if (p === "/api/clients" && req.method === "GET") {
1745
1899
  const ctx = createContext(undefined, deviceFromQuery());
1746
- return json(devicesView(await fetchDevices(ctx)));
1900
+ return json3(devicesView(await fetchDevices(ctx)));
1747
1901
  }
1748
1902
  if (p === "/api/clients/traffic" && req.method === "GET") {
1749
1903
  const ip = url.searchParams.get("ip");
1750
1904
  if (!ip)
1751
- return json({ error: "ip required" }, 400);
1905
+ return json3({ error: "ip required" }, 400);
1752
1906
  const ctx = createContext(undefined, deviceFromQuery());
1753
- return json(await sampleDeviceTraffic(ctx, ip));
1907
+ return json3(await sampleDeviceTraffic(ctx, ip));
1754
1908
  }
1755
1909
  if (p === "/api/clients/traffic-bulk" && req.method === "GET") {
1756
1910
  const ctx = createContext(undefined, deviceFromQuery());
1757
- return json(await sampleAllTraffic(ctx));
1911
+ return json3(await sampleAllTraffic(ctx));
1758
1912
  }
1759
1913
  if (req.method === "POST") {
1760
1914
  const b = await readJson(req);
1761
1915
  const ctx = createContext(undefined, b?.device);
1762
1916
  if (p === "/api/clients/limits") {
1763
1917
  if (!b?.ip)
1764
- return json({ error: "ip required" }, 400);
1765
- return json(await setDeviceLimits(ctx, b.ip, { download: b.download, upload: b.upload }));
1918
+ return json3({ error: "ip required" }, 400);
1919
+ return json3(await setDeviceLimits(ctx, b.ip, { download: b.download, upload: b.upload }));
1766
1920
  }
1767
1921
  if (!b?.mac)
1768
- return json({ error: "mac required" }, 400);
1922
+ return json3({ error: "mac required" }, 400);
1769
1923
  const run = async (op) => {
1770
1924
  const r = await op;
1771
- return json({ ...r, view: r.ok ? devicesView(await fetchDevices(ctx)) : undefined });
1925
+ return json3({ ...r, view: r.ok ? devicesView(await fetchDevices(ctx)) : undefined });
1772
1926
  };
1773
1927
  if (p === "/api/clients/block")
1774
1928
  return run(blockDevice(ctx, b.mac, b.comment));
@@ -1780,12 +1934,12 @@ async function clientsRoutes(req, url) {
1780
1934
  return run(removeDeviceLease(ctx, b.mac));
1781
1935
  if (p === "/api/clients/set-ip") {
1782
1936
  if (!b.ip)
1783
- return json({ error: "ip required" }, 400);
1937
+ return json3({ error: "ip required" }, 400);
1784
1938
  return run(setDeviceIp(ctx, b.mac, b.ip));
1785
1939
  }
1786
1940
  if (p === "/api/clients/label") {
1787
1941
  if (typeof b.label !== "string")
1788
- return json({ error: "label required" }, 400);
1942
+ return json3({ error: "label required" }, 400);
1789
1943
  return run(setDeviceLabel(ctx, b.mac, b.label));
1790
1944
  }
1791
1945
  }
@@ -1798,20 +1952,20 @@ async function aaaRoutes(req, url) {
1798
1952
  const deviceFromQuery = () => url.searchParams.get("device") ?? undefined;
1799
1953
  if (req.method === "GET") {
1800
1954
  if (p === "/api/aaa/entities") {
1801
- return json({ entities: AAA_ENTITIES });
1955
+ return json3({ entities: AAA_ENTITIES });
1802
1956
  }
1803
1957
  if (p === "/api/aaa/radius-incoming") {
1804
- return json(await getRadiusIncoming(createContext(undefined, deviceFromQuery())));
1958
+ return json3(await getRadiusIncoming(createContext(undefined, deviceFromQuery())));
1805
1959
  }
1806
1960
  if (p === "/api/aaa/um-settings") {
1807
- return json(await getUmSettings(createContext(undefined, deviceFromQuery())));
1961
+ return json3(await getUmSettings(createContext(undefined, deviceFromQuery())));
1808
1962
  }
1809
1963
  const listMatch = p.match(/^\/api\/aaa\/list\/([\w-]+)$/);
1810
1964
  if (listMatch) {
1811
1965
  const slug = listMatch[1];
1812
1966
  if (!AAA_ENTITIES[slug])
1813
- return json({ error: "unknown entity" }, 404);
1814
- return json(await listAaaEntity(createContext(undefined, deviceFromQuery()), slug));
1967
+ return json3({ error: "unknown entity" }, 404);
1968
+ return json3(await listAaaEntity(createContext(undefined, deviceFromQuery()), slug));
1815
1969
  }
1816
1970
  return null;
1817
1971
  }
@@ -1819,30 +1973,30 @@ async function aaaRoutes(req, url) {
1819
1973
  const body = await readJson(req);
1820
1974
  const ctx = createContext(undefined, body?.device);
1821
1975
  if (p === "/api/aaa/radius-incoming")
1822
- return json(await setRadiusIncoming(ctx, body?.fields ?? {}));
1976
+ return json3(await setRadiusIncoming(ctx, body?.fields ?? {}));
1823
1977
  if (p === "/api/aaa/um-settings")
1824
- return json(await setUmSettings(ctx, body?.fields ?? {}));
1978
+ return json3(await setUmSettings(ctx, body?.fields ?? {}));
1825
1979
  if (p === "/api/aaa/radius-reset-counters")
1826
- return json(await resetRadiusCounters(ctx));
1980
+ return json3(await resetRadiusCounters(ctx));
1827
1981
  const slug = body?.slug ?? "";
1828
1982
  if (!AAA_ENTITIES[slug])
1829
- return json({ error: "unknown entity" }, 404);
1983
+ return json3({ error: "unknown entity" }, 404);
1830
1984
  if (p === "/api/aaa/add")
1831
- return json(await addAaaEntity(ctx, slug, body?.fields ?? {}));
1985
+ return json3(await addAaaEntity(ctx, slug, body?.fields ?? {}));
1832
1986
  if (p === "/api/aaa/update") {
1833
1987
  if (!body?.id)
1834
- return json({ error: "id required" }, 400);
1835
- return json(await updateAaaEntity(ctx, slug, body.id, body?.fields ?? {}));
1988
+ return json3({ error: "id required" }, 400);
1989
+ return json3(await updateAaaEntity(ctx, slug, body.id, body?.fields ?? {}));
1836
1990
  }
1837
1991
  if (p === "/api/aaa/remove") {
1838
1992
  if (!body?.id)
1839
- return json({ error: "id required" }, 400);
1840
- return json(await removeAaaEntity(ctx, slug, body.id));
1993
+ return json3({ error: "id required" }, 400);
1994
+ return json3(await removeAaaEntity(ctx, slug, body.id));
1841
1995
  }
1842
1996
  if (p === "/api/aaa/toggle") {
1843
1997
  if (!body?.id)
1844
- return json({ error: "id required" }, 400);
1845
- return json(await toggleAaaEntity(ctx, slug, body.id, body?.enable === true));
1998
+ return json3({ error: "id required" }, 400);
1999
+ return json3(await toggleAaaEntity(ctx, slug, body.id, body?.enable === true));
1846
2000
  }
1847
2001
  }
1848
2002
  return null;
@@ -1858,43 +2012,43 @@ async function usageRoutes(req, url) {
1858
2012
  return null;
1859
2013
  if (p === "/api/usage/sampler") {
1860
2014
  if (req.method === "GET")
1861
- return json({ intervalMs: getUsageSamplerInterval() });
2015
+ return json3({ intervalMs: getUsageSamplerInterval() });
1862
2016
  if (req.method === "POST") {
1863
2017
  const b = await readJson(req);
1864
2018
  const applied = setUsageSamplerInterval(Number(b?.intervalMs));
1865
- return json({ intervalMs: applied });
2019
+ return json3({ intervalMs: applied });
1866
2020
  }
1867
2021
  return null;
1868
2022
  }
1869
2023
  if (req.method !== "GET")
1870
2024
  return null;
1871
2025
  if (!usageStore)
1872
- return json({ error: "usage store not active" }, 503);
2026
+ return json3({ error: "usage store not active" }, 503);
1873
2027
  const device = resolveDeviceName(url.searchParams.get("device") ?? undefined);
1874
2028
  const sinceTs = (days) => Date.now() - days * 86400000;
1875
2029
  if (p === "/api/usage/um-users") {
1876
- return json({ users: usageStore.umUsers(device) });
2030
+ return json3({ users: usageStore.umUsers(device) });
1877
2031
  }
1878
2032
  if (p === "/api/usage/client") {
1879
2033
  const ip = url.searchParams.get("ip");
1880
2034
  if (!ip)
1881
- return json({ error: "ip required" }, 400);
2035
+ return json3({ error: "ip required" }, 400);
1882
2036
  const series = usageStore.clientDailyUsage(device, ip, sinceTs(daysParam(url, 90, 400)));
1883
- return json(withTotals(series));
2037
+ return json3(withTotals(series));
1884
2038
  }
1885
2039
  if (p === "/api/usage/um-user") {
1886
2040
  const user = url.searchParams.get("user");
1887
2041
  if (!user)
1888
- return json({ error: "user required" }, 400);
2042
+ return json3({ error: "user required" }, 400);
1889
2043
  const series = usageStore.umUserDailyUsage(device, user, sinceTs(daysParam(url, 90, 400)));
1890
- return json(withTotals(series));
2044
+ return json3(withTotals(series));
1891
2045
  }
1892
2046
  if (p === "/api/usage/heatmap") {
1893
2047
  const user = url.searchParams.get("user");
1894
2048
  const days = usageStore.heatmap(device, user || null, sinceTs(daysParam(url, 371, 400)));
1895
2049
  const total = days.reduce((s, d) => s + d.count, 0);
1896
2050
  const max = days.reduce((m, d) => Math.max(m, d.count), 0);
1897
- return json({ days, total, max });
2051
+ return json3({ days, total, max });
1898
2052
  }
1899
2053
  return null;
1900
2054
  }
@@ -1918,13 +2072,13 @@ async function featureRoutes(req, url) {
1918
2072
  const cfg = getConfig();
1919
2073
  const all = Object.keys(cfg.devices).flatMap((d) => store.list(d, 200, false));
1920
2074
  all.sort((a, b) => b.ts - a.ts);
1921
- return json({ snapshots: all });
2075
+ return json3({ snapshots: all });
1922
2076
  }
1923
2077
  const snapMatch = p.match(/^\/api\/snapshot\/(.+)$/);
1924
2078
  if (snapMatch && req.method === "GET") {
1925
2079
  const store = await snapStore();
1926
2080
  const s = store.get(decodeURIComponent(snapMatch[1]));
1927
- return s ? json(s) : json({ error: "not found" }, 404);
2081
+ return s ? json3(s) : json3({ error: "not found" }, 404);
1928
2082
  }
1929
2083
  if (p === "/api/snapshots/diff" && req.method === "POST") {
1930
2084
  const store = await snapStore();
@@ -1932,13 +2086,13 @@ async function featureRoutes(req, url) {
1932
2086
  const from = b?.from ? store.get(b.from) : null;
1933
2087
  const to = b?.to ? store.get(b.to) : null;
1934
2088
  if (!from || !to) {
1935
- return json({ error: "both 'from' and 'to' snapshot ids are required" }, 400);
2089
+ return json3({ error: "both 'from' and 'to' snapshot ids are required" }, 400);
1936
2090
  }
1937
2091
  const diff = diffLines(normalizeExport(from.body), normalizeExport(to.body), {
1938
2092
  fromLabel: from.label ?? from.id,
1939
2093
  toLabel: to.label ?? to.id
1940
2094
  });
1941
- return json({
2095
+ return json3({
1942
2096
  from: { id: from.id, label: from.label, ts: from.ts, device: from.device },
1943
2097
  to: { id: to.id, label: to.label, ts: to.ts, device: to.device },
1944
2098
  summary: diff.summary,
@@ -1952,16 +2106,16 @@ async function featureRoutes(req, url) {
1952
2106
  ...b?.script ? splitCommands(b.script) : []
1953
2107
  ];
1954
2108
  if (commands.length === 0)
1955
- return json({ error: "no commands provided" }, 400);
2109
+ return json3({ error: "no commands provided" }, 400);
1956
2110
  const plan = buildChangePlan(commands);
1957
- return json({ plan, text: renderPlan(plan) });
2111
+ return json3({ plan, text: renderPlan(plan) });
1958
2112
  }
1959
2113
  if (p === "/api/s3" && req.method === "GET") {
1960
- return json({ configured: isS3Configured(), target: isS3Configured() ? s3Target() : null });
2114
+ return json3({ configured: isS3Configured(), target: isS3Configured() ? s3Target() : null });
1961
2115
  }
1962
2116
  if (p === "/api/s3/list" && req.method === "GET") {
1963
2117
  if (!isS3Configured())
1964
- return json({ configured: false, objects: [] });
2118
+ return json3({ configured: false, objects: [] });
1965
2119
  const prefix = url.searchParams.get("prefix") ?? "";
1966
2120
  try {
1967
2121
  const res = await getS3Client().list({ prefix: prefix || undefined, maxKeys: 1000 });
@@ -1970,42 +2124,42 @@ async function featureRoutes(req, url) {
1970
2124
  size: o.size ?? 0,
1971
2125
  lastModified: o.lastModified ?? null
1972
2126
  }));
1973
- return json({ configured: true, target: s3Target(), objects, truncated: !!res.isTruncated });
2127
+ return json3({ configured: true, target: s3Target(), objects, truncated: !!res.isTruncated });
1974
2128
  } catch (e) {
1975
- return json({ error: e instanceof Error ? e.message : String(e) }, 502);
2129
+ return json3({ error: e instanceof Error ? e.message : String(e) }, 502);
1976
2130
  }
1977
2131
  }
1978
2132
  if (p === "/api/s3/presign" && req.method === "GET") {
1979
2133
  if (!isS3Configured())
1980
- return json({ error: "S3 not configured" }, 400);
2134
+ return json3({ error: "S3 not configured" }, 400);
1981
2135
  const key = url.searchParams.get("key");
1982
2136
  if (!key)
1983
- return json({ error: "key required" }, 400);
2137
+ return json3({ error: "key required" }, 400);
1984
2138
  try {
1985
2139
  const link = getS3Client().presign(key, { expiresIn: presignExpiresIn(), method: "GET" });
1986
- return json({ url: link });
2140
+ return json3({ url: link });
1987
2141
  } catch (e) {
1988
- return json({ error: e instanceof Error ? e.message : String(e) }, 502);
2142
+ return json3({ error: e instanceof Error ? e.message : String(e) }, 502);
1989
2143
  }
1990
2144
  }
1991
2145
  if (p === "/api/s3/delete" && req.method === "POST") {
1992
2146
  if (!isS3Configured())
1993
- return json({ error: "S3 not configured" }, 400);
2147
+ return json3({ error: "S3 not configured" }, 400);
1994
2148
  const b = await readJson(req);
1995
2149
  if (!b?.key)
1996
- return json({ error: "key required" }, 400);
2150
+ return json3({ error: "key required" }, 400);
1997
2151
  try {
1998
2152
  const client = getS3Client();
1999
2153
  if (!await client.exists(b.key))
2000
- return json({ error: "not found" }, 404);
2154
+ return json3({ error: "not found" }, 404);
2001
2155
  await client.delete(b.key);
2002
- return json({ ok: true, key: b.key });
2156
+ return json3({ ok: true, key: b.key });
2003
2157
  } catch (e) {
2004
- return json({ error: e instanceof Error ? e.message : String(e) }, 502);
2158
+ return json3({ error: e instanceof Error ? e.message : String(e) }, 502);
2005
2159
  }
2006
2160
  }
2007
2161
  if (p === "/api/backups" && req.method === "GET") {
2008
- return json({
2162
+ return json3({
2009
2163
  dir: backupDir(),
2010
2164
  devices: Object.keys(getConfig().devices),
2011
2165
  backups: listBackups()
@@ -2015,14 +2169,14 @@ async function featureRoutes(req, url) {
2015
2169
  const b = await readJson(req);
2016
2170
  const raw = b?.dir?.trim();
2017
2171
  if (!raw)
2018
- return json({ error: "dir required" }, 400);
2019
- const dir = raw === "~" || raw.startsWith("~/") ? join2(homedir(), raw.slice(1)) : raw;
2172
+ return json3({ error: "dir required" }, 400);
2173
+ const dir = raw === "~" || raw.startsWith("~/") ? join3(homedir(), raw.slice(1)) : raw;
2020
2174
  const next = { ...getConfig(), backupDir: dir };
2021
2175
  setConfig(next);
2022
2176
  try {
2023
2177
  atomicWrite(getConfigSource().path, serializeConfig(next));
2024
2178
  } catch (e) {
2025
- return json({
2179
+ return json3({
2026
2180
  ok: true,
2027
2181
  dir: backupDir(),
2028
2182
  persisted: false,
@@ -2030,16 +2184,16 @@ async function featureRoutes(req, url) {
2030
2184
  });
2031
2185
  }
2032
2186
  recordVersion(getConfig(), "auto", Date.now(), "backup path changed");
2033
- return json({ ok: true, dir: backupDir(), persisted: true });
2187
+ return json3({ ok: true, dir: backupDir(), persisted: true });
2034
2188
  }
2035
2189
  if (p === "/api/backups/get" && req.method === "GET") {
2036
2190
  const name = url.searchParams.get("name");
2037
2191
  if (!name)
2038
- return json({ error: "name required" }, 400);
2192
+ return json3({ error: "name required" }, 400);
2039
2193
  try {
2040
- return json({ name, content: readBackup(name) });
2194
+ return json3({ name, content: readBackup(name) });
2041
2195
  } catch {
2042
- return json({ error: "not found" }, 404);
2196
+ return json3({ error: "not found" }, 404);
2043
2197
  }
2044
2198
  }
2045
2199
  if (p === "/api/backups/raw" && req.method === "GET") {
@@ -2060,41 +2214,41 @@ async function featureRoutes(req, url) {
2060
2214
  if (p === "/api/backups/upload" && req.method === "POST") {
2061
2215
  const b = await readJson(req);
2062
2216
  if (!b?.name || typeof b.content !== "string") {
2063
- return json({ error: "name and content are required" }, 400);
2217
+ return json3({ error: "name and content are required" }, 400);
2064
2218
  }
2065
2219
  try {
2066
2220
  const safe = b.name.endsWith(".rsc") ? b.name : `${b.name}.rsc`;
2067
- return json({ ok: true, name: writeBackup(safe, b.content) });
2221
+ return json3({ ok: true, name: writeBackup(safe, b.content) });
2068
2222
  } catch (e) {
2069
- return json({ error: e instanceof Error ? e.message : String(e) }, 400);
2223
+ return json3({ error: e instanceof Error ? e.message : String(e) }, 400);
2070
2224
  }
2071
2225
  }
2072
2226
  if (p === "/api/backups/rename" && req.method === "POST") {
2073
2227
  const b = await readJson(req);
2074
2228
  if (!b?.name || !b?.new_name)
2075
- return json({ error: "name and new_name are required" }, 400);
2229
+ return json3({ error: "name and new_name are required" }, 400);
2076
2230
  try {
2077
- return json({ ok: true, name: renameBackup(b.name, b.new_name) });
2231
+ return json3({ ok: true, name: renameBackup(b.name, b.new_name) });
2078
2232
  } catch (e) {
2079
- return json({ error: e instanceof Error ? e.message : String(e) }, 400);
2233
+ return json3({ error: e instanceof Error ? e.message : String(e) }, 400);
2080
2234
  }
2081
2235
  }
2082
2236
  if (p === "/api/backups/delete" && req.method === "POST") {
2083
2237
  const b = await readJson(req);
2084
2238
  if (!b?.name)
2085
- return json({ error: "name required" }, 400);
2239
+ return json3({ error: "name required" }, 400);
2086
2240
  try {
2087
- return deleteBackup(b.name) ? json({ ok: true }) : json({ error: "not found" }, 404);
2241
+ return deleteBackup(b.name) ? json3({ ok: true }) : json3({ error: "not found" }, 404);
2088
2242
  } catch (e) {
2089
- return json({ error: e instanceof Error ? e.message : String(e) }, 400);
2243
+ return json3({ error: e instanceof Error ? e.message : String(e) }, 400);
2090
2244
  }
2091
2245
  }
2092
2246
  if (p === "/api/backups/restore" && req.method === "POST") {
2093
2247
  const b = await readJson(req);
2094
2248
  if (!b?.name)
2095
- return json({ error: "name required" }, 400);
2249
+ return json3({ error: "name required" }, 400);
2096
2250
  const device = resolveDeviceName(b.device);
2097
- return json(await restoreLocalBackup(device, b.name, b.confirm === true));
2251
+ return json3(await restoreLocalBackup(device, b.name, b.confirm === true));
2098
2252
  }
2099
2253
  if (p === "/api/backups/create" && req.method === "POST") {
2100
2254
  const b = await readJson(req);
@@ -2106,7 +2260,7 @@ async function featureRoutes(req, url) {
2106
2260
  compact: b?.compact === true,
2107
2261
  terse: b?.terse === true
2108
2262
  });
2109
- return r.ok ? json(r) : json({ error: r.error ?? "export failed" }, 502);
2263
+ return r.ok ? json3(r) : json3({ error: r.error ?? "export failed" }, 502);
2110
2264
  }
2111
2265
  return null;
2112
2266
  }
@@ -2124,7 +2278,7 @@ async function runDashboard(cfg, transportLabel) {
2124
2278
  });
2125
2279
  startHealthChecks(30000);
2126
2280
  try {
2127
- usageStore = await openUsageStore(join2(dirname4(cfg.dbPath), "usage.db"));
2281
+ usageStore = await openUsageStore(join3(dirname4(cfg.dbPath), "usage.db"));
2128
2282
  startUsageSampler(usageStore);
2129
2283
  } catch (e) {
2130
2284
  logger.warn(`[${SERVER_TAG2}] usage history disabled: ${String(e)}`);
@@ -2141,7 +2295,7 @@ async function runDashboard(cfg, transportLabel) {
2141
2295
  source: getConfigSource,
2142
2296
  readFile: (pth) => {
2143
2297
  try {
2144
- return readFileSync2(pth, "utf8");
2298
+ return readFileSync3(pth, "utf8");
2145
2299
  } catch {
2146
2300
  return null;
2147
2301
  }
@@ -2190,9 +2344,15 @@ async function runDashboard(cfg, transportLabel) {
2190
2344
  const featureResp = await featureRoutes(req, url);
2191
2345
  if (featureResp)
2192
2346
  return featureResp;
2347
+ const driftResp = await driftRoutes(req, url);
2348
+ if (driftResp)
2349
+ return driftResp;
2350
+ const memoryResp = await memoryRoutes(req, url);
2351
+ if (memoryResp)
2352
+ return memoryResp;
2193
2353
  const db = getEventStore();
2194
2354
  if (!db)
2195
- return json({ error: "recorder not active" }, 503);
2355
+ return json3({ error: "recorder not active" }, 503);
2196
2356
  if (url.pathname === "/api/events" && req.method === "DELETE") {
2197
2357
  let body = {};
2198
2358
  try {
@@ -2200,7 +2360,7 @@ async function runDashboard(cfg, transportLabel) {
2200
2360
  } catch {}
2201
2361
  const ids = Array.isArray(body.ids) ? body.ids.filter((x) => typeof x === "string") : [];
2202
2362
  const removed = body.all === true ? db.clear() : db.delete(ids);
2203
- return json({ removed, total: db.total() });
2363
+ return json3({ removed, total: db.total() });
2204
2364
  }
2205
2365
  if (url.pathname === "/api/ssh-pool") {
2206
2366
  const cfg2 = getConfig();
@@ -2209,7 +2369,7 @@ async function runDashboard(cfg, transportLabel) {
2209
2369
  const totalInflight = ps.reduce((s, p) => s + p.inflight, 0);
2210
2370
  const totalIdle = ps.filter((p) => p.idle).length;
2211
2371
  const totalBusy = ps.filter((p) => p.inflight > 0).length;
2212
- return json({
2372
+ return json3({
2213
2373
  enabled,
2214
2374
  config: {
2215
2375
  keepAlive: cfg2.ssh.keepAlive,
@@ -2226,13 +2386,13 @@ async function runDashboard(cfg, transportLabel) {
2226
2386
  });
2227
2387
  }
2228
2388
  if (url.pathname === "/api/devices") {
2229
- return json(devicesPayload(db));
2389
+ return json3(devicesPayload(db));
2230
2390
  }
2231
2391
  if (url.pathname === "/api/topology") {
2232
- return json(topologyPayload());
2392
+ return json3(topologyPayload());
2233
2393
  }
2234
2394
  if (url.pathname === "/api/config") {
2235
- return json(configPayload());
2395
+ return json3(configPayload());
2236
2396
  }
2237
2397
  if (url.pathname === "/" || url.pathname === "/index.html") {
2238
2398
  return new Response(dashboardHtml(), {
@@ -2241,14 +2401,14 @@ async function runDashboard(cfg, transportLabel) {
2241
2401
  }
2242
2402
  if (url.pathname === "/api/releases/latest") {
2243
2403
  try {
2244
- return json(await fetchLatestRelease());
2404
+ return json3(await fetchLatestRelease());
2245
2405
  } catch (e) {
2246
- return json({ error: e instanceof Error ? e.message : "fetch failed" }, 502);
2406
+ return json3({ error: e instanceof Error ? e.message : "fetch failed" }, 502);
2247
2407
  }
2248
2408
  }
2249
2409
  if (url.pathname === "/api/meta") {
2250
2410
  const f = facets(db);
2251
- return json({
2411
+ return json3({
2252
2412
  ...f,
2253
2413
  version: VERSION,
2254
2414
  risks: ["READ", "WRITE", "WRITE_IDEMPOTENT", "DESTRUCTIVE", "DANGEROUS"],
@@ -2259,19 +2419,19 @@ async function runDashboard(cfg, transportLabel) {
2259
2419
  }
2260
2420
  if (url.pathname === "/api/events") {
2261
2421
  const filter = filterFromQuery(url);
2262
- return json({ events: db.query(filter), total: db.total() });
2422
+ return json3({ events: db.query(filter), total: db.total() });
2263
2423
  }
2264
2424
  const eventMatch = url.pathname.match(/^\/api\/event\/(.+)$/);
2265
2425
  if (eventMatch) {
2266
2426
  const e = db.get(decodeURIComponent(eventMatch[1]));
2267
- return e ? json(e) : json({ error: "not found" }, 404);
2427
+ return e ? json3(e) : json3({ error: "not found" }, 404);
2268
2428
  }
2269
2429
  if (url.pathname === "/api/stats") {
2270
2430
  const now = Date.now();
2271
2431
  const windowMs = Number(url.searchParams.get("window") ?? 3600000);
2272
2432
  const buckets = Number(url.searchParams.get("buckets") ?? 60);
2273
2433
  const events = db.query({ since: now - windowMs, limit: 5000 });
2274
- return json(computeStats(events, { now, windowMs, buckets }));
2434
+ return json3(computeStats(events, { now, windowMs, buckets }));
2275
2435
  }
2276
2436
  return new Response("Not Found", { status: 404 });
2277
2437
  }
@@ -2291,7 +2451,7 @@ async function runDashboard(cfg, transportLabel) {
2291
2451
  } catch (e) {
2292
2452
  const msg = e instanceof Error ? e.message : String(e);
2293
2453
  logger.error(`Dashboard request failed (${url.pathname}): ${msg}`);
2294
- return json({ error: msg }, 502);
2454
+ return json3({ error: msg }, 502);
2295
2455
  }
2296
2456
  },
2297
2457
  websocket: {
@@ -2328,6 +2488,7 @@ async function runDashboard(cfg, transportLabel) {
2328
2488
  store.close();
2329
2489
  usageStore?.close();
2330
2490
  usageStore = null;
2491
+ closeMemoryStore();
2331
2492
  }
2332
2493
  };
2333
2494
  }
@@ -2381,8 +2542,8 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2381
2542
  import { ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
2382
2543
 
2383
2544
  // src/prompts/index.ts
2384
- import { readFileSync as readFileSync3, readdirSync as readdirSync2 } from "fs";
2385
- import { join as join3 } from "path";
2545
+ import { readFileSync as readFileSync4, readdirSync as readdirSync2 } from "fs";
2546
+ import { join as join4 } from "path";
2386
2547
  import { z as z2 } from "zod";
2387
2548
  function parseFrontmatter(raw) {
2388
2549
  const match = raw.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
@@ -2453,7 +2614,7 @@ function registerPrompts(server) {
2453
2614
  for (const file of files) {
2454
2615
  let parsed;
2455
2616
  try {
2456
- parsed = parseFrontmatter(readFileSync3(join3(PROMPTS_DIR, file), "utf8"));
2617
+ parsed = parseFrontmatter(readFileSync4(join4(PROMPTS_DIR, file), "utf8"));
2457
2618
  } catch (e) {
2458
2619
  logger.warn(`Skipping prompt ${file}: ${String(e)}`);
2459
2620
  continue;
@@ -2716,8 +2877,8 @@ async function printBanner() {
2716
2877
  const colored = BRAND.multiline(art);
2717
2878
  process.stderr.write(`${colored}
2718
2879
  `);
2719
- const author = typeof package_default.author === "object" ? package_default.author : { name: String(package_default.author) };
2720
- const site = (package_default.homepage ?? "").replace(/^https?:\/\//, "");
2880
+ const author = typeof PKG_META.author === "object" ? PKG_META.author : { name: PKG_META.author };
2881
+ const site = WEBSITE_URL.replace(/^https?:\/\//, "");
2721
2882
  const w = 56;
2722
2883
  const hr = "\u2500".repeat(w);
2723
2884
  const pad = (s, vis) => `${s}${" ".repeat(Math.max(0, w - 2 - vis))}`;
@@ -2727,7 +2888,7 @@ async function printBanner() {
2727
2888
  const r1Text = ` ${CV}\u25CF${R} ${BOLD}v${VERSION}${R}${DIM} \u2502 ${CS}${r1Mid}${R}${DIM} \u2502 ${CT}${r1End}${R}`;
2728
2889
  const r1Vis = r1Label.length + 2 + r1Mid.length + 4 + r1End.length + 4;
2729
2890
  const r2Name = author.name ?? "Unknown";
2730
- const r2License = package_default.license ?? "MIT";
2891
+ const r2License = PKG_META.license ?? "MIT";
2731
2892
  const r2Text = ` ${CA}${r2Name}${R}${DIM} \xB7 ${ITALIC}${r2License}${R}${DIM} \xB7 ${CS}${site}${R}`;
2732
2893
  const r2Vis = r2Name.length + 2 + r2License.length + 4 + site.length + 4;
2733
2894
  const box = [