@coffeerunhobby/mcp-ha-connect 2.4.7 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/.env.example +25 -0
  2. package/README.md +42 -10
  3. package/dist/config.d.ts +7 -0
  4. package/dist/config.js +22 -0
  5. package/dist/config.js.map +1 -1
  6. package/dist/index.js +3 -2
  7. package/dist/index.js.map +1 -1
  8. package/dist/net/adapters/omada.d.ts +46 -7
  9. package/dist/net/adapters/omada.js +432 -44
  10. package/dist/net/adapters/omada.js.map +1 -1
  11. package/dist/net/adapters/omadaPorts.d.ts +62 -0
  12. package/dist/net/adapters/omadaPorts.js +212 -0
  13. package/dist/net/adapters/omadaPorts.js.map +1 -0
  14. package/dist/net/adapters/omadaRules.d.ts +1 -1
  15. package/dist/net/adapters/omadaRules.js +2 -2
  16. package/dist/net/adapters/omadaRules.js.map +1 -1
  17. package/dist/net/adapters/ruijie.d.ts +59 -0
  18. package/dist/net/adapters/ruijie.js +363 -0
  19. package/dist/net/adapters/ruijie.js.map +1 -0
  20. package/dist/net/model.d.ts +37 -12
  21. package/dist/net/model.js.map +1 -1
  22. package/dist/omadaClient/accessControl.js +1 -1
  23. package/dist/omadaClient/accessControl.js.map +1 -1
  24. package/dist/omadaClient/index.d.ts +4 -0
  25. package/dist/omadaClient/index.js +3 -0
  26. package/dist/omadaClient/index.js.map +1 -1
  27. package/dist/omadaClient/networkConfig.d.ts +21 -0
  28. package/dist/omadaClient/networkConfig.js +41 -0
  29. package/dist/omadaClient/networkConfig.js.map +1 -1
  30. package/dist/ruijieClient/crypto.d.ts +20 -0
  31. package/dist/ruijieClient/crypto.js +57 -0
  32. package/dist/ruijieClient/crypto.js.map +1 -0
  33. package/dist/ruijieClient/index.d.ts +62 -0
  34. package/dist/ruijieClient/index.js +197 -0
  35. package/dist/ruijieClient/index.js.map +1 -0
  36. package/dist/server/common.d.ts +2 -0
  37. package/dist/server/common.js +2 -1
  38. package/dist/server/common.js.map +1 -1
  39. package/dist/server/http.d.ts +2 -0
  40. package/dist/server/http.js +2 -2
  41. package/dist/server/http.js.map +1 -1
  42. package/dist/server/stdio.d.ts +2 -0
  43. package/dist/server/stdio.js.map +1 -1
  44. package/dist/server/stream.d.ts +2 -0
  45. package/dist/server/stream.js +2 -1
  46. package/dist/server/stream.js.map +1 -1
  47. package/dist/tools/index.d.ts +3 -0
  48. package/dist/tools/index.js +9 -3
  49. package/dist/tools/index.js.map +1 -1
  50. package/dist/tools/net/index.d.ts +43 -2
  51. package/dist/tools/net/index.js +197 -12
  52. package/dist/tools/net/index.js.map +1 -1
  53. package/dist/tools/omada/listClients.js +1 -1
  54. package/dist/tools/omada/listClients.js.map +1 -1
  55. package/dist/upstreams.d.ts +8 -2
  56. package/dist/upstreams.js +56 -3
  57. package/dist/upstreams.js.map +1 -1
  58. package/dist/utils/redact.js +1 -1
  59. package/dist/utils/redact.js.map +1 -1
  60. package/dist/version.d.ts +1 -1
  61. package/dist/version.js +1 -1
  62. package/package.json +7 -7
