@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.
- package/dist/cli.js +434 -273
- package/dist/index.js +10 -141
- package/dist/shared/{cli-z6dyvbeb.js → cli-22wrpg71.js} +7935 -4199
- package/dist/shared/{cli-p080cnxa.js → cli-dq28zbns.js} +1 -1
- package/dist/shared/{library-5yx3nvtf.js → library-510csx1a.js} +1 -1
- package/dist/shared/{library-bpa5fz2e.js → library-mwcpnqqg.js} +7937 -4207
- package/dist/ui/observability.html +48 -47
- package/package.json +1 -1
- package/schemas/README.md +1 -1
- package/schemas/config.schema.json +29 -1
- package/schemas/tool-catalog.json +940 -47
- package/schemas/tools/aaa_mutate.json +10 -2
- package/schemas/tools/add_address_list_entry.json +5 -1
- package/schemas/tools/add_bfd_configuration.json +4 -1
- package/schemas/tools/add_bgp_connection.json +5 -1
- package/schemas/tools/add_bgp_template.json +4 -1
- package/schemas/tools/add_blackhole_route.json +4 -1
- package/schemas/tools/add_bridge_port.json +31 -6
- package/schemas/tools/add_bridge_vlan.json +4 -1
- package/schemas/tools/add_container_env.json +5 -1
- package/schemas/tools/add_container_mount.json +5 -1
- package/schemas/tools/add_default_route.json +5 -1
- package/schemas/tools/add_dhcp_client.json +7 -1
- package/schemas/tools/add_disk.json +3 -1
- package/schemas/tools/add_dns_regexp.json +6 -1
- package/schemas/tools/add_dns_static.json +4 -1
- package/schemas/tools/add_dot1x_client.json +5 -1
- package/schemas/tools/add_dot1x_server.json +13 -3
- package/schemas/tools/add_graphing_interface.json +3 -1
- package/schemas/tools/add_graphing_queue.json +3 -1
- package/schemas/tools/add_igmp_proxy_interface.json +5 -1
- package/schemas/tools/add_ip_address.json +5 -1
- package/schemas/tools/add_ipv6_address.json +5 -1
- package/schemas/tools/add_ipv6_address_list_entry.json +5 -1
- package/schemas/tools/add_ipv6_default_route.json +4 -1
- package/schemas/tools/add_ipv6_dhcp_binding.json +4 -1
- package/schemas/tools/add_ipv6_dhcp_client.json +15 -3
- package/schemas/tools/add_ipv6_dhcp_option.json +5 -1
- package/schemas/tools/add_ipv6_dhcp_relay.json +6 -1
- package/schemas/tools/add_ipv6_nd.json +9 -2
- package/schemas/tools/add_ipv6_nd_prefix.json +4 -1
- package/schemas/tools/add_ipv6_route.json +10 -2
- package/schemas/tools/add_logging_action.json +12 -2
- package/schemas/tools/add_logging_rule.json +4 -1
- package/schemas/tools/add_netwatch.json +10 -2
- package/schemas/tools/add_ospf_area.json +11 -2
- package/schemas/tools/add_ospf_area_range.json +5 -1
- package/schemas/tools/add_ospf_instance.json +5 -1
- package/schemas/tools/add_ospf_interface_template.json +19 -3
- package/schemas/tools/add_ovpn_server.json +17 -4
- package/schemas/tools/add_pimsm_instance.json +9 -2
- package/schemas/tools/add_pimsm_interface_template.json +5 -1
- package/schemas/tools/add_pimsm_rp.json +5 -1
- package/schemas/tools/add_radius_server.json +4 -1
- package/schemas/tools/add_rip_instance.json +4 -1
- package/schemas/tools/add_rip_interface_template.json +5 -1
- package/schemas/tools/add_rip_static_neighbor.json +4 -1
- package/schemas/tools/add_romon_port.json +4 -1
- package/schemas/tools/add_route.json +5 -1
- package/schemas/tools/add_routing_filter_num_list.json +4 -1
- package/schemas/tools/add_routing_filter_rule.json +5 -1
- package/schemas/tools/add_routing_id.json +4 -1
- package/schemas/tools/add_routing_rule.json +10 -2
- package/schemas/tools/add_routing_table.json +5 -1
- package/schemas/tools/add_rpki_session.json +6 -1
- package/schemas/tools/add_script.json +5 -1
- package/schemas/tools/add_switch_port_isolation.json +4 -1
- package/schemas/tools/add_switch_rule.json +10 -2
- package/schemas/tools/add_traffic_generator_port.json +5 -1
- package/schemas/tools/add_traffic_generator_stream.json +5 -1
- package/schemas/tools/add_traffic_monitor.json +17 -3
- package/schemas/tools/add_user.json +6 -1
- package/schemas/tools/add_user_group.json +4 -1
- package/schemas/tools/add_user_manager_limitation.json +3 -1
- package/schemas/tools/add_user_manager_profile.json +7 -2
- package/schemas/tools/add_user_manager_router.json +6 -1
- package/schemas/tools/add_user_manager_user.json +5 -1
- package/schemas/tools/add_wireguard_peer.json +6 -1
- package/schemas/tools/allow_device.json +3 -1
- package/schemas/tools/apply_plan.json +3 -1
- package/schemas/tools/apply_traffic_shaping.json +10 -2
- package/schemas/tools/assign_user_manager_profile.json +4 -1
- package/schemas/tools/audit_certificate_expiry.json +3 -1
- package/schemas/tools/audit_fleet.json +33 -0
- package/schemas/tools/audit_remediate.json +46 -0
- package/schemas/tools/backup_info.json +3 -1
- package/schemas/tools/bandwidth_test.json +15 -3
- package/schemas/tools/block_device.json +3 -1
- package/schemas/tools/build_guest_hotspot.json +7 -1
- package/schemas/tools/build_wireguard_mesh.json +4 -1
- package/schemas/tools/capture_config_snapshot.json +5 -1
- package/schemas/tools/change_password.json +4 -1
- package/schemas/tools/check_route_path.json +3 -1
- package/schemas/tools/config_check_drift.json +26 -0
- package/schemas/tools/config_promote_drift.json +16 -0
- package/schemas/tools/config_reconcile.json +14 -0
- package/schemas/tools/config_set_baseline.json +30 -0
- package/schemas/tools/correlate_events.json +29 -0
- package/schemas/tools/create_backup.json +4 -1
- package/schemas/tools/create_bridge.json +28 -5
- package/schemas/tools/create_certificate.json +6 -1
- package/schemas/tools/create_dhcp_network.json +4 -1
- package/schemas/tools/create_dhcp_pool.json +4 -1
- package/schemas/tools/create_dhcp_server.json +22 -4
- package/schemas/tools/create_eoip_tunnel.json +17 -3
- package/schemas/tools/create_export.json +17 -3
- package/schemas/tools/create_filter_rule.json +11 -2
- package/schemas/tools/create_gre_tunnel.json +9 -2
- package/schemas/tools/create_ip_pool.json +4 -1
- package/schemas/tools/create_ipip_tunnel.json +9 -2
- package/schemas/tools/create_ipsec_identity.json +21 -4
- package/schemas/tools/create_ipsec_peer.json +10 -2
- package/schemas/tools/create_ipsec_policy.json +15 -3
- package/schemas/tools/create_ipsec_profile.json +5 -1
- package/schemas/tools/create_ipsec_proposal.json +7 -1
- package/schemas/tools/create_ipv6_dhcp_server.json +7 -1
- package/schemas/tools/create_ipv6_filter_rule.json +11 -2
- package/schemas/tools/create_ipv6_mangle_rule.json +13 -2
- package/schemas/tools/create_ipv6_nat_rule.json +10 -2
- package/schemas/tools/create_ipv6_pool.json +5 -1
- package/schemas/tools/create_ipv6_raw_rule.json +19 -3
- package/schemas/tools/create_l2tp_client.json +12 -2
- package/schemas/tools/create_local_backup.json +6 -1
- package/schemas/tools/create_mangle_rule.json +13 -2
- package/schemas/tools/create_nat_rule.json +10 -2
- package/schemas/tools/create_ovpn_client.json +18 -4
- package/schemas/tools/create_ppp_profile.json +30 -6
- package/schemas/tools/create_ppp_secret.json +14 -2
- package/schemas/tools/create_pptp_client.json +7 -1
- package/schemas/tools/create_queue_tree.json +5 -1
- package/schemas/tools/create_queue_type.json +28 -5
- package/schemas/tools/create_scheduler.json +5 -1
- package/schemas/tools/create_simple_queue.json +5 -1
- package/schemas/tools/create_sstp_client.json +11 -2
- package/schemas/tools/create_vlan_interface.json +19 -3
- package/schemas/tools/create_vxlan_tunnel.json +17 -3
- package/schemas/tools/create_wireguard_interface.json +4 -1
- package/schemas/tools/create_wireless_interface.json +37 -6
- package/schemas/tools/create_wireless_security_profile.json +3 -1
- package/schemas/tools/delete_local_backup.json +3 -1
- package/schemas/tools/delete_s3_backup.json +3 -1
- package/schemas/tools/describe_tool.json +3 -1
- package/schemas/tools/design_network_segment.json +10 -1
- package/schemas/tools/diagnose.json +37 -0
- package/schemas/tools/diff_config_snapshots.json +6 -1
- package/schemas/tools/disable_address_list_entry.json +3 -1
- package/schemas/tools/disable_dns_static.json +3 -1
- package/schemas/tools/disable_filter_rule.json +3 -1
- package/schemas/tools/disable_interface.json +3 -1
- package/schemas/tools/disable_ip_service.json +3 -1
- package/schemas/tools/disable_ipv6_address_list_entry.json +3 -1
- package/schemas/tools/disable_ipv6_dhcp_relay.json +3 -1
- package/schemas/tools/disable_ipv6_filter_rule.json +3 -1
- package/schemas/tools/disable_ipv6_mangle_rule.json +3 -1
- package/schemas/tools/disable_ipv6_nat_rule.json +3 -1
- package/schemas/tools/disable_ipv6_raw_rule.json +3 -1
- package/schemas/tools/disable_ipv6_route.json +3 -1
- package/schemas/tools/disable_l2tp_client.json +3 -1
- package/schemas/tools/disable_mangle_rule.json +3 -1
- package/schemas/tools/disable_nat_rule.json +3 -1
- package/schemas/tools/disable_ovpn_client.json +3 -1
- package/schemas/tools/disable_ovpn_server.json +3 -1
- package/schemas/tools/disable_queue_tree.json +3 -1
- package/schemas/tools/disable_radius_server.json +3 -1
- package/schemas/tools/disable_route.json +3 -1
- package/schemas/tools/disable_scheduler.json +3 -1
- package/schemas/tools/disable_simple_queue.json +3 -1
- package/schemas/tools/disable_sstp_client.json +3 -1
- package/schemas/tools/disable_switch_rule.json +3 -1
- package/schemas/tools/disable_traffic_monitor.json +3 -1
- package/schemas/tools/disable_user.json +3 -1
- package/schemas/tools/disable_wireguard_interface.json +3 -1
- package/schemas/tools/disable_wireguard_peer.json +3 -1
- package/schemas/tools/disable_wireless_interface.json +3 -1
- package/schemas/tools/disconnect_ppp_active.json +3 -1
- package/schemas/tools/disconnect_user.json +3 -1
- package/schemas/tools/download_backup_from_s3.json +3 -1
- package/schemas/tools/download_file.json +3 -1
- package/schemas/tools/eject_disk.json +3 -1
- package/schemas/tools/enable_address_list_entry.json +3 -1
- package/schemas/tools/enable_dns_static.json +3 -1
- package/schemas/tools/enable_filter_rule.json +3 -1
- package/schemas/tools/enable_interface.json +3 -1
- package/schemas/tools/enable_ip_service.json +3 -1
- package/schemas/tools/enable_ipv6_address_list_entry.json +3 -1
- package/schemas/tools/enable_ipv6_dhcp_relay.json +3 -1
- package/schemas/tools/enable_ipv6_filter_rule.json +3 -1
- package/schemas/tools/enable_ipv6_mangle_rule.json +3 -1
- package/schemas/tools/enable_ipv6_nat_rule.json +3 -1
- package/schemas/tools/enable_ipv6_raw_rule.json +3 -1
- package/schemas/tools/enable_ipv6_route.json +3 -1
- package/schemas/tools/enable_l2tp_client.json +3 -1
- package/schemas/tools/enable_mangle_rule.json +3 -1
- package/schemas/tools/enable_nat_rule.json +3 -1
- package/schemas/tools/enable_ovpn_client.json +3 -1
- package/schemas/tools/enable_ovpn_server.json +3 -1
- package/schemas/tools/enable_queue_tree.json +3 -1
- package/schemas/tools/enable_radius_server.json +3 -1
- package/schemas/tools/enable_route.json +3 -1
- package/schemas/tools/enable_scheduler.json +3 -1
- package/schemas/tools/enable_simple_queue.json +3 -1
- package/schemas/tools/enable_sstp_client.json +3 -1
- package/schemas/tools/enable_switch_rule.json +3 -1
- package/schemas/tools/enable_traffic_monitor.json +3 -1
- package/schemas/tools/enable_user.json +3 -1
- package/schemas/tools/enable_wireguard_interface.json +3 -1
- package/schemas/tools/enable_wireguard_peer.json +3 -1
- package/schemas/tools/enable_wireless_interface.json +3 -1
- package/schemas/tools/expand_ip_pool.json +4 -1
- package/schemas/tools/export_logs.json +7 -2
- package/schemas/tools/export_section.json +5 -1
- package/schemas/tools/export_ssh_host_key.json +3 -1
- package/schemas/tools/find_tools.json +3 -1
- package/schemas/tools/firewall_audit.json +4 -1
- package/schemas/tools/firmware_check.json +32 -0
- package/schemas/tools/firmware_stage.json +17 -0
- package/schemas/tools/firmware_status.json +20 -0
- package/schemas/tools/firmware_upgrade.json +35 -0
- package/schemas/tools/flood_ping.json +4 -1
- package/schemas/tools/forecast_link_saturation.json +5 -1
- package/schemas/tools/format_disk.json +10 -2
- package/schemas/tools/forward_port.json +10 -2
- package/schemas/tools/generate_hotspot_vouchers.json +5 -1
- package/schemas/tools/get_aaa_section.json +3 -1
- package/schemas/tools/get_address_list_entry.json +3 -1
- package/schemas/tools/get_bgp_connection.json +3 -1
- package/schemas/tools/get_bridge.json +3 -1
- package/schemas/tools/get_certificate.json +3 -1
- package/schemas/tools/get_config_snapshot.json +4 -1
- package/schemas/tools/get_device_traffic.json +3 -1
- package/schemas/tools/get_dhcp_client.json +3 -1
- package/schemas/tools/get_dhcp_server.json +3 -1
- package/schemas/tools/get_disk.json +3 -1
- package/schemas/tools/get_dns_static.json +3 -1
- package/schemas/tools/get_dot1x_client.json +3 -1
- package/schemas/tools/get_dot1x_server.json +3 -1
- package/schemas/tools/get_eoip_tunnel.json +3 -1
- package/schemas/tools/get_filter_rule.json +3 -1
- package/schemas/tools/get_gre_tunnel.json +3 -1
- package/schemas/tools/get_interface.json +3 -1
- package/schemas/tools/get_ip_address.json +3 -1
- package/schemas/tools/get_ip_pool.json +3 -1
- package/schemas/tools/get_ip_service.json +3 -1
- package/schemas/tools/get_ipip_tunnel.json +3 -1
- package/schemas/tools/get_ipsec_peer.json +3 -1
- package/schemas/tools/get_ipsec_profile.json +3 -1
- package/schemas/tools/get_ipsec_proposal.json +3 -1
- package/schemas/tools/get_ipv6_address.json +3 -1
- package/schemas/tools/get_ipv6_address_list_entry.json +3 -1
- package/schemas/tools/get_ipv6_dhcp_client.json +3 -1
- package/schemas/tools/get_ipv6_dhcp_relay.json +3 -1
- package/schemas/tools/get_ipv6_dhcp_server.json +3 -1
- package/schemas/tools/get_ipv6_filter_rule.json +3 -1
- package/schemas/tools/get_ipv6_mangle_rule.json +3 -1
- package/schemas/tools/get_ipv6_nat_rule.json +3 -1
- package/schemas/tools/get_ipv6_nd.json +3 -1
- package/schemas/tools/get_ipv6_neighbor.json +3 -1
- package/schemas/tools/get_ipv6_pool.json +3 -1
- package/schemas/tools/get_ipv6_raw_rule.json +3 -1
- package/schemas/tools/get_ipv6_route.json +3 -1
- package/schemas/tools/get_l2tp_client.json +3 -1
- package/schemas/tools/get_local_backup.json +3 -1
- package/schemas/tools/get_logs.json +9 -2
- package/schemas/tools/get_logs_by_severity.json +10 -2
- package/schemas/tools/get_logs_by_topic.json +3 -1
- package/schemas/tools/get_mangle_rule.json +3 -1
- package/schemas/tools/get_nat_rule.json +3 -1
- package/schemas/tools/get_netwatch.json +3 -1
- package/schemas/tools/get_ovpn_client.json +3 -1
- package/schemas/tools/get_poe_monitor.json +3 -1
- package/schemas/tools/get_poe_settings.json +3 -1
- package/schemas/tools/get_port.json +3 -1
- package/schemas/tools/get_ppp_profile.json +3 -1
- package/schemas/tools/get_ppp_secret.json +3 -1
- package/schemas/tools/get_pptp_client.json +3 -1
- package/schemas/tools/get_queue_interface.json +3 -1
- package/schemas/tools/get_queue_tree.json +3 -1
- package/schemas/tools/get_queue_type.json +3 -1
- package/schemas/tools/get_radius_server.json +3 -1
- package/schemas/tools/get_route.json +3 -1
- package/schemas/tools/get_routing_id.json +3 -1
- package/schemas/tools/get_routing_rule.json +3 -1
- package/schemas/tools/get_routing_table.json +4 -1
- package/schemas/tools/get_routing_table_def.json +3 -1
- package/schemas/tools/get_scheduler.json +3 -1
- package/schemas/tools/get_simple_queue.json +3 -1
- package/schemas/tools/get_sstp_client.json +3 -1
- package/schemas/tools/get_switch.json +3 -1
- package/schemas/tools/get_switch_port.json +3 -1
- package/schemas/tools/get_switch_port_isolation.json +3 -1
- package/schemas/tools/get_switch_rule.json +3 -1
- package/schemas/tools/get_traffic_monitor.json +3 -1
- package/schemas/tools/get_user.json +3 -1
- package/schemas/tools/get_user_group.json +3 -1
- package/schemas/tools/get_user_manager_user.json +3 -1
- package/schemas/tools/get_vlan_interface.json +3 -1
- package/schemas/tools/get_vxlan_tunnel.json +3 -1
- package/schemas/tools/get_wireguard_interface.json +3 -1
- package/schemas/tools/get_wireguard_peer.json +3 -1
- package/schemas/tools/get_wireless_interface.json +3 -1
- package/schemas/tools/get_wireless_security_profile.json +3 -1
- package/schemas/tools/harden_firewall.json +6 -1
- package/schemas/tools/import_certificate.json +3 -1
- package/schemas/tools/import_configuration.json +5 -1
- package/schemas/tools/import_ssh_host_key.json +3 -1
- package/schemas/tools/invoke_tool.json +3 -1
- package/schemas/tools/ip_scan.json +3 -1
- package/schemas/tools/list_address_lists.json +3 -1
- package/schemas/tools/list_backups.json +3 -1
- package/schemas/tools/list_bfd_sessions.json +3 -1
- package/schemas/tools/list_bgp_sessions.json +3 -1
- package/schemas/tools/list_compliance_checks.json +24 -0
- package/schemas/tools/list_config_snapshots.json +3 -1
- package/schemas/tools/list_connected_devices.json +4 -1
- package/schemas/tools/list_containers.json +3 -1
- package/schemas/tools/list_dhcp_servers.json +4 -1
- package/schemas/tools/list_disks.json +3 -1
- package/schemas/tools/list_dns_static.json +4 -1
- package/schemas/tools/list_dot1x_clients.json +3 -1
- package/schemas/tools/list_dot1x_servers.json +3 -1
- package/schemas/tools/list_filter_rules.json +5 -1
- package/schemas/tools/list_graphing.json +8 -2
- package/schemas/tools/list_interfaces.json +4 -1
- package/schemas/tools/list_ip_addresses.json +4 -1
- package/schemas/tools/list_ip_pools.json +3 -1
- package/schemas/tools/list_ipv6_address_lists.json +3 -1
- package/schemas/tools/list_ipv6_addresses.json +5 -1
- package/schemas/tools/list_ipv6_dhcp_bindings.json +3 -1
- package/schemas/tools/list_ipv6_dhcp_clients.json +4 -1
- package/schemas/tools/list_ipv6_dhcp_relays.json +3 -1
- package/schemas/tools/list_ipv6_dhcp_servers.json +4 -1
- package/schemas/tools/list_ipv6_filter_rules.json +5 -1
- package/schemas/tools/list_ipv6_mangle_rules.json +5 -1
- package/schemas/tools/list_ipv6_nat_rules.json +5 -1
- package/schemas/tools/list_ipv6_nd.json +3 -1
- package/schemas/tools/list_ipv6_nd_prefixes.json +3 -1
- package/schemas/tools/list_ipv6_neighbors.json +3 -1
- package/schemas/tools/list_ipv6_raw_rules.json +5 -1
- package/schemas/tools/list_ipv6_routes.json +5 -1
- package/schemas/tools/list_mangle_rules.json +5 -1
- package/schemas/tools/list_nat_rules.json +4 -1
- package/schemas/tools/list_ppp_secrets.json +8 -1
- package/schemas/tools/list_queue_trees.json +4 -1
- package/schemas/tools/list_routes.json +6 -1
- package/schemas/tools/list_routing_nexthops.json +3 -1
- package/schemas/tools/list_routing_rules.json +3 -1
- package/schemas/tools/list_s3_backups.json +3 -1
- package/schemas/tools/list_simple_queues.json +4 -1
- package/schemas/tools/list_switch_rules.json +3 -1
- package/schemas/tools/list_traffic_monitors.json +3 -1
- package/schemas/tools/list_user_manager_sessions.json +3 -1
- package/schemas/tools/list_user_ssh_keys.json +3 -1
- package/schemas/tools/list_users.json +4 -1
- package/schemas/tools/list_vlan_interfaces.json +3 -1
- package/schemas/tools/list_wireguard_interfaces.json +4 -1
- package/schemas/tools/list_wireguard_peers.json +3 -1
- package/schemas/tools/list_wireless_interfaces.json +4 -1
- package/schemas/tools/make_device_static.json +3 -1
- package/schemas/tools/memory_add_observations.json +32 -0
- package/schemas/tools/memory_create_entities.json +36 -0
- package/schemas/tools/memory_create_relations.json +33 -0
- package/schemas/tools/memory_delete_entities.json +17 -0
- package/schemas/tools/memory_delete_observations.json +32 -0
- package/schemas/tools/memory_delete_relations.json +33 -0
- package/schemas/tools/memory_open_nodes.json +17 -0
- package/schemas/tools/memory_read_graph.json +7 -0
- package/schemas/tools/memory_search_nodes.json +19 -0
- package/schemas/tools/mirror_traffic_to_capture.json +12 -2
- package/schemas/tools/monitor_logs.json +3 -1
- package/schemas/tools/move_filter_rule.json +4 -1
- package/schemas/tools/move_ipv6_filter_rule.json +4 -1
- package/schemas/tools/move_ipv6_mangle_rule.json +4 -1
- package/schemas/tools/move_ipv6_nat_rule.json +4 -1
- package/schemas/tools/move_ipv6_raw_rule.json +4 -1
- package/schemas/tools/move_mangle_rule.json +4 -1
- package/schemas/tools/move_nat_rule.json +4 -1
- package/schemas/tools/onboard_wireguard_user.json +8 -1
- package/schemas/tools/packet_capture_status.json +3 -1
- package/schemas/tools/ping.json +4 -1
- package/schemas/tools/profile_cpu.json +3 -1
- package/schemas/tools/reboot_system.json +3 -1
- package/schemas/tools/release_ipv6_dhcp_client.json +3 -1
- package/schemas/tools/remove_address_list_entry.json +3 -1
- package/schemas/tools/remove_bfd_configuration.json +3 -1
- package/schemas/tools/remove_bgp_connection.json +3 -1
- package/schemas/tools/remove_bgp_template.json +3 -1
- package/schemas/tools/remove_bridge.json +3 -1
- package/schemas/tools/remove_bridge_port.json +3 -1
- package/schemas/tools/remove_certificate.json +3 -1
- package/schemas/tools/remove_config_snapshot.json +3 -1
- package/schemas/tools/remove_container_env.json +4 -1
- package/schemas/tools/remove_container_mount.json +3 -1
- package/schemas/tools/remove_device_lease.json +3 -1
- package/schemas/tools/remove_dhcp_client.json +3 -1
- package/schemas/tools/remove_dhcp_server.json +3 -1
- package/schemas/tools/remove_disk.json +3 -1
- package/schemas/tools/remove_dns_static.json +3 -1
- package/schemas/tools/remove_dot1x_client.json +3 -1
- package/schemas/tools/remove_dot1x_server.json +3 -1
- package/schemas/tools/remove_eoip_tunnel.json +3 -1
- package/schemas/tools/remove_file.json +3 -1
- package/schemas/tools/remove_filter_rule.json +3 -1
- package/schemas/tools/remove_graphing.json +9 -2
- package/schemas/tools/remove_gre_tunnel.json +3 -1
- package/schemas/tools/remove_igmp_proxy_interface.json +3 -1
- package/schemas/tools/remove_ip_address.json +3 -1
- package/schemas/tools/remove_ip_pool.json +3 -1
- package/schemas/tools/remove_ipip_tunnel.json +3 -1
- package/schemas/tools/remove_ipsec_identity.json +3 -1
- package/schemas/tools/remove_ipsec_peer.json +3 -1
- package/schemas/tools/remove_ipsec_policy.json +3 -1
- package/schemas/tools/remove_ipsec_profile.json +3 -1
- package/schemas/tools/remove_ipsec_proposal.json +3 -1
- package/schemas/tools/remove_ipv6_address.json +3 -1
- package/schemas/tools/remove_ipv6_address_list_entry.json +3 -1
- package/schemas/tools/remove_ipv6_dhcp_binding.json +3 -1
- package/schemas/tools/remove_ipv6_dhcp_client.json +3 -1
- package/schemas/tools/remove_ipv6_dhcp_option.json +3 -1
- package/schemas/tools/remove_ipv6_dhcp_relay.json +3 -1
- package/schemas/tools/remove_ipv6_dhcp_server.json +3 -1
- package/schemas/tools/remove_ipv6_filter_rule.json +3 -1
- package/schemas/tools/remove_ipv6_mangle_rule.json +3 -1
- package/schemas/tools/remove_ipv6_nat_rule.json +3 -1
- package/schemas/tools/remove_ipv6_nd.json +3 -1
- package/schemas/tools/remove_ipv6_nd_prefix.json +3 -1
- package/schemas/tools/remove_ipv6_neighbor.json +3 -1
- package/schemas/tools/remove_ipv6_pool.json +3 -1
- package/schemas/tools/remove_ipv6_raw_rule.json +3 -1
- package/schemas/tools/remove_ipv6_route.json +3 -1
- package/schemas/tools/remove_l2tp_client.json +3 -1
- package/schemas/tools/remove_logging_action.json +3 -1
- package/schemas/tools/remove_logging_rule.json +3 -1
- package/schemas/tools/remove_mangle_rule.json +3 -1
- package/schemas/tools/remove_nat_rule.json +3 -1
- package/schemas/tools/remove_netwatch.json +3 -1
- package/schemas/tools/remove_ospf_area.json +3 -1
- package/schemas/tools/remove_ospf_area_range.json +3 -1
- package/schemas/tools/remove_ospf_instance.json +3 -1
- package/schemas/tools/remove_ospf_interface_template.json +3 -1
- package/schemas/tools/remove_ovpn_client.json +3 -1
- package/schemas/tools/remove_ovpn_server.json +3 -1
- package/schemas/tools/remove_pimsm_instance.json +3 -1
- package/schemas/tools/remove_pimsm_interface_template.json +3 -1
- package/schemas/tools/remove_pimsm_rp.json +3 -1
- package/schemas/tools/remove_ppp_profile.json +3 -1
- package/schemas/tools/remove_ppp_secret.json +3 -1
- package/schemas/tools/remove_pptp_client.json +3 -1
- package/schemas/tools/remove_queue_tree.json +3 -1
- package/schemas/tools/remove_queue_type.json +3 -1
- package/schemas/tools/remove_radius_server.json +3 -1
- package/schemas/tools/remove_rip_instance.json +3 -1
- package/schemas/tools/remove_rip_interface_template.json +3 -1
- package/schemas/tools/remove_rip_static_neighbor.json +3 -1
- package/schemas/tools/remove_romon_port.json +3 -1
- package/schemas/tools/remove_route.json +3 -1
- package/schemas/tools/remove_routing_filter_num_list.json +3 -1
- package/schemas/tools/remove_routing_filter_rule.json +3 -1
- package/schemas/tools/remove_routing_id.json +3 -1
- package/schemas/tools/remove_routing_rule.json +3 -1
- package/schemas/tools/remove_routing_table.json +3 -1
- package/schemas/tools/remove_rpki_session.json +3 -1
- package/schemas/tools/remove_scheduler.json +3 -1
- package/schemas/tools/remove_script.json +3 -1
- package/schemas/tools/remove_simple_queue.json +3 -1
- package/schemas/tools/remove_sstp_client.json +3 -1
- package/schemas/tools/remove_switch_port_isolation.json +3 -1
- package/schemas/tools/remove_switch_rule.json +3 -1
- package/schemas/tools/remove_threat_feed.json +3 -1
- package/schemas/tools/remove_time_policy.json +3 -1
- package/schemas/tools/remove_traffic_generator_port.json +3 -1
- package/schemas/tools/remove_traffic_generator_stream.json +3 -1
- package/schemas/tools/remove_traffic_monitor.json +3 -1
- package/schemas/tools/remove_user.json +3 -1
- package/schemas/tools/remove_user_group.json +3 -1
- package/schemas/tools/remove_user_manager_limitation.json +3 -1
- package/schemas/tools/remove_user_manager_profile.json +3 -1
- package/schemas/tools/remove_user_manager_router.json +3 -1
- package/schemas/tools/remove_user_manager_user.json +3 -1
- package/schemas/tools/remove_user_ssh_key.json +3 -1
- package/schemas/tools/remove_vlan_interface.json +3 -1
- package/schemas/tools/remove_vxlan_tunnel.json +3 -1
- package/schemas/tools/remove_wireguard_interface.json +3 -1
- package/schemas/tools/remove_wireguard_peer.json +3 -1
- package/schemas/tools/remove_wireless_access_list_entry.json +3 -1
- package/schemas/tools/remove_wireless_interface.json +3 -1
- package/schemas/tools/remove_wireless_security_profile.json +3 -1
- package/schemas/tools/rename_local_backup.json +4 -1
- package/schemas/tools/renew_dhcp_client.json +4 -1
- package/schemas/tools/renew_ipv6_dhcp_client.json +3 -1
- package/schemas/tools/request_letsencrypt_certificate.json +3 -1
- package/schemas/tools/reset_configuration.json +3 -1
- package/schemas/tools/resolve_dns.json +3 -1
- package/schemas/tools/restore_backup.json +3 -1
- package/schemas/tools/restore_local_backup.json +4 -1
- package/schemas/tools/revoke_wireguard_user.json +3 -1
- package/schemas/tools/run_compliance_audit.json +46 -0
- package/schemas/tools/run_failover_drill.json +11 -2
- package/schemas/tools/run_routeros_command.json +3 -1
- package/schemas/tools/run_script.json +3 -1
- package/schemas/tools/s3_backup_info.json +3 -1
- package/schemas/tools/save_sniffer.json +3 -1
- package/schemas/tools/scan_wireless_networks.json +4 -1
- package/schemas/tools/search_logs.json +4 -1
- package/schemas/tools/send_sms.json +5 -1
- package/schemas/tools/set_aaa_settings.json +8 -2
- package/schemas/tools/set_bfd_configuration_enabled.json +4 -1
- package/schemas/tools/set_bgp_connection_enabled.json +4 -1
- package/schemas/tools/set_device_ip.json +4 -1
- package/schemas/tools/set_device_label.json +4 -1
- package/schemas/tools/set_device_limits.json +3 -1
- package/schemas/tools/set_dhcp_client.json +3 -1
- package/schemas/tools/set_disk.json +3 -1
- package/schemas/tools/set_dns_servers.json +6 -1
- package/schemas/tools/set_igmp_proxy_interface_enabled.json +4 -1
- package/schemas/tools/set_ip_cloud_advanced.json +3 -1
- package/schemas/tools/set_ip_service.json +3 -1
- package/schemas/tools/set_l2tp_server.json +9 -2
- package/schemas/tools/set_leds_settings.json +6 -1
- package/schemas/tools/set_neighbor_discovery_settings.json +5 -1
- package/schemas/tools/set_ntp_client.json +6 -1
- package/schemas/tools/set_ovpn_server.json +12 -3
- package/schemas/tools/set_port.json +13 -3
- package/schemas/tools/set_routing_filter_rule_enabled.json +4 -1
- package/schemas/tools/set_routing_id_enabled.json +4 -1
- package/schemas/tools/set_routing_rule_enabled.json +4 -1
- package/schemas/tools/set_routing_table_enabled.json +4 -1
- package/schemas/tools/set_rpki_session_enabled.json +4 -1
- package/schemas/tools/set_ssh_settings.json +10 -2
- package/schemas/tools/set_sstp_server.json +4 -1
- package/schemas/tools/set_system_identity.json +3 -1
- package/schemas/tools/set_time_policy.json +7 -1
- package/schemas/tools/set_user_ssh_keys.json +4 -1
- package/schemas/tools/set_wireless_security_profile.json +4 -1
- package/schemas/tools/setup_port_knock.json +7 -1
- package/schemas/tools/setup_wan_failover.json +11 -2
- package/schemas/tools/setup_wan_loadbalance.json +10 -2
- package/schemas/tools/share_disk.json +3 -1
- package/schemas/tools/shutdown_system.json +3 -1
- package/schemas/tools/sign_certificate.json +3 -1
- package/schemas/tools/speed_test.json +10 -2
- package/schemas/tools/start_packet_capture.json +4 -1
- package/schemas/tools/subscribe_threat_feed.json +8 -1
- package/schemas/tools/suggest_fix.json +33 -0
- package/schemas/tools/test_dns_query.json +4 -1
- package/schemas/tools/test_ssh_from_device.json +6 -1
- package/schemas/tools/test_ssh_to_host.json +6 -1
- package/schemas/tools/trace_path.json +25 -0
- package/schemas/tools/traceroute.json +4 -1
- package/schemas/tools/tune_wifi_channel.json +5 -1
- package/schemas/tools/update_bfd_configuration.json +3 -1
- package/schemas/tools/update_bgp_connection.json +3 -1
- package/schemas/tools/update_bridge.json +26 -5
- package/schemas/tools/update_dns_static.json +3 -1
- package/schemas/tools/update_dot1x_client.json +3 -1
- package/schemas/tools/update_dot1x_server.json +12 -3
- package/schemas/tools/update_filter_rule.json +3 -1
- package/schemas/tools/update_igmp_proxy_interface.json +3 -1
- package/schemas/tools/update_ip_pool.json +3 -1
- package/schemas/tools/update_ipsec_peer.json +8 -2
- package/schemas/tools/update_ipsec_profile.json +3 -1
- package/schemas/tools/update_ipsec_proposal.json +3 -1
- package/schemas/tools/update_ipv6_filter_rule.json +3 -1
- package/schemas/tools/update_ipv6_mangle_rule.json +3 -1
- package/schemas/tools/update_ipv6_nat_rule.json +3 -1
- package/schemas/tools/update_ipv6_nd.json +8 -2
- package/schemas/tools/update_ipv6_pool.json +3 -1
- package/schemas/tools/update_ipv6_raw_rule.json +3 -1
- package/schemas/tools/update_ipv6_settings.json +15 -3
- package/schemas/tools/update_mac_ping.json +3 -1
- package/schemas/tools/update_mac_server.json +3 -1
- package/schemas/tools/update_mac_winbox.json +3 -1
- package/schemas/tools/update_mangle_rule.json +3 -1
- package/schemas/tools/update_nat_rule.json +3 -1
- package/schemas/tools/update_ospf_instance.json +3 -1
- package/schemas/tools/update_ovpn_server.json +15 -4
- package/schemas/tools/update_ppp_profile.json +30 -6
- package/schemas/tools/update_ppp_secret.json +11 -2
- package/schemas/tools/update_queue_interface.json +4 -1
- package/schemas/tools/update_queue_tree.json +3 -1
- package/schemas/tools/update_queue_type.json +3 -1
- package/schemas/tools/update_radius_server.json +7 -2
- package/schemas/tools/update_rip_instance.json +3 -1
- package/schemas/tools/update_route.json +3 -1
- package/schemas/tools/update_routing_filter_rule.json +3 -1
- package/schemas/tools/update_routing_id.json +3 -1
- package/schemas/tools/update_routing_rule.json +9 -2
- package/schemas/tools/update_routing_settings.json +10 -2
- package/schemas/tools/update_routing_table.json +3 -1
- package/schemas/tools/update_rpki_session.json +3 -1
- package/schemas/tools/update_simple_queue.json +3 -1
- package/schemas/tools/update_sniffer_settings.json +9 -2
- package/schemas/tools/update_switch.json +3 -1
- package/schemas/tools/update_switch_port.json +14 -3
- package/schemas/tools/update_switch_port_isolation.json +3 -1
- package/schemas/tools/update_switch_rule.json +8 -2
- package/schemas/tools/update_traffic_monitor.json +12 -3
- package/schemas/tools/update_user.json +3 -1
- package/schemas/tools/update_user_group.json +3 -1
- package/schemas/tools/update_user_manager_user.json +3 -1
- package/schemas/tools/update_vlan_interface.json +14 -3
- package/schemas/tools/update_wireguard_interface.json +3 -1
- package/schemas/tools/update_wireguard_peer.json +3 -1
- package/schemas/tools/update_wireless_interface.json +36 -6
- package/schemas/tools/upload_backup_to_s3.json +3 -1
- package/schemas/tools/upload_file.json +4 -1
- 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-
|
|
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
|
|
104
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
99
105
|
import { homedir, networkInterfaces } from "os";
|
|
100
|
-
import { dirname as dirname4, join as
|
|
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
|
-
|
|
1281
|
-
|
|
1282
|
-
var
|
|
1283
|
-
var
|
|
1284
|
-
var
|
|
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
|
|
1328
|
-
function
|
|
1329
|
-
return new Response(JSON.stringify(body), { status, 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
|
|
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
|
|
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
|
|
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
|
|
1700
|
+
return json3({ ok: false, errors: issues(parsed.error) }, 400);
|
|
1547
1701
|
const status = await probeDevice(`config-test:${name}`, parsed.data);
|
|
1548
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1756
|
+
return json3({ error: "id required" }, 400);
|
|
1603
1757
|
try {
|
|
1604
1758
|
const v = readVersion(id);
|
|
1605
|
-
return
|
|
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
|
|
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
|
|
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
|
|
1777
|
+
return json3({ summary: d.summary, unified: d.unified });
|
|
1624
1778
|
} catch {
|
|
1625
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1662
|
-
return deleteVersion(b.id) ?
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
1905
|
+
return json3({ error: "ip required" }, 400);
|
|
1752
1906
|
const ctx = createContext(undefined, deviceFromQuery());
|
|
1753
|
-
return
|
|
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
|
|
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
|
|
1765
|
-
return
|
|
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
|
|
1922
|
+
return json3({ error: "mac required" }, 400);
|
|
1769
1923
|
const run = async (op) => {
|
|
1770
1924
|
const r = await op;
|
|
1771
|
-
return
|
|
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
|
|
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
|
|
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
|
|
1955
|
+
return json3({ entities: AAA_ENTITIES });
|
|
1802
1956
|
}
|
|
1803
1957
|
if (p === "/api/aaa/radius-incoming") {
|
|
1804
|
-
return
|
|
1958
|
+
return json3(await getRadiusIncoming(createContext(undefined, deviceFromQuery())));
|
|
1805
1959
|
}
|
|
1806
1960
|
if (p === "/api/aaa/um-settings") {
|
|
1807
|
-
return
|
|
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
|
|
1814
|
-
return
|
|
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
|
|
1976
|
+
return json3(await setRadiusIncoming(ctx, body?.fields ?? {}));
|
|
1823
1977
|
if (p === "/api/aaa/um-settings")
|
|
1824
|
-
return
|
|
1978
|
+
return json3(await setUmSettings(ctx, body?.fields ?? {}));
|
|
1825
1979
|
if (p === "/api/aaa/radius-reset-counters")
|
|
1826
|
-
return
|
|
1980
|
+
return json3(await resetRadiusCounters(ctx));
|
|
1827
1981
|
const slug = body?.slug ?? "";
|
|
1828
1982
|
if (!AAA_ENTITIES[slug])
|
|
1829
|
-
return
|
|
1983
|
+
return json3({ error: "unknown entity" }, 404);
|
|
1830
1984
|
if (p === "/api/aaa/add")
|
|
1831
|
-
return
|
|
1985
|
+
return json3(await addAaaEntity(ctx, slug, body?.fields ?? {}));
|
|
1832
1986
|
if (p === "/api/aaa/update") {
|
|
1833
1987
|
if (!body?.id)
|
|
1834
|
-
return
|
|
1835
|
-
return
|
|
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
|
|
1840
|
-
return
|
|
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
|
|
1845
|
-
return
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2035
|
+
return json3({ error: "ip required" }, 400);
|
|
1882
2036
|
const series = usageStore.clientDailyUsage(device, ip, sinceTs(daysParam(url, 90, 400)));
|
|
1883
|
-
return
|
|
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
|
|
2042
|
+
return json3({ error: "user required" }, 400);
|
|
1889
2043
|
const series = usageStore.umUserDailyUsage(device, user, sinceTs(daysParam(url, 90, 400)));
|
|
1890
|
-
return
|
|
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
|
|
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
|
|
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 ?
|
|
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
|
|
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
|
|
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
|
|
2109
|
+
return json3({ error: "no commands provided" }, 400);
|
|
1956
2110
|
const plan = buildChangePlan(commands);
|
|
1957
|
-
return
|
|
2111
|
+
return json3({ plan, text: renderPlan(plan) });
|
|
1958
2112
|
}
|
|
1959
2113
|
if (p === "/api/s3" && req.method === "GET") {
|
|
1960
|
-
return
|
|
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
|
|
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
|
|
2127
|
+
return json3({ configured: true, target: s3Target(), objects, truncated: !!res.isTruncated });
|
|
1974
2128
|
} catch (e) {
|
|
1975
|
-
return
|
|
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
|
|
2134
|
+
return json3({ error: "S3 not configured" }, 400);
|
|
1981
2135
|
const key = url.searchParams.get("key");
|
|
1982
2136
|
if (!key)
|
|
1983
|
-
return
|
|
2137
|
+
return json3({ error: "key required" }, 400);
|
|
1984
2138
|
try {
|
|
1985
2139
|
const link = getS3Client().presign(key, { expiresIn: presignExpiresIn(), method: "GET" });
|
|
1986
|
-
return
|
|
2140
|
+
return json3({ url: link });
|
|
1987
2141
|
} catch (e) {
|
|
1988
|
-
return
|
|
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
|
|
2147
|
+
return json3({ error: "S3 not configured" }, 400);
|
|
1994
2148
|
const b = await readJson(req);
|
|
1995
2149
|
if (!b?.key)
|
|
1996
|
-
return
|
|
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
|
|
2154
|
+
return json3({ error: "not found" }, 404);
|
|
2001
2155
|
await client.delete(b.key);
|
|
2002
|
-
return
|
|
2156
|
+
return json3({ ok: true, key: b.key });
|
|
2003
2157
|
} catch (e) {
|
|
2004
|
-
return
|
|
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
|
|
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
|
|
2019
|
-
const dir = raw === "~" || raw.startsWith("~/") ?
|
|
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
|
|
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
|
|
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
|
|
2192
|
+
return json3({ error: "name required" }, 400);
|
|
2039
2193
|
try {
|
|
2040
|
-
return
|
|
2194
|
+
return json3({ name, content: readBackup(name) });
|
|
2041
2195
|
} catch {
|
|
2042
|
-
return
|
|
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
|
|
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
|
|
2221
|
+
return json3({ ok: true, name: writeBackup(safe, b.content) });
|
|
2068
2222
|
} catch (e) {
|
|
2069
|
-
return
|
|
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
|
|
2229
|
+
return json3({ error: "name and new_name are required" }, 400);
|
|
2076
2230
|
try {
|
|
2077
|
-
return
|
|
2231
|
+
return json3({ ok: true, name: renameBackup(b.name, b.new_name) });
|
|
2078
2232
|
} catch (e) {
|
|
2079
|
-
return
|
|
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
|
|
2239
|
+
return json3({ error: "name required" }, 400);
|
|
2086
2240
|
try {
|
|
2087
|
-
return deleteBackup(b.name) ?
|
|
2241
|
+
return deleteBackup(b.name) ? json3({ ok: true }) : json3({ error: "not found" }, 404);
|
|
2088
2242
|
} catch (e) {
|
|
2089
|
-
return
|
|
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
|
|
2249
|
+
return json3({ error: "name required" }, 400);
|
|
2096
2250
|
const device = resolveDeviceName(b.device);
|
|
2097
|
-
return
|
|
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 ?
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2389
|
+
return json3(devicesPayload(db));
|
|
2230
2390
|
}
|
|
2231
2391
|
if (url.pathname === "/api/topology") {
|
|
2232
|
-
return
|
|
2392
|
+
return json3(topologyPayload());
|
|
2233
2393
|
}
|
|
2234
2394
|
if (url.pathname === "/api/config") {
|
|
2235
|
-
return
|
|
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
|
|
2404
|
+
return json3(await fetchLatestRelease());
|
|
2245
2405
|
} catch (e) {
|
|
2246
|
-
return
|
|
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
|
|
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
|
|
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 ?
|
|
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
|
|
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
|
|
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
|
|
2385
|
-
import { join as
|
|
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(
|
|
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
|
|
2720
|
-
const site =
|
|
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 =
|
|
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 = [
|