diff --git a/modules/unifi/client.ts b/modules/unifi/client.ts index fda068c..89afa7a 100644 --- a/modules/unifi/client.ts +++ b/modules/unifi/client.ts @@ -23,6 +23,19 @@ export interface UnifiPortForward { site_id?: string; } +export interface UnifiNetwork { + _id: string; + name: string; + purpose: string; + ip_subnet?: string; + dhcpd_enabled?: boolean; + dhcpd_dns_enabled?: boolean; + dhcpd_dns_1?: string; + dhcpd_dns_2?: string; + dhcpd_dns_3?: string; + dhcpd_dns_4?: string; +} + export interface UnifiDevice { _id: string; name: string; @@ -232,6 +245,17 @@ export class UnifiApiClient { await this.request("DELETE", `/api/s/${this.site}/rest/portforward/${id}`); } + // --- Networks --- + + async listNetworks(): Promise { + return this.request("GET", `/api/s/${this.site}/rest/networkconf`); + } + + async updateNetwork(id: string, fields: Partial): Promise { + const result = await this.request("PUT", `/api/s/${this.site}/rest/networkconf/${id}`, fields); + return result[0]; + } + // --- Devices --- async listDevices(): Promise { diff --git a/modules/unifi/tools/index.ts b/modules/unifi/tools/index.ts index da26751..d890999 100644 --- a/modules/unifi/tools/index.ts +++ b/modules/unifi/tools/index.ts @@ -1,10 +1,23 @@ // unifi's tools — its own code (novox/hq ADR 0039), importing unifi's own client. They return // structured data; the mesh serves them through the sdk's tool harness. unifi is tools-only (no // events entrypoint): the controller does not push lifecycle events the mesh consumes, so this -// module reads its resources — port forwards, devices, clients — and exposes them, and stops there. +// module reads its resources — port forwards, networks, devices, clients — and exposes them, and stops there. import { registerModuleTools, type ToolDefinition } from "@novox/mesh-sdk/tools"; -import { UnifiApiClient, type UnifiPortForward } from "../client.js"; +import { UnifiApiClient, type UnifiNetwork, type UnifiPortForward } from "../client.js"; + +function summarizeNetwork(n: UnifiNetwork): Record { + const dns = [n.dhcpd_dns_1, n.dhcpd_dns_2, n.dhcpd_dns_3, n.dhcpd_dns_4].filter((s): s is string => !!s); + return { + id: n._id, + name: n.name, + purpose: n.purpose, + subnet: n.ip_subnet ?? null, + dhcp: n.dhcpd_enabled ?? null, + // What DHCP hands out as DNS: the listed servers when set, otherwise the gateway itself. + dhcp_dns: n.dhcpd_dns_enabled ? dns : "the gateway", + }; +} function summarizePortForward(r: UnifiPortForward): Record { return { @@ -88,6 +101,40 @@ export function getUnifiTools(unifi: UnifiApiClient): ToolDefinition[] { return { deleted: true, id: String(args.id) }; }, }, + { + name: "unifi_list_networks", + description: "List the networks the UniFi controller manages, with the DNS servers each one's DHCP hands out.", + input: {}, + run: async () => { + const nets = await unifi.listNetworks(); + return { count: nets.length, networks: nets.map(summarizeNetwork) }; + }, + }, + { + name: "unifi_set_network_dns", + description: + "Set the DNS servers a network's DHCP hands out to its devices (see unifi_list_networks for ids). " + + "Up to four addresses, comma-separated; \"gateway\" hands out the gateway itself. Devices pick it up when they renew.", + input: { + id: { type: "string", description: "the network id" }, + dns: { type: "string", description: "comma-separated DNS server addresses, or \"gateway\"" }, + }, + run: async (args) => { + const asked = String(args.dns ?? "").trim(); + if (!asked) return { updated: false, reason: "say dns: addresses, or \"gateway\"" }; + const servers = asked === "gateway" ? [] : asked.split(",").map((s) => s.trim()).filter(Boolean); + if (servers.length > 4) return { updated: false, reason: "DHCP hands out at most four DNS servers" }; + const fields: Partial = { + dhcpd_dns_enabled: servers.length > 0, + dhcpd_dns_1: servers[0] ?? "", + dhcpd_dns_2: servers[1] ?? "", + dhcpd_dns_3: servers[2] ?? "", + dhcpd_dns_4: servers[3] ?? "", + }; + const net = await unifi.updateNetwork(String(args.id), fields); + return { updated: summarizeNetwork(net) }; + }, + }, { name: "unifi_list_devices", description: "List the network devices (APs, switches, gateways) the UniFi controller manages.",