@@ -0,0 +1,363 @@
1
+ /**
2
+ * Ruijie / Reyee adapter for the vendor-neutral network model (src/net/model.ts), through the
3
+ * router's local eWeb API (src/ruijieClient). Mapped on a RG-EG105G-V3, ReyeeOS 2.324
4
+ * (2026-10-24). 2.5.0 READS ONLY: every change is refused (UNSUPPORTED) until the write
5
+ * phases (reservations and rules, then networks) are built and live-tested.
6
+ *
7
+ * What is read, and how it maps:
8
+ * - networks: devConfig.get network, `lan[]` (one entry per LAN interface: address, netmask,
9
+ * VLAN id, DHCP). The default LAN carries Ruijie's internal VLAN 233, untagged on the LAN
10
+ * ports; the id of a network is its interface name, "vlan<id>", which rules refer to.
11
+ * - clients: the devices the router sees on its LAN right now, from its ARP table (devSta.get
12
+ * arp, interfaces "br-lan*", resolved MACs only), named by their address lease (devSta.get
13
+ * dhcp_lease) when they have one. A lease alone is no online device (a switched-off laptop
14
+ * keeps it until it expires); WAN-side ARP entries are the upstream network.
15
+ * - reservations: devConfig.get dhcp_static.
16
+ * - ports: devSta.get port_status. The router doesn't say which device is on which port, so
17
+ * `seen` stays empty; the WAN port is the uplink and protected.
18
+ * - rules: devConfig.get access_ctrl (evaluated in list order). Anything the model can't say
19
+ * faithfully (a source "anywhere", source ports, IPv6, user groups, schedules, a rule that
20
+ * matches only some connection states) is listed as `foreign` with the reason.
21
+ * No Wi-Fi radio on this model, no switch port profiles, no mDNS repeater; groups (Ruijie's
22
+ * user groups are address ranges and users, not the model's address groups) come later.
23
+ */
24
+ import { formatMac, normalizeMac } from '../../omadaClient/client.js';
25
+ import { inSubnet, ipToNumber, parseCidr, subnetsOverlap } from '../../utils/ipv4.js';
26
+ import { NetError, } from '../model.js';
27
+ /** Ruijie's name for "all the time" in a rule's time setting (the default time group). */
28
+ const ALL_TIME = new Set(['所有时段', 'everytime', 'All Time', '']);
29
+ const READ_ONLY = 'The Ruijie adapter reads only in this version; changes through it (reservations and rules first, then networks) come in a later version.';
30
+ const text = (value) => (typeof value === 'string' ? value : value === undefined || value === null ? '' : String(value));
31
+ const list = (value) => (Array.isArray(value) ? value.filter((v) => v && typeof v === 'object') : []);
32
+ /** "255.255.255.0" -> 24; undefined for a mask that isn't contiguous ones. */
33
+ export function maskToPrefix(mask) {
34
+ let n;
35
+ try {
36
+ n = ipToNumber(mask);
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ let prefix = 0;
42
+ for (let bit = 31; bit >= 0; bit--) {
43
+ if (Math.floor(n / 2 ** bit) % 2 === 1)
44
+ prefix++;
45
+ else
46
+ break;
47
+ }
48
+ return n === (prefix === 0 ? 0 : (2 ** 32 - 2 ** (32 - prefix))) ? prefix : undefined;
49
+ }
50
+ const numberToIp = (n) => [24, 16, 8, 0].map((s) => Math.floor(n / 2 ** s) % 256).join('.');
51
+ /** One `lan[]` entry as a model network (undefined when its address or mask can't be read). */
52
+ export function toNetwork(raw, index, routerHost) {
53
+ const ip = text(raw.ipaddr);
54
+ const prefix = maskToPrefix(text(raw.netmask));
55
+ try {
56
+ ipToNumber(ip);
57
+ }
58
+ catch {
59
+ return undefined;
60
+ }
61
+ if (prefix === undefined)
62
+ return undefined;
63
+ const vlan = Number(text(raw.vlanid));
64
+ const gateway = `${ip}/${prefix}`;
65
+ const start = text(raw.ipstart);
66
+ const limit = Number(text(raw.limit));
67
+ let range;
68
+ try {
69
+ if (start && Number.isInteger(limit) && limit > 0)
70
+ range = { from: start, to: numberToIp(ipToNumber(start) + limit - 1) };
71
+ }
72
+ catch {
73
+ range = undefined;
74
+ }
75
+ const lease = Number(text(raw.leasetime));
76
+ let routerAddress;
77
+ try {
78
+ routerAddress = ipToNumber(routerHost);
79
+ }
80
+ catch {
81
+ routerAddress = undefined;
82
+ }
83
+ return {
84
+ id: `vlan${text(raw.vlanid)}`,
85
+ name: text(raw.desc) || text(raw.name) || (index === 0 ? 'LAN' : `VLAN ${text(raw.vlanid)}`),
86
+ purpose: 'normal',
87
+ vlan: Number.isInteger(vlan) ? vlan : 0,
88
+ gateway,
89
+ dhcp: {
90
+ enabled: text(raw.ignore) !== '1',
91
+ ...(range ? { range } : {}),
92
+ ...(Number.isInteger(lease) && lease > 0 ? { leaseMinutes: lease } : {}),
93
+ },
94
+ ipv6: false,
95
+ isolated: false,
96
+ // The first LAN is the router's own (its management address), and so is any LAN the
97
+ // router is reached through; neither may be cut off.
98
+ protected: index === 0 || (routerAddress !== undefined && inSubnet(routerAddress, gateway)),
99
+ };
100
+ }
101
+ const networkOf = (ip, networks) => {
102
+ let n;
103
+ try {
104
+ n = ipToNumber(ip);
105
+ }
106
+ catch {
107
+ return undefined;
108
+ }
109
+ return networks.find((net) => net.gateway && inSubnet(n, net.gateway));
110
+ };
111
+ const macOrUndefined = (value) => {
112
+ try {
113
+ return formatMac(text(value));
114
+ }
115
+ catch {
116
+ return undefined;
117
+ }
118
+ };
119
+ /** The devices the router sees on its LAN (ARP), once each, named by their lease (same MAC and address) when they have one. */
120
+ export function toClients(leases, arp, networks) {
121
+ const names = new Map();
122
+ for (const l of leases) {
123
+ const mac = macOrUndefined(l.mac);
124
+ const name = text(l.hostname);
125
+ const key = `${mac}|${text(l.ip)}`;
126
+ if (mac && name && name !== '*' && !names.has(key))
127
+ names.set(key, name);
128
+ }
129
+ const clients = [];
130
+ const seen = new Set();
131
+ for (const a of arp) {
132
+ if (!text(a.intf).startsWith('br-lan'))
133
+ continue;
134
+ const mac = macOrUndefined(a.hardware);
135
+ // 00:00:00:00:00:00 = an address nobody answered for.
136
+ if (!mac || mac === '00-00-00-00-00-00' || seen.has(mac))
137
+ continue;
138
+ seen.add(mac);
139
+ const ip = text(a.address);
140
+ const network = networkOf(ip, networks);
141
+ const name = names.get(`${mac}|${ip}`);
142
+ clients.push({ mac, ...(name ? { name } : {}), ...(ip ? { ip } : {}), ...(network ? { network: { id: network.id } } : {}), online: true });
143
+ }
144
+ return clients;
145
+ }
146
+ export function toReservations(entries, networks) {
147
+ const out = [];
148
+ for (const e of entries) {
149
+ const mac = macOrUndefined(e.macaddr);
150
+ const ip = text(e.ipaddr);
151
+ if (!mac || !ip)
152
+ continue;
153
+ const network = networkOf(ip, networks);
154
+ out.push({ mac, ip, network: network ? { id: network.id } : { name: 'outside the router\'s networks' } });
155
+ }
156
+ return out;
157
+ }
158
+ export function toPorts(entries, routerMac) {
159
+ return entries.map((p) => {
160
+ const wan = text(p.name).toUpperCase() === 'WAN';
161
+ return {
162
+ device: { mac: routerMac },
163
+ port: Number(text(p.portId)),
164
+ ...(text(p.panel_name) ? { name: text(p.panel_name) } : {}),
165
+ seen: [],
166
+ uplink: wan,
167
+ ...(wan ? { protected: "it is the router's internet (WAN) port" } : {}),
168
+ };
169
+ });
170
+ }
171
+ /**
172
+ * A rule side (src or dest, with its addresses) as model selectors, or the reason it can't be one.
173
+ * The model's address selector carries no interface, so addresses are kept only where they imply
174
+ * the interface the rule names: inside the LANs (lan) or that VLAN (vlanX), outside every LAN
175
+ * (wan), or any interface (all). Anything else would overstate or misplace what the rule matches.
176
+ */
177
+ function side(where, addresses, networks, isSource) {
178
+ if (addresses) {
179
+ const parts = addresses.split(',').map((a) => a.trim()).filter(Boolean);
180
+ if (parts.some((a) => a.includes('-')))
181
+ return `an address range (${addresses})`;
182
+ let ranges;
183
+ try {
184
+ ranges = parts.map((a) => parseCidr(a));
185
+ }
186
+ catch {
187
+ return `an address it can't read (${addresses})`;
188
+ }
189
+ const inside = (nets) => ranges.every((r) => nets.some((n) => n.gateway && r.network >= parseCidr(n.gateway).network && r.broadcast <= parseCidr(n.gateway).broadcast));
190
+ const lans = networks.filter((n) => n.gateway);
191
+ const implied = where === 'all' ||
192
+ (where === 'lan' && inside(lans)) ||
193
+ (where === 'wan' && parts.every((a) => !lans.some((n) => subnetsOverlap(a, n.gateway)))) ||
194
+ (where.startsWith('vlan') && inside(networks.filter((n) => n.id === where)));
195
+ if (!implied)
196
+ return `addresses (${addresses}) limited to the interface '${where}'`;
197
+ return [{ addresses: parts }];
198
+ }
199
+ if (where === 'wan')
200
+ return [{ internet: true }];
201
+ if (where === 'lan')
202
+ return networks.map((n) => ({ network: { id: n.id } }));
203
+ if (where === 'all')
204
+ return isSource ? 'traffic from anywhere, the internet included' : 'traffic to anywhere';
205
+ const network = networks.find((n) => n.id === where);
206
+ if (network)
207
+ return [{ network: { id: network.id } }];
208
+ return `the interface '${where}'`;
209
+ }
210
+ export function toRule(raw, index, networks) {
211
+ const reasons = [];
212
+ const target = text(raw.target).toUpperCase();
213
+ const action = target === 'ACCEPT' ? 'allow' : 'block';
214
+ if (!['ACCEPT', 'DROP', 'REJECT'].includes(target))
215
+ reasons.push(`an unknown action '${text(raw.target)}'`);
216
+ let from = [];
217
+ if (text(raw.by) === 'mac') {
218
+ const mac = macOrUndefined(raw.mac);
219
+ if (mac)
220
+ from = [{ device: mac }];
221
+ else
222
+ reasons.push('a device match without a readable MAC');
223
+ // The model's device match has no interface: only "any LAN" or "anywhere" say the same.
224
+ const where = text(raw.src);
225
+ if (where && where !== 'lan' && where !== 'all')
226
+ reasons.push(`a device match limited to the interface '${where}'`);
227
+ }
228
+ else {
229
+ const s = side(text(raw.src), text(raw.srcIP), networks, true);
230
+ if (typeof s === 'string')
231
+ reasons.push(s);
232
+ else
233
+ from = s;
234
+ }
235
+ let to = [];
236
+ const d = side(text(raw.dest), text(raw.destIP), networks, false);
237
+ if (typeof d === 'string')
238
+ reasons.push(d);
239
+ else
240
+ to = d;
241
+ const proto = text(raw.proto).toLowerCase();
242
+ let protocol = 'any';
243
+ if (proto === 'tcp' || proto === 'udp' || proto === 'icmp')
244
+ protocol = proto;
245
+ else if (proto === 'tcpudp' && !text(raw.destPort))
246
+ reasons.push('TCP and UDP together without ports');
247
+ else if (proto !== 'all' && proto !== '' && proto !== 'tcpudp')
248
+ reasons.push(`the protocol '${text(raw.proto)}'`);
249
+ if (text(raw.destPort)) {
250
+ // The model's ports mean TCP and UDP together; a rule for one of them only would be overstated.
251
+ if (proto === 'tcpudp')
252
+ protocol = { tcpUdpPorts: text(raw.destPort) };
253
+ else if (proto === 'tcp' || proto === 'udp')
254
+ reasons.push(`destination ports for ${proto} only (the model's ports mean TCP and UDP)`);
255
+ else
256
+ reasons.push('destination ports without TCP or UDP');
257
+ }
258
+ if (text(raw.srcPort))
259
+ reasons.push('source ports');
260
+ if (text(raw.netProto) && text(raw.netProto) !== 'ipv4')
261
+ reasons.push(`an ${text(raw.netProto)} rule`);
262
+ if (list(raw.user).length || list(raw.user_group_list).length || (Array.isArray(raw.userGroupList) && raw.userGroupList.length))
263
+ reasons.push('user groups');
264
+ if (!ALL_TIME.has(text(raw.tmngtName)))
265
+ reasons.push(`a schedule ('${text(raw.tmngtName)}')`);
266
+ // "0" = every connection state, replies included (the rule is not stateful).
267
+ const state = text(raw.state) || '0';
268
+ if (state !== '0')
269
+ reasons.push(`only some connection states (${state})`);
270
+ return {
271
+ id: text(raw.uuid) || text(raw.ruleid) || `rule-${index + 1}`,
272
+ name: text(raw.ruleName) || `rule ${index + 1}`,
273
+ action,
274
+ from,
275
+ to,
276
+ protocol,
277
+ replies: 'match',
278
+ schedule: { kind: 'always' },
279
+ enabled: text(raw.enable) === '1',
280
+ position: index + 1,
281
+ ...(reasons.length ? { foreign: { reason: `Read-only here: it uses ${reasons.join(', ')}.` } } : {}),
282
+ };
283
+ }
284
+ export class RuijieNetworkController {
285
+ client;
286
+ vendor = 'ruijie';
287
+ id;
288
+ constructor(client, options = {}) {
289
+ this.client = client;
290
+ this.id = options.id ?? `ruijie:${client.host}`;
291
+ }
292
+ async capabilities() {
293
+ return {
294
+ vendor: 'ruijie',
295
+ networks: { create: 'unsupported', isolation: 'unsupported', ipv6: 'unsupported', guestPurpose: 'unsupported' },
296
+ wifi: { create: 'unsupported', bands: [], security: [], pmf: 'unsupported', fastRoaming: 'unsupported', bandSteering: 'unsupported' },
297
+ ports: { profiles: 'unsupported', assign: 'unsupported' },
298
+ rules: { between: 'unsupported', toInternet: 'unsupported', deviceMatch: 'unsupported', wifiMatch: 'unsupported', stateful: 'unsupported', schedule: 'unsupported' },
299
+ groups: { address: 'unsupported', mac: 'unsupported' },
300
+ reservations: 'unsupported',
301
+ mdnsRepeater: 'unsupported',
302
+ };
303
+ }
304
+ async protectedResources() {
305
+ const networks = (await this.listNetworks()).filter((n) => n.protected);
306
+ const ports = (await this.listPorts()).filter((p) => p.protected);
307
+ return { networks: networks.map((n) => ({ id: n.id })), ports: ports.map((p) => ({ device: p.device, port: p.port })), devices: [] };
308
+ }
309
+ async listNetworks() {
310
+ const config = await this.client.config('network');
311
+ return list(config.lan)
312
+ .map((raw, i) => toNetwork(raw, i, this.client.host))
313
+ .filter((n) => n !== undefined);
314
+ }
315
+ async listWifi() {
316
+ return [];
317
+ }
318
+ async listPortProfiles() {
319
+ return [];
320
+ }
321
+ async listPorts(device) {
322
+ const info = await this.client.status('sysinfo');
323
+ const routerMac = macOrUndefined(info.sys_mac) ?? 'unknown';
324
+ if (device) {
325
+ const wanted = 'mac' in device ? device.mac : 'id' in device ? device.id : device.name;
326
+ const names = [routerMac, text(info.model), text(info.product_class), 'router'].map((v) => v.toLowerCase());
327
+ if (normalizeMac(wanted) !== normalizeMac(routerMac) && !names.includes(wanted.trim().toLowerCase())) {
328
+ throw new NetError('NOT_FOUND', `This Ruijie router (${text(info.model) || 'router'}, ${routerMac}) is the only device here; '${wanted}' is not it`);
329
+ }
330
+ }
331
+ const status = await this.client.status('port_status');
332
+ return toPorts(list(status.List), routerMac);
333
+ }
334
+ async listRules() {
335
+ const networks = await this.listNetworks();
336
+ const acl = await this.client.config('access_ctrl');
337
+ return list(acl.list).map((raw, i) => toRule(raw, i, networks));
338
+ }
339
+ async listGroups() {
340
+ return [];
341
+ }
342
+ async listReservations() {
343
+ const networks = await this.listNetworks();
344
+ const config = await this.client.config('dhcp_static');
345
+ return toReservations(list(config.dhcp_static), networks);
346
+ }
347
+ async listClients() {
348
+ const networks = await this.listNetworks();
349
+ const leases = await this.client.status('dhcp_lease');
350
+ const arp = await this.client.status('arp');
351
+ return toClients(list(leases.List), list(arp.arpList), networks);
352
+ }
353
+ async getMdnsRepeater() {
354
+ return { enabled: false, networks: [], foreign: { reason: 'This Ruijie router has no mDNS repeater the tools manage.' } };
355
+ }
356
+ async plan(change) {
357
+ throw new NetError('UNSUPPORTED', READ_ONLY, { capability: change.kind });
358
+ }
359
+ async apply() {
360
+ throw new NetError('UNSUPPORTED', READ_ONLY);
361
+ }
362
+ }
363
+ //# sourceMappingURL=ruijie.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ruijie.js","sourceRoot":"","sources":["../../../src/net/adapters/ruijie.ts"],"names":[],"mappings":"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"}
@@ -26,7 +26,7 @@ export type Ipv4 = string;
26
26
  export type Clock = string;
27
27
  export declare const DAYS: readonly ["mon", "tue", "wed", "thu", "fri", "sat", "sun"];
28
28
  export type Day = (typeof DAYS)[number];
29
- export type Vendor = 'omada' | 'unifi' | 'mikrotik';
29
+ export type Vendor = 'omada' | 'unifi' | 'mikrotik' | 'ruijie';
30
30
  export interface TimeWindow {
31
31
  days: Day[];
32
32
  from: Clock;
@@ -135,19 +135,47 @@ export interface PortRef {
135
135
  };
136
136
  port: number;
137
137
  }
138
+ /** The networks a port carries: untagged (native) and tagged. */
139
+ export interface PortNetworks {
140
+ /** Untagged network (the port's PVID); absent when the port carries no untagged traffic. */
141
+ native?: Ref;
142
+ /** Further untagged networks besides the native one (rare). */
143
+ untagged?: Ref[];
144
+ tagged: Ref[] | 'all' | 'none';
145
+ }
138
146
  export interface PortProfile {
139
147
  id: string;
140
148
  name: string;
141
- /** Untagged network (the port's PVID). */
142
- native: Ref;
143
- tagged: Ref[] | 'all' | 'none';
149
+ /** What a port following the profile carries; absent when the profile sets no networks (ports keep their own). */
150
+ networks?: PortNetworks;
144
151
  isolation: boolean;
145
152
  }
146
153
  export interface Port extends PortRef {
154
+ name?: string;
147
155
  profile?: Ref;
148
- /** What the port carries right now (clients, an access point, the gateway uplink...). */
156
+ /** The networks the port carries right now. */
157
+ networks?: PortNetworks;
158
+ /** The port has its own settings instead of following its profile (Omada: profile override). */
159
+ customSettings?: boolean;
160
+ /** Online devices on the port right now. */
149
161
  seen: Client[];
162
+ /** The port links the switch to the network or to another network device (gateway, switch, access point). */
150
163
  uplink: boolean;
164
+ /** Why the port can't be reassigned through the tools (uplink, the controller's port...), when it can't. */
165
+ protected?: string;
166
+ }
167
+ /**
168
+ * Repeats mDNS (Bonjour) announcements between networks, so devices discover each other's
169
+ * services (AirPlay, Chromecast, HomeKit, printers) across VLANs. Discovery only: traffic
170
+ * still follows the rules.
171
+ */
172
+ export interface MdnsRepeater {
173
+ enabled: boolean;
174
+ networks: Ref[];
175
+ /** Set when the controller's mDNS setup is more than the model can express (then it can't be changed here). */
176
+ foreign?: {
177
+ reason: string;
178
+ };
151
179
  }
152
180
  export interface Group {
153
181
  id: string;
@@ -293,7 +321,7 @@ export type Change = {
293
321
  op: 'assign';
294
322
  kind: 'port';
295
323
  port: PortRef;
296
- profile: Ref;
324
+ networks: PortNetworks;
297
325
  } | {
298
326
  op: 'create';
299
327
  kind: 'rule';
@@ -331,7 +359,7 @@ export type Change = {
331
359
  op: 'set';
332
360
  kind: 'mdnsRepeater';
333
361
  enabled: boolean;
334
- networks: Ref[];
362
+ networks?: Ref[];
335
363
  };
336
364
  /** One vendor-level step of a plan, in the order it will run. Never contains secrets. */
337
365
  export interface Step {
@@ -355,7 +383,7 @@ export interface Plan {
355
383
  export interface ApplyResult {
356
384
  applied: true;
357
385
  /** The neutral object after the change (redacted), when there is one. */
358
- object?: Network | Wifi | PortProfile | Rule | Group | Reservation;
386
+ object?: Network | Wifi | PortProfile | Port | Rule | Group | Reservation | MdnsRepeater;
359
387
  /** Vendor objects created for this change; tagged so delete() removes them all again. */
360
388
  owned: Array<{
361
389
  object: string;
@@ -403,10 +431,7 @@ export interface NetworkController {
403
431
  listGroups(kind?: Group['kind']): Promise<Group[]>;
404
432
  listReservations(): Promise<Reservation[]>;
405
433
  listClients(): Promise<Client[]>;
406
- getMdnsRepeater(): Promise<{
407
- enabled: boolean;
408
- networks: Ref[];
409
- }>;
434
+ getMdnsRepeater(): Promise<MdnsRepeater>;
410
435
  /** Read-only: validates, resolves refs, checks protection, in-use and lock-out. Throws NetError. */
411
436
  plan(change: Change): Promise<Plan>;
412
437
  /** Runs a plan; re-checks `basis` first (CONFLICT) and lock-out (LOCKOUT unless allowLockout). */
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/net/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AA0Q/E,MAAM,OAAO,QAAS,SAAQ,KAAK;IAElB;IAEA;IAHb,YACa,IAAkB,EAC3B,OAAe,EACN,UAAyE,EAAE;QAEpF,KAAK,CAAC,OAAO,CAAC,CAAC;QAJN,SAAI,GAAJ,IAAI,CAAc;QAElB,YAAO,GAAP,OAAO,CAAoE;QAGpF,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IAC3B,CAAC;CACJ"}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../src/net/model.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAcH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAU,CAAC;AAwS/E,MAAM,OAAO,QAAS,SAAQ,KAAK;IAElB;IAEA;IAHb,YACa,IAAkB,EAC3B,OAAe,EACN,UAAyE,EAAE;QAEpF,KAAK,CAAC,OAAO,CAAC,CAAC;QAJN,SAAI,GAAJ,IAAI,CAAc;QAElB,YAAO,GAAP,OAAO,CAAoE;QAGpF,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;IAC3B,CAAC;CACJ"}
@@ -389,7 +389,7 @@ export class AccessControlOperations {
389
389
  */
390
390
  async refuseLanDestination(destinationType, destinationIds, siteId) {
391
391
  const remedy = 'Gateway ACLs made here only cover LAN to internet traffic, so a rule towards your own networks would never match anything. ' +
392
- 'Rules between networks come with the net_ rule tools (2.4.7); until then make them in the controller web UI (Gateway ACL, direction LAN -> LAN).';
392
+ 'Rules between networks are made with net_createRule (stateful, LAN -> LAN), or in the controller web UI.';
393
393
  if (destinationType === DESTINATION_TYPES.network) {
394
394
  throw new Error(`A LAN network can't be the destination. ${remedy}`);
395
395
  }