@owine/unifi-network-mcp 2.15.0 → 3.1.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/LICENSE +21 -0
- package/README.md +257 -77
- package/dist/client.d.ts +26 -1
- package/dist/client.js +199 -24
- package/dist/config.d.ts +11 -0
- package/dist/config.js +102 -3
- package/dist/guard.d.ts +46 -0
- package/dist/guard.js +180 -0
- package/dist/index.js +17 -4
- package/dist/instructions.d.ts +9 -0
- package/dist/instructions.js +32 -0
- package/dist/tls.d.ts +14 -0
- package/dist/tls.js +58 -0
- package/dist/tools/acl.js +123 -50
- package/dist/tools/classic.d.ts +3 -0
- package/dist/tools/classic.js +54 -0
- package/dist/tools/client-history.js +22 -12
- package/dist/tools/clients.js +10 -9
- package/dist/tools/devices.js +21 -20
- package/dist/tools/dns-policies.js +27 -27
- package/dist/tools/exposure.d.ts +3 -0
- package/dist/tools/exposure.js +118 -0
- package/dist/tools/firewall.d.ts +2 -1
- package/dist/tools/firewall.js +200 -75
- package/dist/tools/hotspot.js +35 -28
- package/dist/tools/index.d.ts +13 -1
- package/dist/tools/index.js +67 -4
- package/dist/tools/networks.d.ts +2 -1
- package/dist/tools/networks.js +44 -31
- package/dist/tools/sites.js +2 -2
- package/dist/tools/supporting.js +22 -21
- package/dist/tools/switching.js +16 -15
- package/dist/tools/system.js +1 -1
- package/dist/tools/traffic-matching.d.ts +2 -1
- package/dist/tools/traffic-matching.js +22 -20
- package/dist/tools/wifi.js +76 -30
- package/dist/utils/full-put.d.ts +11 -2
- package/dist/utils/full-put.js +25 -2
- package/dist/utils/output-schemas.d.ts +241 -4
- package/dist/utils/output-schemas.js +225 -49
- package/dist/utils/params.d.ts +12 -0
- package/dist/utils/params.js +21 -0
- package/dist/utils/pick.d.ts +6 -0
- package/dist/utils/pick.js +11 -0
- package/dist/utils/responses.d.ts +2 -0
- package/dist/utils/responses.js +28 -2
- package/dist/utils/safety.d.ts +35 -1
- package/dist/utils/safety.js +53 -3
- package/dist/utils/secrets.js +3 -0
- package/dist/utils/wan-exposure.d.ts +69 -0
- package/dist/utils/wan-exposure.js +245 -0
- package/dist/write-log.d.ts +52 -0
- package/dist/write-log.js +178 -0
- package/dist/write-tools.d.ts +27 -0
- package/dist/write-tools.js +50 -0
- package/package.json +19 -15
|
@@ -1,22 +1,28 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.listClientSessionsOutputSchema = exports.listClientHistoryOutputSchema = exports.ClientSessionEnvelope = exports.ClientSession = exports.HistoryClient = exports.listPendingDevicesOutputSchema = exports.listCountriesOutputSchema = exports.listDpiApplicationsOutputSchema = exports.listDpiCategoriesOutputSchema = exports.listDeviceTagsOutputSchema = exports.listRadiusProfilesOutputSchema = exports.listVpnTunnelsOutputSchema = exports.listVpnServersOutputSchema = exports.listWansOutputSchema = exports.getWifiOutputSchema = exports.listWifiOutputSchema = exports.getClientOutputSchema = exports.listClientsOutputSchema = exports.getDeviceStatisticsOutputSchema = exports.getDeviceOutputSchema = exports.listDevicesOutputSchema = exports.listTrafficMatchingListsOutputSchema = exports.trafficMatchingListOutputSchema = exports.listDnsPoliciesOutputSchema = exports.dnsPolicyOutputSchema = exports.listLagsOutputSchema = exports.lagOutputSchema = exports.listMcLagDomainsOutputSchema = exports.mcLagDomainOutputSchema = exports.listSwitchStacksOutputSchema = exports.switchStackOutputSchema = exports.aclRuleOrderingOutputSchema = exports.listAclRulesOutputSchema = exports.aclRuleOutputSchema = exports.firewallPolicyOrderingOutputSchema = exports.listFirewallPoliciesOutputSchema = exports.firewallPolicyOutputSchema = exports.listFirewallZonesOutputSchema = exports.firewallZoneOutputSchema = exports.createVouchersOutputSchema = exports.listVouchersOutputSchema = exports.voucherOutputSchema = exports.getNetworkReferencesOutputSchema = exports.listNetworksOutputSchema = exports.networkOutputSchema = exports.listSitesOutputSchema = exports.getInfoOutputSchema = void 0;
|
|
3
|
+
exports.LegacyFirewallRule = exports.ClassicFirewallGroup = exports.ClassicNetwork = exports.listClientSessionsOutputSchema = exports.listClientHistoryOutputSchema = exports.ClientSessionEnvelope = exports.ClientSession = exports.HistoryClient = exports.listPendingDevicesOutputSchema = exports.listCountriesOutputSchema = exports.listDpiApplicationsOutputSchema = exports.listDpiCategoriesOutputSchema = exports.listDeviceTagsOutputSchema = exports.listRadiusProfilesOutputSchema = exports.listVpnTunnelsOutputSchema = exports.listVpnServersOutputSchema = exports.listWansOutputSchema = exports.getWifiOutputSchema = exports.listWifiOutputSchema = exports.getClientOutputSchema = exports.listClientsOutputSchema = exports.getDeviceStatisticsOutputSchema = exports.getDeviceOutputSchema = exports.listDevicesOutputSchema = exports.listTrafficMatchingListsOutputSchema = exports.trafficMatchingListOutputSchema = exports.listDnsPoliciesOutputSchema = exports.dnsPolicyOutputSchema = exports.listLagsOutputSchema = exports.lagOutputSchema = exports.listMcLagDomainsOutputSchema = exports.mcLagDomainOutputSchema = exports.listSwitchStacksOutputSchema = exports.switchStackOutputSchema = exports.aclRuleOrderingOutputSchema = exports.listAclRulesOutputSchema = exports.aclRuleOutputSchema = exports.firewallPolicyOrderingOutputSchema = exports.listFirewallPoliciesOutputSchema = exports.firewallPolicyOutputSchema = exports.listFirewallZonesOutputSchema = exports.firewallZoneOutputSchema = exports.createVouchersOutputSchema = exports.listVouchersOutputSchema = exports.voucherOutputSchema = exports.getNetworkReferencesOutputSchema = exports.listNetworksOutputSchema = exports.networkOutputSchema = exports.listSitesOutputSchema = exports.getInfoOutputSchema = void 0;
|
|
4
|
+
exports.getDeviceSshSettingsOutputSchema = exports.getUpnpStatusOutputSchema = exports.listPortForwardsOutputSchema = exports.PortForward = exports.PortForwardDestination = exports.listLegacyFirewallRulesOutputSchema = exports.listFirewallGroupsOutputSchema = exports.listClassicNetworksOutputSchema = exports.ClassicEnvelope = void 0;
|
|
5
|
+
exports.looseOutput = looseOutput;
|
|
6
|
+
exports.writeResult = writeResult;
|
|
4
7
|
/**
|
|
5
|
-
* Output schemas for
|
|
6
|
-
*
|
|
8
|
+
* Output schemas for every read tool, and for write tools whose response
|
|
9
|
+
* echoes the resource (loose strategy: every non-key
|
|
10
|
+
* field optional, every nested object uses .loose() to allow
|
|
7
11
|
* firmware/hardware-specific fields to flow through unchanged).
|
|
8
12
|
*
|
|
9
13
|
* Verified against UniFi Network API 11.0.83. Where the docs collapse
|
|
10
14
|
* nested arrays/objects (e.g. interfaces.ports[], radios[]), the schema
|
|
11
|
-
* uses
|
|
12
|
-
* haven't verified.
|
|
15
|
+
* uses loose records so the contract doesn't lock to fields we
|
|
16
|
+
* haven't verified. Tools register these raw shapes, and registerAllTools()
|
|
17
|
+
* publishes each as a loose object (src/tools/index.ts), so fields a schema
|
|
18
|
+
* does not list never fail client-side validation.
|
|
13
19
|
*/
|
|
14
20
|
const zod_1 = require("zod");
|
|
15
21
|
// configurable is live-verified (11.0.83) on system-defined zones, policies,
|
|
16
22
|
// networks and RADIUS profiles; it is absent on user-defined resources.
|
|
17
23
|
const Metadata = zod_1.z
|
|
18
24
|
.object({ origin: zod_1.z.string().optional(), configurable: zod_1.z.boolean().optional() })
|
|
19
|
-
.
|
|
25
|
+
.loose();
|
|
20
26
|
const PaginationFields = {
|
|
21
27
|
offset: zod_1.z.number().int().optional(),
|
|
22
28
|
limit: zod_1.z.number().int().optional(),
|
|
@@ -45,7 +51,7 @@ const DeviceListItem = zod_1.z
|
|
|
45
51
|
features: zod_1.z.array(zod_1.z.string()).optional(),
|
|
46
52
|
interfaces: zod_1.z.array(zod_1.z.string()).optional(),
|
|
47
53
|
})
|
|
48
|
-
.
|
|
54
|
+
.loose();
|
|
49
55
|
const DevicePort = zod_1.z
|
|
50
56
|
.object({
|
|
51
57
|
idx: zod_1.z.number(),
|
|
@@ -61,10 +67,10 @@ const DevicePort = zod_1.z
|
|
|
61
67
|
enabled: zod_1.z.boolean().optional(),
|
|
62
68
|
state: zod_1.z.string().optional(),
|
|
63
69
|
})
|
|
64
|
-
.
|
|
70
|
+
.loose()
|
|
65
71
|
.optional(),
|
|
66
72
|
})
|
|
67
|
-
.
|
|
73
|
+
.loose();
|
|
68
74
|
const DeviceRadio = zod_1.z
|
|
69
75
|
.object({
|
|
70
76
|
wlanStandard: zod_1.z.string().optional(),
|
|
@@ -72,7 +78,7 @@ const DeviceRadio = zod_1.z
|
|
|
72
78
|
channelWidthMHz: zod_1.z.number().optional(),
|
|
73
79
|
channel: zod_1.z.number().optional(),
|
|
74
80
|
})
|
|
75
|
-
.
|
|
81
|
+
.loose();
|
|
76
82
|
const DeviceDetail = zod_1.z
|
|
77
83
|
.object({
|
|
78
84
|
...DeviceCommon,
|
|
@@ -81,7 +87,7 @@ const DeviceDetail = zod_1.z
|
|
|
81
87
|
configurationId: zod_1.z.string().optional(),
|
|
82
88
|
uplink: zod_1.z
|
|
83
89
|
.object({ deviceId: zod_1.z.string().optional() })
|
|
84
|
-
.
|
|
90
|
+
.loose()
|
|
85
91
|
.optional(),
|
|
86
92
|
// Detail view: object keyed by capability (switching/accessPoint).
|
|
87
93
|
features: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
@@ -90,10 +96,10 @@ const DeviceDetail = zod_1.z
|
|
|
90
96
|
ports: zod_1.z.array(DevicePort).optional(),
|
|
91
97
|
radios: zod_1.z.array(DeviceRadio).optional(),
|
|
92
98
|
})
|
|
93
|
-
.
|
|
99
|
+
.loose()
|
|
94
100
|
.optional(),
|
|
95
101
|
})
|
|
96
|
-
.
|
|
102
|
+
.loose();
|
|
97
103
|
const DeviceStatistics = zod_1.z
|
|
98
104
|
.object({
|
|
99
105
|
uptimeSec: zod_1.z.number().optional(),
|
|
@@ -109,7 +115,7 @@ const DeviceStatistics = zod_1.z
|
|
|
109
115
|
txRateBps: zod_1.z.number().optional(),
|
|
110
116
|
rxRateBps: zod_1.z.number().optional(),
|
|
111
117
|
})
|
|
112
|
-
.
|
|
118
|
+
.loose()
|
|
113
119
|
.optional(),
|
|
114
120
|
interfaces: zod_1.z
|
|
115
121
|
.object({
|
|
@@ -119,13 +125,13 @@ const DeviceStatistics = zod_1.z
|
|
|
119
125
|
frequencyGHz: zod_1.z.number().optional(),
|
|
120
126
|
txRetriesPct: zod_1.z.number().optional(),
|
|
121
127
|
})
|
|
122
|
-
.
|
|
128
|
+
.loose())
|
|
123
129
|
.optional(),
|
|
124
130
|
})
|
|
125
|
-
.
|
|
131
|
+
.loose()
|
|
126
132
|
.optional(),
|
|
127
133
|
})
|
|
128
|
-
.
|
|
134
|
+
.loose();
|
|
129
135
|
const Client = zod_1.z
|
|
130
136
|
.object({
|
|
131
137
|
id: zod_1.z.string(),
|
|
@@ -137,10 +143,10 @@ const Client = zod_1.z
|
|
|
137
143
|
uplinkDeviceId: zod_1.z.string().optional(),
|
|
138
144
|
access: zod_1.z
|
|
139
145
|
.object({ type: zod_1.z.string().optional() })
|
|
140
|
-
.
|
|
146
|
+
.loose()
|
|
141
147
|
.optional(),
|
|
142
148
|
})
|
|
143
|
-
.
|
|
149
|
+
.loose();
|
|
144
150
|
const WifiBroadcast = zod_1.z
|
|
145
151
|
.object({
|
|
146
152
|
id: zod_1.z.string(),
|
|
@@ -159,20 +165,20 @@ const WifiBroadcast = zod_1.z
|
|
|
159
165
|
broadcastingDeviceFilter: zod_1.z.unknown().optional(),
|
|
160
166
|
metadata: Metadata.optional(),
|
|
161
167
|
})
|
|
162
|
-
.
|
|
168
|
+
.loose();
|
|
163
169
|
const ApplicationInfo = zod_1.z
|
|
164
170
|
.object({
|
|
165
171
|
// Live-verified (11.0.83): only applicationVersion is returned.
|
|
166
172
|
applicationVersion: zod_1.z.string().optional(),
|
|
167
173
|
})
|
|
168
|
-
.
|
|
174
|
+
.loose();
|
|
169
175
|
const Site = zod_1.z
|
|
170
176
|
.object({
|
|
171
177
|
id: zod_1.z.string(),
|
|
172
178
|
internalReference: zod_1.z.string().optional(),
|
|
173
179
|
name: zod_1.z.string().optional(),
|
|
174
180
|
})
|
|
175
|
-
.
|
|
181
|
+
.loose();
|
|
176
182
|
const Network = zod_1.z
|
|
177
183
|
.object({
|
|
178
184
|
id: zod_1.z.string(),
|
|
@@ -192,12 +198,12 @@ const Network = zod_1.z
|
|
|
192
198
|
ipv6Configuration: zod_1.z.unknown().optional(),
|
|
193
199
|
metadata: Metadata.optional(),
|
|
194
200
|
})
|
|
195
|
-
.
|
|
201
|
+
.loose();
|
|
196
202
|
const NetworkReferences = zod_1.z
|
|
197
203
|
.object({
|
|
198
204
|
referenceResources: zod_1.z.array(zod_1.z.unknown()).optional(),
|
|
199
205
|
})
|
|
200
|
-
.
|
|
206
|
+
.loose();
|
|
201
207
|
const Voucher = zod_1.z
|
|
202
208
|
.object({
|
|
203
209
|
id: zod_1.z.string(),
|
|
@@ -214,7 +220,7 @@ const Voucher = zod_1.z
|
|
|
214
220
|
authorizedGuestCount: zod_1.z.number().optional(),
|
|
215
221
|
expired: zod_1.z.boolean().optional(),
|
|
216
222
|
})
|
|
217
|
-
.
|
|
223
|
+
.loose();
|
|
218
224
|
const FirewallZone = zod_1.z
|
|
219
225
|
.object({
|
|
220
226
|
id: zod_1.z.string(),
|
|
@@ -222,7 +228,7 @@ const FirewallZone = zod_1.z
|
|
|
222
228
|
networkIds: zod_1.z.array(zod_1.z.string()).optional(),
|
|
223
229
|
metadata: Metadata.optional(),
|
|
224
230
|
})
|
|
225
|
-
.
|
|
231
|
+
.loose();
|
|
226
232
|
const FirewallPolicy = zod_1.z
|
|
227
233
|
.object({
|
|
228
234
|
id: zod_1.z.string(),
|
|
@@ -244,7 +250,7 @@ const FirewallPolicy = zod_1.z
|
|
|
244
250
|
description: zod_1.z.string().optional(),
|
|
245
251
|
metadata: Metadata.optional(),
|
|
246
252
|
})
|
|
247
|
-
.
|
|
253
|
+
.loose();
|
|
248
254
|
const FirewallPolicyOrdering = zod_1.z
|
|
249
255
|
.object({
|
|
250
256
|
orderedFirewallPolicyIds: zod_1.z
|
|
@@ -252,10 +258,10 @@ const FirewallPolicyOrdering = zod_1.z
|
|
|
252
258
|
beforeSystemDefined: zod_1.z.array(zod_1.z.string()).optional(),
|
|
253
259
|
afterSystemDefined: zod_1.z.array(zod_1.z.string()).optional(),
|
|
254
260
|
})
|
|
255
|
-
.
|
|
261
|
+
.loose()
|
|
256
262
|
.optional(),
|
|
257
263
|
})
|
|
258
|
-
.
|
|
264
|
+
.loose();
|
|
259
265
|
const AclRule = zod_1.z
|
|
260
266
|
.object({
|
|
261
267
|
id: zod_1.z.string(),
|
|
@@ -276,12 +282,12 @@ const AclRule = zod_1.z
|
|
|
276
282
|
index: zod_1.z.number().int().optional(),
|
|
277
283
|
metadata: Metadata.optional(),
|
|
278
284
|
})
|
|
279
|
-
.
|
|
285
|
+
.loose();
|
|
280
286
|
const AclRuleOrdering = zod_1.z
|
|
281
287
|
.object({
|
|
282
288
|
orderedAclRuleIds: zod_1.z.array(zod_1.z.string()).optional(),
|
|
283
289
|
})
|
|
284
|
-
.
|
|
290
|
+
.loose();
|
|
285
291
|
const SwitchStack = zod_1.z
|
|
286
292
|
.object({
|
|
287
293
|
id: zod_1.z.string(),
|
|
@@ -296,7 +302,7 @@ const SwitchStack = zod_1.z
|
|
|
296
302
|
lags: zod_1.z.array(zod_1.z.unknown()).optional(),
|
|
297
303
|
metadata: Metadata.optional(),
|
|
298
304
|
})
|
|
299
|
-
.
|
|
305
|
+
.loose();
|
|
300
306
|
const McLagDomain = zod_1.z
|
|
301
307
|
.object({
|
|
302
308
|
id: zod_1.z.string(),
|
|
@@ -305,7 +311,7 @@ const McLagDomain = zod_1.z
|
|
|
305
311
|
lags: zod_1.z.array(zod_1.z.unknown()).optional(),
|
|
306
312
|
metadata: Metadata.optional(),
|
|
307
313
|
})
|
|
308
|
-
.
|
|
314
|
+
.loose();
|
|
309
315
|
const Lag = zod_1.z
|
|
310
316
|
.object({
|
|
311
317
|
id: zod_1.z.string(),
|
|
@@ -316,11 +322,11 @@ const Lag = zod_1.z
|
|
|
316
322
|
deviceId: zod_1.z.string().optional(),
|
|
317
323
|
portIdxs: zod_1.z.array(zod_1.z.number()).optional(),
|
|
318
324
|
})
|
|
319
|
-
.
|
|
325
|
+
.loose())
|
|
320
326
|
.optional(),
|
|
321
327
|
metadata: Metadata.optional(),
|
|
322
328
|
})
|
|
323
|
-
.
|
|
329
|
+
.loose();
|
|
324
330
|
const DnsPolicy = zod_1.z
|
|
325
331
|
.object({
|
|
326
332
|
id: zod_1.z.string(),
|
|
@@ -344,7 +350,7 @@ const DnsPolicy = zod_1.z
|
|
|
344
350
|
ipAddress: zod_1.z.string().nullable().optional(),
|
|
345
351
|
metadata: Metadata.optional(),
|
|
346
352
|
})
|
|
347
|
-
.
|
|
353
|
+
.loose();
|
|
348
354
|
const TrafficMatchingList = zod_1.z
|
|
349
355
|
.object({
|
|
350
356
|
id: zod_1.z.string(),
|
|
@@ -353,7 +359,7 @@ const TrafficMatchingList = zod_1.z
|
|
|
353
359
|
items: zod_1.z.array(zod_1.z.unknown()).optional(),
|
|
354
360
|
metadata: Metadata.optional(),
|
|
355
361
|
})
|
|
356
|
-
.
|
|
362
|
+
.loose();
|
|
357
363
|
// Verified against the live Integration API (11.0.83). The WAN and
|
|
358
364
|
// site-to-site-tunnel list rows are intentionally minimal in the API.
|
|
359
365
|
const Wan = zod_1.z
|
|
@@ -361,7 +367,7 @@ const Wan = zod_1.z
|
|
|
361
367
|
id: zod_1.z.string(),
|
|
362
368
|
name: zod_1.z.string().optional(),
|
|
363
369
|
})
|
|
364
|
-
.
|
|
370
|
+
.loose();
|
|
365
371
|
const VpnServer = zod_1.z
|
|
366
372
|
.object({
|
|
367
373
|
id: zod_1.z.string(),
|
|
@@ -370,21 +376,21 @@ const VpnServer = zod_1.z
|
|
|
370
376
|
enabled: zod_1.z.boolean().optional(),
|
|
371
377
|
metadata: Metadata.optional(),
|
|
372
378
|
})
|
|
373
|
-
.
|
|
379
|
+
.loose();
|
|
374
380
|
// No tunnels present in the verification environment — keep loose.
|
|
375
381
|
const VpnTunnel = zod_1.z
|
|
376
382
|
.object({
|
|
377
383
|
id: zod_1.z.string(),
|
|
378
384
|
name: zod_1.z.string().optional(),
|
|
379
385
|
})
|
|
380
|
-
.
|
|
386
|
+
.loose();
|
|
381
387
|
const RadiusProfile = zod_1.z
|
|
382
388
|
.object({
|
|
383
389
|
id: zod_1.z.string(),
|
|
384
390
|
name: zod_1.z.string().optional(),
|
|
385
391
|
metadata: Metadata.optional(),
|
|
386
392
|
})
|
|
387
|
-
.
|
|
393
|
+
.loose();
|
|
388
394
|
// No tags present in the verification environment — documented shape.
|
|
389
395
|
const DeviceTag = zod_1.z
|
|
390
396
|
.object({
|
|
@@ -392,27 +398,27 @@ const DeviceTag = zod_1.z
|
|
|
392
398
|
name: zod_1.z.string().optional(),
|
|
393
399
|
deviceIds: zod_1.z.array(zod_1.z.string()).optional(),
|
|
394
400
|
})
|
|
395
|
-
.
|
|
401
|
+
.loose();
|
|
396
402
|
// DPI category/application ids are NUMERIC (verified live).
|
|
397
403
|
const DpiCategory = zod_1.z
|
|
398
404
|
.object({
|
|
399
405
|
id: zod_1.z.number(),
|
|
400
406
|
name: zod_1.z.string().optional(),
|
|
401
407
|
})
|
|
402
|
-
.
|
|
408
|
+
.loose();
|
|
403
409
|
const DpiApplication = zod_1.z
|
|
404
410
|
.object({
|
|
405
411
|
id: zod_1.z.number(),
|
|
406
412
|
name: zod_1.z.string().optional(),
|
|
407
413
|
})
|
|
408
|
-
.
|
|
414
|
+
.loose();
|
|
409
415
|
// Countries key on `code` (ISO alpha-2), NOT `id` (verified live).
|
|
410
416
|
const Country = zod_1.z
|
|
411
417
|
.object({
|
|
412
418
|
code: zod_1.z.string(),
|
|
413
419
|
name: zod_1.z.string().optional(),
|
|
414
420
|
})
|
|
415
|
-
.
|
|
421
|
+
.loose();
|
|
416
422
|
// No pending devices in the verification environment — keep loose.
|
|
417
423
|
const PendingDevice = zod_1.z
|
|
418
424
|
.object({
|
|
@@ -421,7 +427,31 @@ const PendingDevice = zod_1.z
|
|
|
421
427
|
ipAddress: zod_1.z.string().optional(),
|
|
422
428
|
firmwareVersion: zod_1.z.string().optional(),
|
|
423
429
|
})
|
|
424
|
-
.
|
|
430
|
+
.loose();
|
|
431
|
+
// A write tool returns either the API's echo of the resource or, with dryRun,
|
|
432
|
+
// a preview of the request it would send. The SDK validates both against the
|
|
433
|
+
// one outputSchema, so the resource's key fields relax to optional alongside
|
|
434
|
+
// the preview fields; read tools keep their stricter schemas.
|
|
435
|
+
const DryRunPreview = {
|
|
436
|
+
dryRun: zod_1.z.literal(true).optional(),
|
|
437
|
+
wouldExecute: zod_1.z
|
|
438
|
+
.object({ method: zod_1.z.string(), path: zod_1.z.string(), body: zod_1.z.unknown().optional() })
|
|
439
|
+
.loose()
|
|
440
|
+
.optional(),
|
|
441
|
+
confirmationRequired: zod_1.z.string().optional(),
|
|
442
|
+
};
|
|
443
|
+
/**
|
|
444
|
+
* The outputSchema to publish for a tool: always a loose object, whether the
|
|
445
|
+
* tool passed a raw shape or a Zod object (the SDK accepts both). Wrapping a
|
|
446
|
+
* Zod object in z.looseObject() would build a broken schema that makes
|
|
447
|
+
* tools/list throw for every tool.
|
|
448
|
+
*/
|
|
449
|
+
function looseOutput(schema) {
|
|
450
|
+
return schema instanceof zod_1.z.ZodObject ? schema.loose() : zod_1.z.looseObject(schema);
|
|
451
|
+
}
|
|
452
|
+
function writeResult(shape) {
|
|
453
|
+
return { ...zod_1.z.object(shape).partial().shape, ...DryRunPreview };
|
|
454
|
+
}
|
|
425
455
|
exports.getInfoOutputSchema = ApplicationInfo.shape;
|
|
426
456
|
exports.listSitesOutputSchema = {
|
|
427
457
|
...PaginationFields,
|
|
@@ -552,7 +582,7 @@ exports.HistoryClient = zod_1.z
|
|
|
552
582
|
is_wired: zod_1.z.boolean().optional(),
|
|
553
583
|
status: zod_1.z.string().optional(),
|
|
554
584
|
})
|
|
555
|
-
.
|
|
585
|
+
.loose();
|
|
556
586
|
exports.ClientSession = zod_1.z
|
|
557
587
|
.object({
|
|
558
588
|
_id: zod_1.z.string(),
|
|
@@ -565,15 +595,18 @@ exports.ClientSession = zod_1.z
|
|
|
565
595
|
is_wired: zod_1.z.boolean().optional(),
|
|
566
596
|
roaming_sessions: zod_1.z.array(zod_1.z.unknown()).optional(),
|
|
567
597
|
})
|
|
568
|
-
.
|
|
598
|
+
.loose();
|
|
569
599
|
/** Envelope returned by the controller's stat/session endpoint. */
|
|
570
600
|
exports.ClientSessionEnvelope = zod_1.z.object({
|
|
571
|
-
meta: zod_1.z.object({ rc: zod_1.z.literal("ok") }).
|
|
601
|
+
meta: zod_1.z.object({ rc: zod_1.z.literal("ok") }).loose(),
|
|
572
602
|
data: zod_1.z.array(exports.ClientSession),
|
|
573
603
|
});
|
|
574
604
|
exports.listClientHistoryOutputSchema = {
|
|
575
605
|
data: zod_1.z.array(exports.HistoryClient),
|
|
576
606
|
count: zod_1.z.number(),
|
|
607
|
+
totalCount: zod_1.z.number(),
|
|
608
|
+
limit: zod_1.z.number(),
|
|
609
|
+
truncated: zod_1.z.boolean(),
|
|
577
610
|
withinHours: zod_1.z.number(),
|
|
578
611
|
coverage: zod_1.z.string(),
|
|
579
612
|
};
|
|
@@ -586,3 +619,146 @@ exports.listClientSessionsOutputSchema = {
|
|
|
586
619
|
end: zod_1.z.number(),
|
|
587
620
|
coverage: zod_1.z.string(),
|
|
588
621
|
};
|
|
622
|
+
// Classic controller API reads (rest/networkconf, rest/firewallgroup,
|
|
623
|
+
// rest/firewallrule, rest/portforward, get/setting). Unlike every other schema
|
|
624
|
+
// here, each row shape is also the tool's output allowlist: the tools copy only
|
|
625
|
+
// these fields from a record, so a field the controller adds never reaches the
|
|
626
|
+
// model. networkconf records carry WAN, VPN and RADIUS credentials
|
|
627
|
+
// (x_passphrase, x_wan_password, x_ipsec_*, x_openvpn_*, WireGuard keys, PPPoE
|
|
628
|
+
// logins), and settings sections carry more (x_ssh_password, x_mgmt_key,
|
|
629
|
+
// cloud-access keys); never list one here. Field types vary across controller
|
|
630
|
+
// versions, hence the unions.
|
|
631
|
+
const classicScalar = zod_1.z.union([zod_1.z.string(), zod_1.z.number()]).nullish();
|
|
632
|
+
const classicString = zod_1.z.string().nullish();
|
|
633
|
+
const classicBoolean = zod_1.z.boolean().nullish();
|
|
634
|
+
exports.ClassicNetwork = zod_1.z
|
|
635
|
+
.object({
|
|
636
|
+
_id: zod_1.z.string(),
|
|
637
|
+
name: classicString,
|
|
638
|
+
purpose: classicString,
|
|
639
|
+
vlan: classicScalar,
|
|
640
|
+
vlan_enabled: classicBoolean,
|
|
641
|
+
ip_subnet: classicString,
|
|
642
|
+
networkgroup: classicString,
|
|
643
|
+
enabled: classicBoolean,
|
|
644
|
+
dhcpd_enabled: classicBoolean,
|
|
645
|
+
dhcpd_start: classicString,
|
|
646
|
+
dhcpd_stop: classicString,
|
|
647
|
+
domain_name: classicString,
|
|
648
|
+
igmp_snooping: classicBoolean,
|
|
649
|
+
mdns_enabled: classicBoolean,
|
|
650
|
+
network_isolation_enabled: classicBoolean,
|
|
651
|
+
internet_access_enabled: classicBoolean,
|
|
652
|
+
firewall_zone_id: classicString,
|
|
653
|
+
is_nat: classicBoolean,
|
|
654
|
+
ipv6_interface_type: classicString,
|
|
655
|
+
setting_preference: classicString,
|
|
656
|
+
})
|
|
657
|
+
.loose();
|
|
658
|
+
exports.ClassicFirewallGroup = zod_1.z
|
|
659
|
+
.object({
|
|
660
|
+
_id: zod_1.z.string(),
|
|
661
|
+
name: classicString,
|
|
662
|
+
group_type: classicString,
|
|
663
|
+
group_members: zod_1.z.array(zod_1.z.string()).nullish(),
|
|
664
|
+
})
|
|
665
|
+
.loose();
|
|
666
|
+
exports.LegacyFirewallRule = zod_1.z
|
|
667
|
+
.object({
|
|
668
|
+
_id: zod_1.z.string(),
|
|
669
|
+
name: classicString,
|
|
670
|
+
enabled: classicBoolean,
|
|
671
|
+
ruleset: classicString,
|
|
672
|
+
rule_index: classicScalar,
|
|
673
|
+
action: classicString,
|
|
674
|
+
protocol: classicString,
|
|
675
|
+
protocol_v6: classicString,
|
|
676
|
+
protocol_match_excepted: classicBoolean,
|
|
677
|
+
icmp_typename: classicString,
|
|
678
|
+
icmpv6_typename: classicString,
|
|
679
|
+
logging: classicBoolean,
|
|
680
|
+
state_established: classicBoolean,
|
|
681
|
+
state_related: classicBoolean,
|
|
682
|
+
state_new: classicBoolean,
|
|
683
|
+
state_invalid: classicBoolean,
|
|
684
|
+
src_firewallgroup_ids: zod_1.z.array(zod_1.z.string()).nullish(),
|
|
685
|
+
src_networkconf_id: classicString,
|
|
686
|
+
src_networkconf_type: classicString,
|
|
687
|
+
src_address: classicString,
|
|
688
|
+
src_address_ipv6: classicString,
|
|
689
|
+
src_mac_address: classicString,
|
|
690
|
+
src_port: classicScalar,
|
|
691
|
+
dst_firewallgroup_ids: zod_1.z.array(zod_1.z.string()).nullish(),
|
|
692
|
+
dst_networkconf_id: classicString,
|
|
693
|
+
dst_networkconf_type: classicString,
|
|
694
|
+
dst_address: classicString,
|
|
695
|
+
dst_address_ipv6: classicString,
|
|
696
|
+
dst_port: classicScalar,
|
|
697
|
+
ipsec: classicString,
|
|
698
|
+
setting_preference: classicString,
|
|
699
|
+
})
|
|
700
|
+
.loose();
|
|
701
|
+
/** Envelope of a classic REST collection; rows stay unknown until the allowlist is applied. */
|
|
702
|
+
exports.ClassicEnvelope = zod_1.z.object({
|
|
703
|
+
meta: zod_1.z.object({ rc: zod_1.z.literal("ok") }).loose(),
|
|
704
|
+
data: zod_1.z.array(zod_1.z.record(zod_1.z.string(), zod_1.z.unknown())),
|
|
705
|
+
});
|
|
706
|
+
const classicList = (row) => ({
|
|
707
|
+
data: zod_1.z.array(row),
|
|
708
|
+
count: zod_1.z.number(),
|
|
709
|
+
siteReference: zod_1.z.string(),
|
|
710
|
+
});
|
|
711
|
+
exports.listClassicNetworksOutputSchema = classicList(exports.ClassicNetwork);
|
|
712
|
+
exports.listFirewallGroupsOutputSchema = classicList(exports.ClassicFirewallGroup);
|
|
713
|
+
exports.listLegacyFirewallRulesOutputSchema = classicList(exports.LegacyFirewallRule);
|
|
714
|
+
exports.PortForwardDestination = zod_1.z
|
|
715
|
+
.object({
|
|
716
|
+
destination_ip: classicString,
|
|
717
|
+
interface: classicString,
|
|
718
|
+
})
|
|
719
|
+
.loose();
|
|
720
|
+
exports.PortForward = zod_1.z
|
|
721
|
+
.object({
|
|
722
|
+
_id: zod_1.z.string(),
|
|
723
|
+
name: classicString,
|
|
724
|
+
enabled: classicBoolean,
|
|
725
|
+
pfwd_interface: classicString,
|
|
726
|
+
destination_ip: classicString,
|
|
727
|
+
destination_ips: zod_1.z.array(exports.PortForwardDestination).nullish(),
|
|
728
|
+
src: classicString,
|
|
729
|
+
src_limiting_enabled: classicBoolean,
|
|
730
|
+
src_limiting_type: classicString,
|
|
731
|
+
src_firewall_group_id: classicString,
|
|
732
|
+
dst_port: classicScalar,
|
|
733
|
+
fwd: classicString,
|
|
734
|
+
fwd_port: classicScalar,
|
|
735
|
+
proto: classicString,
|
|
736
|
+
log: classicBoolean,
|
|
737
|
+
})
|
|
738
|
+
.loose();
|
|
739
|
+
exports.listPortForwardsOutputSchema = {
|
|
740
|
+
data: zod_1.z.array(exports.PortForward),
|
|
741
|
+
count: zod_1.z.number(),
|
|
742
|
+
enabledCount: zod_1.z.number(),
|
|
743
|
+
siteReference: zod_1.z.string(),
|
|
744
|
+
};
|
|
745
|
+
const settingsStatus = zod_1.z.enum(["available", "unavailable"]);
|
|
746
|
+
exports.getUpnpStatusOutputSchema = {
|
|
747
|
+
siteReference: zod_1.z.string(),
|
|
748
|
+
status: settingsStatus,
|
|
749
|
+
source: zod_1.z.string().nullable(),
|
|
750
|
+
upnp_enabled: zod_1.z.boolean().nullable(),
|
|
751
|
+
upnp_nat_pmp_enabled: zod_1.z.boolean().nullable(),
|
|
752
|
+
upnp_secure_mode: zod_1.z.boolean().nullable(),
|
|
753
|
+
upnp_wan_interface: zod_1.z.string().nullable(),
|
|
754
|
+
detail: zod_1.z.string(),
|
|
755
|
+
};
|
|
756
|
+
exports.getDeviceSshSettingsOutputSchema = {
|
|
757
|
+
siteReference: zod_1.z.string(),
|
|
758
|
+
status: settingsStatus,
|
|
759
|
+
x_ssh_enabled: zod_1.z.boolean().nullable(),
|
|
760
|
+
x_ssh_auth_password_enabled: zod_1.z.boolean().nullable(),
|
|
761
|
+
x_ssh_bind_wildcard: zod_1.z.boolean().nullable(),
|
|
762
|
+
sshKeyCount: zod_1.z.number().nullable(),
|
|
763
|
+
detail: zod_1.z.string(),
|
|
764
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* An Integration API resource ID. IDs are interpolated into request paths, so
|
|
4
|
+
* only the plain ID charset is accepted — anything else could smuggle a query
|
|
5
|
+
* (`?force=true`), a fragment, or extra path segments into the request.
|
|
6
|
+
*/
|
|
7
|
+
export declare function resourceId(): z.ZodString;
|
|
8
|
+
/**
|
|
9
|
+
* A site's internalReference (often default), which the controller routes
|
|
10
|
+
* outside the Integration API take in place of the site UUID.
|
|
11
|
+
*/
|
|
12
|
+
export declare function siteReference(): z.ZodString;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resourceId = resourceId;
|
|
4
|
+
exports.siteReference = siteReference;
|
|
5
|
+
const zod_1 = require("zod");
|
|
6
|
+
/**
|
|
7
|
+
* An Integration API resource ID. IDs are interpolated into request paths, so
|
|
8
|
+
* only the plain ID charset is accepted — anything else could smuggle a query
|
|
9
|
+
* (`?force=true`), a fragment, or extra path segments into the request.
|
|
10
|
+
*/
|
|
11
|
+
function resourceId() {
|
|
12
|
+
return zod_1.z.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "Use an ID returned by a list or get tool");
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A site's internalReference (often default), which the controller routes
|
|
16
|
+
* outside the Integration API take in place of the site UUID.
|
|
17
|
+
*/
|
|
18
|
+
function siteReference() {
|
|
19
|
+
return zod_1.z.string().regex(/^[A-Za-z0-9_-]{1,64}$/)
|
|
20
|
+
.describe("Site internalReference from unifi_list_sites (often default), not the Integration API UUID");
|
|
21
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copy only the allowlisted fields of a record. Classic records hold
|
|
3
|
+
* credentials under names no redaction list could anticipate, so the output is
|
|
4
|
+
* built up from the schema's keys and nothing is ever passed through.
|
|
5
|
+
*/
|
|
6
|
+
export declare function pick(row: Record<string, unknown>, fields: readonly string[]): Record<string, unknown>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pick = pick;
|
|
4
|
+
/**
|
|
5
|
+
* Copy only the allowlisted fields of a record. Classic records hold
|
|
6
|
+
* credentials under names no redaction list could anticipate, so the output is
|
|
7
|
+
* built up from the schema's keys and nothing is ever passed through.
|
|
8
|
+
*/
|
|
9
|
+
function pick(row, fields) {
|
|
10
|
+
return Object.fromEntries(fields.filter((field) => Object.hasOwn(row, field)).map((field) => [field, row[field]]));
|
|
11
|
+
}
|
|
@@ -7,6 +7,8 @@ export declare function formatSuccess(data: unknown, opts?: {
|
|
|
7
7
|
}[];
|
|
8
8
|
structuredContent?: Record<string, unknown>;
|
|
9
9
|
};
|
|
10
|
+
/** The message of a thrown value, which need not be an Error. */
|
|
11
|
+
export declare function errorMessage(err: unknown): string;
|
|
10
12
|
export declare function formatError(err: unknown): {
|
|
11
13
|
content: {
|
|
12
14
|
type: "text";
|
package/dist/utils/responses.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.formatSuccess = formatSuccess;
|
|
4
|
+
exports.errorMessage = errorMessage;
|
|
4
5
|
exports.formatError = formatError;
|
|
5
6
|
function formatSuccess(data, opts) {
|
|
6
7
|
const result = {
|
|
7
|
-
content: [{ type: "text", text: JSON.stringify(data
|
|
8
|
+
content: [{ type: "text", text: JSON.stringify(data) }],
|
|
8
9
|
};
|
|
9
10
|
if (opts?.structured &&
|
|
10
11
|
data !== null &&
|
|
@@ -14,10 +15,35 @@ function formatSuccess(data, opts) {
|
|
|
14
15
|
}
|
|
15
16
|
return result;
|
|
16
17
|
}
|
|
18
|
+
/** The message of a thrown value, which need not be an Error. */
|
|
19
|
+
function errorMessage(err) {
|
|
20
|
+
return err instanceof Error ? err.message : String(err);
|
|
21
|
+
}
|
|
17
22
|
function formatError(err) {
|
|
18
|
-
|
|
23
|
+
let message = errorMessage(err);
|
|
24
|
+
// fetch reports every network failure as "fetch failed" and keeps the reason
|
|
25
|
+
// (ECONNREFUSED, ENOTFOUND, a certificate error) in cause.
|
|
26
|
+
const cause = err instanceof Error ? err.cause : undefined;
|
|
27
|
+
const code = cause instanceof Error && "code" in cause && typeof cause.code === "string" ? cause.code : undefined;
|
|
28
|
+
if (code) {
|
|
29
|
+
message += ` (${code})`;
|
|
30
|
+
message += tlsHint(code);
|
|
31
|
+
}
|
|
19
32
|
return {
|
|
20
33
|
content: [{ type: "text", text: `Error: ${message}` }],
|
|
21
34
|
isError: true,
|
|
22
35
|
};
|
|
23
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* What to do about a certificate that did not verify. Pinning makes a
|
|
39
|
+
* console's own certificate trusted, but it does not lift the validity dates,
|
|
40
|
+
* so an expired certificate gets its own advice.
|
|
41
|
+
*/
|
|
42
|
+
function tlsHint(code) {
|
|
43
|
+
if (code === "CERT_HAS_EXPIRED" || code === "CERT_NOT_YET_VALID") {
|
|
44
|
+
return ". The console's TLS certificate is outside its validity dates; check the clocks on the console and this machine, and renew the certificate if it has expired (pinning it with UNIFI_NETWORK_CA_CERT does not override the dates)";
|
|
45
|
+
}
|
|
46
|
+
if (!/CERT|SELF_SIGNED|LEAF_SIGNATURE/.test(code))
|
|
47
|
+
return "";
|
|
48
|
+
return ". The console's TLS certificate did not verify. For a local console with a self-signed certificate, save the certificate and point UNIFI_NETWORK_CA_CERT at it; if it is already set, the console has a new certificate, so save it again. Against api.ui.com, something between you and Ubiquiti presented this certificate: do not work around it";
|
|
49
|
+
}
|