rhombus-node-mcp 0.1.55 → 0.1.57
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/api/access-control-tool-api.js +231 -32
- package/dist/api/alarm-monitoring-tool-api.js +50 -1
- package/dist/api/automated-prompts-tool-api.js +9 -17
- package/dist/api/camera-policy-tool-api.js +122 -0
- package/dist/api/camera-tool-api.js +3 -5
- package/dist/api/camera-uptime-tool-api.js +62 -25
- package/dist/api/clips-tool-api.js +45 -9
- package/dist/api/create-tool-api.js +55 -7
- package/dist/api/door-tool-api.js +74 -13
- package/dist/api/entity-lookup-tool-api.js +8 -1
- package/dist/api/events-tool-api.js +79 -6
- package/dist/api/faces-tool-api.js +112 -7
- package/dist/api/get-accessible-apps.js +68 -6
- package/dist/api/get-entity-tool-api.js +12 -12
- package/dist/api/get-org-information-tool-api.js +2 -2
- package/dist/api/guest-management-tool-api.js +75 -1
- package/dist/api/location-tool-api.js +120 -28
- package/dist/api/lpr-tool-api.js +71 -16
- package/dist/api/policy-alerts-tool-api.js +7 -3
- package/dist/api/report-tool-api.js +4 -1
- package/dist/api/rules-tool-api.js +6 -16
- package/dist/api/schedule-tool-api.js +107 -0
- package/dist/api/search-tool-api.js +5 -13
- package/dist/api/update-tool-api.js +96 -1
- package/dist/api/user-tool-api.js +58 -13
- package/dist/createServer.js +18 -6
- package/dist/filtering-utils.js +421 -29
- package/dist/index.js +1 -1
- package/dist/network/network.js +122 -10
- package/dist/network/org-reference-cache.js +74 -0
- package/dist/telemetry/tracingProxy.js +4 -0
- package/dist/tools/entity-lookup-tool.js +8 -4
- package/dist/tools/get-entity-tool.js +46 -21
- package/dist/tools/get-org-information-tool.js +36 -3
- package/dist/tools/time-conversion-tool.js +65 -37
- package/dist/tools/time-tool.js +18 -4
- package/dist/tools/user-tool.js +101 -1
- package/dist/tools-console/access-anomaly-tool.js +4 -19
- package/dist/tools-console/access-control-tool.js +330 -2
- package/dist/tools-console/alarm-monitoring-tool.js +73 -1
- package/dist/tools-console/analytics-tool.js +4 -2
- package/dist/tools-console/automated-prompts-tool.js +2 -0
- package/dist/tools-console/badge-timeline-tool.js +4 -21
- package/dist/tools-console/camera-policy-tool.js +252 -0
- package/dist/tools-console/camera-tool.js +113 -89
- package/dist/tools-console/camera-uptime-tool.js +2 -0
- package/dist/tools-console/clips-tool.js +23 -31
- package/dist/tools-console/count-tool.js +19 -6
- package/dist/tools-console/create-camera-policy-tool.js +304 -122
- package/dist/tools-console/door-schedule-exception-tool.js +4 -33
- package/dist/tools-console/door-tool.js +131 -1
- package/dist/tools-console/elements-access-anomaly-tool.js +4 -19
- package/dist/tools-console/elements-badge-timeline-tool.js +4 -21
- package/dist/tools-console/elements-lost-badge-tool.js +4 -17
- package/dist/tools-console/elements-tool.js +4 -29
- package/dist/tools-console/events-tool.js +24 -101
- package/dist/tools-console/faces-tool.js +255 -27
- package/dist/tools-console/guest-management-tool.js +128 -1
- package/dist/tools-console/location-tool.js +161 -35
- package/dist/tools-console/lost-badge-tool.js +4 -17
- package/dist/tools-console/lpr-tool.js +90 -22
- package/dist/tools-console/netbox-access-anomaly-tool.js +4 -19
- package/dist/tools-console/netbox-badge-timeline-tool.js +4 -21
- package/dist/tools-console/netbox-lost-badge-tool.js +4 -17
- package/dist/tools-console/netbox-tool.js +4 -29
- package/dist/tools-console/onguard-tool.js +4 -30
- package/dist/tools-console/person-tracking-tool.js +4 -2
- package/dist/tools-console/policy-alerts-tool.js +6 -28
- package/dist/tools-console/reboot-cameras-tool.js +23 -3
- package/dist/tools-console/report-tool.js +12 -40
- package/dist/tools-console/rules-tool.js +2 -0
- package/dist/tools-console/schedule-tool.js +190 -0
- package/dist/tools-console/search-tool.js +2 -0
- package/dist/tools-console/update-tool.js +300 -168
- package/dist/tools-console/user-access-trail-tool.js +2 -0
- package/dist/tools-console/user-audit-tool.js +2 -0
- package/dist/tools-console/video-walls-tool.js +94 -20
- package/dist/types/access-anomaly-tool-types.js +22 -2
- package/dist/types/access-control-tool-types.js +116 -1
- package/dist/types/alarm-monitoring-tool-types.js +42 -1
- package/dist/types/badge-timeline-tool-types.js +18 -3
- package/dist/types/camera-policy-tool-types.js +104 -0
- package/dist/types/camera-tool-types.js +80 -1
- package/dist/types/clips-tool-types.js +27 -3
- package/dist/types/create-camera-policy-tool-types.js +275 -26
- package/dist/types/door-schedule-exception-tool-types.js +20 -6
- package/dist/types/door-tool-types.js +36 -4
- package/dist/types/entity-lookup-tool-types.js +6 -1
- package/dist/types/events-tools-types.js +24 -16
- package/dist/types/faces-tools-types.js +106 -3
- package/dist/types/get-entity-tool-types.js +76 -12
- package/dist/types/guest-management-tool-types.js +103 -1
- package/dist/types/location-tool-types.js +100 -14
- package/dist/types/lost-badge-tool-types.js +17 -2
- package/dist/types/lpr-tool-types.js +47 -8
- package/dist/types/onguard-tool-types.js +21 -1
- package/dist/types/policy-alerts-tool-types.js +1 -1
- package/dist/types/report-tool-types.js +29 -1
- package/dist/types/schedule-tool-types.js +370 -0
- package/dist/types/schema.js +479 -9
- package/dist/types/time-conversion-tool-types.js +3 -4
- package/dist/types/update-tool-types.js +40 -18
- package/dist/types/user-tool-types.js +44 -1
- package/dist/types/video-walls-tool-types.js +30 -2
- package/dist/types/zod-schemas.js +5128 -996
- package/dist/util.js +62 -3
- package/dist/utils/entity-name-match.js +11 -0
- package/package.json +3 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { postApi } from "../network/network.js";
|
|
1
|
+
import { apiWarning, postApi, throwIfApiError } from "../network/network.js";
|
|
2
|
+
import { cachedPostApi } from "../network/org-reference-cache.js";
|
|
2
3
|
const PERMISSION_RANK = {
|
|
3
4
|
LIVEONLY: 0,
|
|
4
5
|
READONLY: 1,
|
|
@@ -14,9 +15,7 @@ export async function unlockDoor(doorUuid, requestModifiers, sessionId) {
|
|
|
14
15
|
modifiers: requestModifiers,
|
|
15
16
|
sessionId,
|
|
16
17
|
});
|
|
17
|
-
|
|
18
|
-
throw new Error(JSON.stringify(res));
|
|
19
|
-
}
|
|
18
|
+
throwIfApiError(res);
|
|
20
19
|
return { success: true, doorUuid };
|
|
21
20
|
}
|
|
22
21
|
export async function getAccessControlGroups(requestModifiers, sessionId) {
|
|
@@ -26,9 +25,7 @@ export async function getAccessControlGroups(requestModifiers, sessionId) {
|
|
|
26
25
|
modifiers: requestModifiers,
|
|
27
26
|
sessionId,
|
|
28
27
|
});
|
|
29
|
-
|
|
30
|
-
throw new Error(JSON.stringify(res));
|
|
31
|
-
}
|
|
28
|
+
throwIfApiError(res);
|
|
32
29
|
return (res.groups?.map((group) => ({
|
|
33
30
|
uuid: group.uuid ?? undefined,
|
|
34
31
|
name: group.name ?? undefined,
|
|
@@ -44,9 +41,7 @@ export async function getCredentialsByUser(userUuid, requestModifiers, sessionId
|
|
|
44
41
|
modifiers: requestModifiers,
|
|
45
42
|
sessionId,
|
|
46
43
|
});
|
|
47
|
-
|
|
48
|
-
throw new Error(JSON.stringify(res));
|
|
49
|
-
}
|
|
44
|
+
throwIfApiError(res);
|
|
50
45
|
return (res.credentials?.map((cred) => ({
|
|
51
46
|
uuid: cred.uuid ?? undefined,
|
|
52
47
|
userUuid: cred.userUuid ?? undefined,
|
|
@@ -62,9 +57,7 @@ export async function getLockdownPlans(requestModifiers, sessionId) {
|
|
|
62
57
|
modifiers: requestModifiers,
|
|
63
58
|
sessionId,
|
|
64
59
|
});
|
|
65
|
-
|
|
66
|
-
throw new Error(JSON.stringify(res));
|
|
67
|
-
}
|
|
60
|
+
throwIfApiError(res);
|
|
68
61
|
return (res.lockdownPlans?.map((plan) => ({
|
|
69
62
|
uuid: plan.uuid ?? undefined,
|
|
70
63
|
name: plan.name ?? undefined,
|
|
@@ -80,9 +73,7 @@ export async function activateLockdown(locationUuid, lockdownPlanUuid, requestMo
|
|
|
80
73
|
modifiers: requestModifiers,
|
|
81
74
|
sessionId,
|
|
82
75
|
});
|
|
83
|
-
|
|
84
|
-
throw new Error(JSON.stringify(res));
|
|
85
|
-
}
|
|
76
|
+
throwIfApiError(res);
|
|
86
77
|
return { success: true, locationUuid, action: "activated" };
|
|
87
78
|
}
|
|
88
79
|
export async function deactivateLockdown(locationUuid, lockdownPlanUuid, requestModifiers, sessionId) {
|
|
@@ -92,9 +83,7 @@ export async function deactivateLockdown(locationUuid, lockdownPlanUuid, request
|
|
|
92
83
|
modifiers: requestModifiers,
|
|
93
84
|
sessionId,
|
|
94
85
|
});
|
|
95
|
-
|
|
96
|
-
throw new Error(JSON.stringify(res));
|
|
97
|
-
}
|
|
86
|
+
throwIfApiError(res);
|
|
98
87
|
return { success: true, locationUuid, action: "deactivated" };
|
|
99
88
|
}
|
|
100
89
|
export async function getDoorScheduleExceptions(locationUuid, requestModifiers, sessionId) {
|
|
@@ -104,9 +93,7 @@ export async function getDoorScheduleExceptions(locationUuid, requestModifiers,
|
|
|
104
93
|
modifiers: requestModifiers,
|
|
105
94
|
sessionId,
|
|
106
95
|
});
|
|
107
|
-
|
|
108
|
-
throw new Error(JSON.stringify(res));
|
|
109
|
-
}
|
|
96
|
+
throwIfApiError(res);
|
|
110
97
|
return (res.exceptions?.map((exc) => ({
|
|
111
98
|
uuid: exc.uuid ?? undefined,
|
|
112
99
|
name: exc.name ?? undefined,
|
|
@@ -129,9 +116,7 @@ export async function getAccessGrants(locationUuid, requestModifiers, sessionId)
|
|
|
129
116
|
modifiers: requestModifiers,
|
|
130
117
|
sessionId,
|
|
131
118
|
});
|
|
132
|
-
|
|
133
|
-
throw new Error(JSON.stringify(res));
|
|
134
|
-
}
|
|
119
|
+
throwIfApiError(res);
|
|
135
120
|
const filterNulls = (arr) => arr?.filter((v) => v !== null) ?? [];
|
|
136
121
|
return (res.accessGrants?.map((grant) => ({
|
|
137
122
|
uuid: grant.uuid ?? undefined,
|
|
@@ -177,7 +162,7 @@ export async function getRemoteUnlockUsers(locationUuid, requestModifiers, sessi
|
|
|
177
162
|
modifiers: requestModifiers,
|
|
178
163
|
sessionId,
|
|
179
164
|
}),
|
|
180
|
-
|
|
165
|
+
cachedPostApi({
|
|
181
166
|
route: "/component/findAccessControlledDoors",
|
|
182
167
|
body: {},
|
|
183
168
|
modifiers: requestModifiers,
|
|
@@ -190,12 +175,9 @@ export async function getRemoteUnlockUsers(locationUuid, requestModifiers, sessi
|
|
|
190
175
|
sessionId,
|
|
191
176
|
}),
|
|
192
177
|
]);
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
throw new Error(JSON.stringify(doorsRes));
|
|
197
|
-
if (usersRes.error)
|
|
198
|
-
throw new Error(JSON.stringify(usersRes));
|
|
178
|
+
throwIfApiError(permGroupsRes);
|
|
179
|
+
throwIfApiError(doorsRes);
|
|
180
|
+
throwIfApiError(usersRes);
|
|
199
181
|
const permissionGroups = permGroupsRes.permissionGroups ?? [];
|
|
200
182
|
const groupMembership = {};
|
|
201
183
|
for (const [groupUuid, userUuids] of Object.entries(permGroupsRes.groupMembership ?? {})) {
|
|
@@ -254,3 +236,220 @@ export async function getRemoteUnlockUsers(locationUuid, requestModifiers, sessi
|
|
|
254
236
|
const totalUsers = seenUsers.size;
|
|
255
237
|
return { doors: doorNames, totalUsers, groups };
|
|
256
238
|
}
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
240
|
+
// Access control groups
|
|
241
|
+
// ---------------------------------------------------------------------------
|
|
242
|
+
export async function createAccessControlGroup(name, description, userUuids, requestModifiers, sessionId) {
|
|
243
|
+
const res = await postApi({
|
|
244
|
+
route: "/accesscontrol/createAccessControlGroup",
|
|
245
|
+
body: {
|
|
246
|
+
name,
|
|
247
|
+
description: description || undefined,
|
|
248
|
+
userUuids: userUuids ?? [],
|
|
249
|
+
},
|
|
250
|
+
modifiers: requestModifiers,
|
|
251
|
+
sessionId,
|
|
252
|
+
});
|
|
253
|
+
throwIfApiError(res);
|
|
254
|
+
return {
|
|
255
|
+
success: true,
|
|
256
|
+
uuid: res.group?.uuid ?? undefined,
|
|
257
|
+
memberCount: res.groupMembers?.length ?? userUuids?.length ?? 0,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
export async function updateAccessControlGroup(groupUuid, changes, requestModifiers, sessionId) {
|
|
261
|
+
const res = await postApi({
|
|
262
|
+
route: "/accesscontrol/updateAccessControlGroup",
|
|
263
|
+
body: {
|
|
264
|
+
groupUuid,
|
|
265
|
+
name: changes.name,
|
|
266
|
+
description: changes.description,
|
|
267
|
+
},
|
|
268
|
+
modifiers: requestModifiers,
|
|
269
|
+
sessionId,
|
|
270
|
+
});
|
|
271
|
+
throwIfApiError(res);
|
|
272
|
+
return { success: true, uuid: groupUuid };
|
|
273
|
+
}
|
|
274
|
+
export async function deleteAccessControlGroup(groupUuid, requestModifiers, sessionId) {
|
|
275
|
+
const res = await postApi({
|
|
276
|
+
route: "/accesscontrol/deleteAccessControlGroup",
|
|
277
|
+
body: { groupUuid },
|
|
278
|
+
modifiers: requestModifiers,
|
|
279
|
+
sessionId,
|
|
280
|
+
});
|
|
281
|
+
throwIfApiError(res);
|
|
282
|
+
return { success: true, uuid: groupUuid };
|
|
283
|
+
}
|
|
284
|
+
export async function changeAccessControlGroupMembers(groupUuid, userUuids, action, requestModifiers, sessionId) {
|
|
285
|
+
const res = await postApi({
|
|
286
|
+
route: action === "add"
|
|
287
|
+
? "/accesscontrol/addUsersToAccessControlGroup"
|
|
288
|
+
: "/accesscontrol/removeUsersFromAccessControlGroup",
|
|
289
|
+
body: { groupUuid, userUuids },
|
|
290
|
+
modifiers: requestModifiers,
|
|
291
|
+
sessionId,
|
|
292
|
+
});
|
|
293
|
+
throwIfApiError(res);
|
|
294
|
+
return { success: true, uuid: groupUuid, userCount: userUuids.length };
|
|
295
|
+
}
|
|
296
|
+
// ---------------------------------------------------------------------------
|
|
297
|
+
// Location access grants
|
|
298
|
+
// ---------------------------------------------------------------------------
|
|
299
|
+
/**
|
|
300
|
+
* `createAccessGrant` / `updateAccessGrant` both take the WHOLE grant object,
|
|
301
|
+
* so update callers must read the current grant and merge. Both can also
|
|
302
|
+
* succeed while reporting doors whose access-control licences are expired or
|
|
303
|
+
* unassigned — those doors silently do not get access, so they are surfaced.
|
|
304
|
+
*/
|
|
305
|
+
export async function writeAccessGrant(accessGrant, mode, requestModifiers, sessionId) {
|
|
306
|
+
const res = await postApi({
|
|
307
|
+
route: mode === "create"
|
|
308
|
+
? "/accesscontrol/createAccessGrant"
|
|
309
|
+
: "/accesscontrol/updateAccessGrant",
|
|
310
|
+
body: {
|
|
311
|
+
accessGrant,
|
|
312
|
+
},
|
|
313
|
+
modifiers: requestModifiers,
|
|
314
|
+
sessionId,
|
|
315
|
+
});
|
|
316
|
+
throwIfApiError(res);
|
|
317
|
+
return {
|
|
318
|
+
success: true,
|
|
319
|
+
uuid: res.accessGrant?.uuid ?? accessGrant.uuid,
|
|
320
|
+
expiredACDLicensesDoorUuids: res.expiredACDLicensesDoorUuids?.filter((value) => !!value) ?? [],
|
|
321
|
+
unassignedACDLicensesDoorUuids: res.unassignedACDLicensesDoorUuids?.filter((value) => !!value) ?? [],
|
|
322
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
export async function deleteAccessGrant(accessGrantUuid, requestModifiers, sessionId) {
|
|
326
|
+
const res = await postApi({
|
|
327
|
+
route: "/accesscontrol/deleteLocationAccessGrant",
|
|
328
|
+
body: {
|
|
329
|
+
accessGrantUuid,
|
|
330
|
+
},
|
|
331
|
+
modifiers: requestModifiers,
|
|
332
|
+
sessionId,
|
|
333
|
+
});
|
|
334
|
+
throwIfApiError(res);
|
|
335
|
+
return { success: true, uuid: accessGrantUuid, warningMsg: res.warningMsg ?? undefined };
|
|
336
|
+
}
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// Credentials
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
/** Attach an existing (unassigned) physical credential to a user. */
|
|
341
|
+
export async function assignCredential(credentialHexValue, userUuid, requestModifiers, sessionId) {
|
|
342
|
+
const res = await postApi({
|
|
343
|
+
route: "/accesscontrol/assignAccessControlCredential",
|
|
344
|
+
body: {
|
|
345
|
+
credentialHexValue,
|
|
346
|
+
userUuid,
|
|
347
|
+
},
|
|
348
|
+
modifiers: requestModifiers,
|
|
349
|
+
sessionId,
|
|
350
|
+
});
|
|
351
|
+
throwIfApiError(res);
|
|
352
|
+
return { success: true, userUuid, warningMsg: apiWarning(res) };
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* The four credential state changes that share a `{credentialUuid}` body.
|
|
356
|
+
*
|
|
357
|
+
* They are NOT interchangeable and the difference matters operationally:
|
|
358
|
+
* suspend is reversible (unsuspend restores it), revoke detaches the credential
|
|
359
|
+
* from its user but keeps the record, and delete destroys the record entirely.
|
|
360
|
+
*/
|
|
361
|
+
export async function changeCredentialState(credentialUuid, action, requestModifiers, sessionId) {
|
|
362
|
+
const routes = {
|
|
363
|
+
revoke: "/accesscontrol/revokeAccessControlCredential",
|
|
364
|
+
suspend: "/accesscontrol/suspendAccessControlCredential",
|
|
365
|
+
unsuspend: "/accesscontrol/unsuspendAccessControlCredential",
|
|
366
|
+
delete: "/accesscontrol/deleteAccessControlCredential",
|
|
367
|
+
};
|
|
368
|
+
const res = await postApi({
|
|
369
|
+
route: routes[action],
|
|
370
|
+
body: {
|
|
371
|
+
credentialUuid,
|
|
372
|
+
},
|
|
373
|
+
modifiers: requestModifiers,
|
|
374
|
+
sessionId,
|
|
375
|
+
});
|
|
376
|
+
throwIfApiError(res);
|
|
377
|
+
return { success: true, uuid: credentialUuid, warningMsg: apiWarning(res) };
|
|
378
|
+
}
|
|
379
|
+
export async function updateCredentialNote(credentialUuid, note, requestModifiers, sessionId) {
|
|
380
|
+
const res = await postApi({
|
|
381
|
+
route: "/accesscontrol/updateAccessControlCredentialNote",
|
|
382
|
+
body: {
|
|
383
|
+
credentialUuid,
|
|
384
|
+
note,
|
|
385
|
+
},
|
|
386
|
+
modifiers: requestModifiers,
|
|
387
|
+
sessionId,
|
|
388
|
+
});
|
|
389
|
+
throwIfApiError(res);
|
|
390
|
+
return { success: true, uuid: credentialUuid, warningMsg: apiWarning(res) };
|
|
391
|
+
}
|
|
392
|
+
// ---------------------------------------------------------------------------
|
|
393
|
+
// Lockdown plans
|
|
394
|
+
// ---------------------------------------------------------------------------
|
|
395
|
+
export async function getLockdownPlan(lockdownPlanUuid, requestModifiers, sessionId) {
|
|
396
|
+
const res = await postApi({
|
|
397
|
+
route: "/accesscontrol/lockdownPlan/getLockdownPlan",
|
|
398
|
+
body: {
|
|
399
|
+
lockdownPlanUuid,
|
|
400
|
+
},
|
|
401
|
+
modifiers: requestModifiers,
|
|
402
|
+
sessionId,
|
|
403
|
+
});
|
|
404
|
+
throwIfApiError(res);
|
|
405
|
+
return res.lockdownPlan ?? undefined;
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Rename a lockdown plan.
|
|
409
|
+
*
|
|
410
|
+
* `updateLocationLockdownPlan` takes the plan's door-state map, activation,
|
|
411
|
+
* deactivation and test plans as sibling fields, so a name-only body would blank
|
|
412
|
+
* them — which on a lockdown plan means doors silently stop locking in an
|
|
413
|
+
* emergency. Everything except the name is therefore read back and resent
|
|
414
|
+
* verbatim. Authoring those nested plans from natural language is deliberately
|
|
415
|
+
* NOT exposed.
|
|
416
|
+
*/
|
|
417
|
+
export async function renameLockdownPlan(lockdownPlanUuid, name, requestModifiers, sessionId) {
|
|
418
|
+
const existing = await getLockdownPlan(lockdownPlanUuid, requestModifiers, sessionId);
|
|
419
|
+
if (!existing)
|
|
420
|
+
return { success: false, missing: true };
|
|
421
|
+
const res = await postApi({
|
|
422
|
+
route: "/accesscontrol/lockdownPlan/updateLocationLockdownPlan",
|
|
423
|
+
body: {
|
|
424
|
+
lockdownPlanUuid,
|
|
425
|
+
name,
|
|
426
|
+
activationPlan: existing.activationPlan,
|
|
427
|
+
deactivationPlan: existing.deactivationPlan,
|
|
428
|
+
defaultLockdownState: existing.defaultLockdownState,
|
|
429
|
+
doorLockdownStateMap: existing.doorLockdownStateMap,
|
|
430
|
+
physicalAccess: existing.physicalAccess,
|
|
431
|
+
testPlan: existing.testPlan,
|
|
432
|
+
},
|
|
433
|
+
modifiers: requestModifiers,
|
|
434
|
+
sessionId,
|
|
435
|
+
});
|
|
436
|
+
throwIfApiError(res);
|
|
437
|
+
return {
|
|
438
|
+
success: true,
|
|
439
|
+
missing: false,
|
|
440
|
+
uuid: lockdownPlanUuid,
|
|
441
|
+
previousName: existing.name ?? undefined,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
export async function deleteLockdownPlan(lockdownPlanUuid, requestModifiers, sessionId) {
|
|
445
|
+
const res = await postApi({
|
|
446
|
+
route: "/accesscontrol/lockdownPlan/deleteLockdownPlan",
|
|
447
|
+
body: {
|
|
448
|
+
lockdownPlanUuid,
|
|
449
|
+
},
|
|
450
|
+
modifiers: requestModifiers,
|
|
451
|
+
sessionId,
|
|
452
|
+
});
|
|
453
|
+
throwIfApiError(res);
|
|
454
|
+
return { success: true, uuid: lockdownPlanUuid };
|
|
455
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { postApi } from "../network/network.js";
|
|
1
|
+
import { apiWarning, postApi, throwIfApiError } from "../network/network.js";
|
|
2
2
|
export async function getOrgAlarmStatus(requestModifiers, sessionId) {
|
|
3
3
|
const res = await postApi({
|
|
4
4
|
route: "/alertmonitoring/orgStatus",
|
|
@@ -63,3 +63,52 @@ export async function getLocationAlarmStatus(locationUuid, requestModifiers, ses
|
|
|
63
63
|
notEnabledReason: res.status?.notEnabledReason ?? undefined,
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Alarm monitoring write paths
|
|
68
|
+
//
|
|
69
|
+
// `armMonitoring` / `disarmMonitoring` are deliberately NOT exposed: their
|
|
70
|
+
// `armingRule` / `disarmingRule` bodies (activeSinceMs, armAllUntilMs,
|
|
71
|
+
// armedAlarmPolicies) have semantics that are not documented in the api2 spec,
|
|
72
|
+
// and getting them wrong leaves a real alarm system in a state nobody asked
|
|
73
|
+
// for. enable/disable below are the unambiguous pair, and they require the
|
|
74
|
+
// location's alarm PIN.
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
/**
|
|
77
|
+
* Turn alarm monitoring on or off for a location.
|
|
78
|
+
*
|
|
79
|
+
* `pin` is the location's alarm PIN, supplied by the user. It is passed straight
|
|
80
|
+
* through to api2 and must never be echoed into a tool result, a note, or a log
|
|
81
|
+
* line.
|
|
82
|
+
*/
|
|
83
|
+
export async function setLocationMonitoring(locationUuid, pin, enabled, requestModifiers, sessionId) {
|
|
84
|
+
const res = await postApi({
|
|
85
|
+
route: enabled
|
|
86
|
+
? "/alertmonitoring/enableMonitoringForLocation"
|
|
87
|
+
: "/alertmonitoring/disableMonitoringForLocation",
|
|
88
|
+
body: {
|
|
89
|
+
locationUuid,
|
|
90
|
+
pin,
|
|
91
|
+
},
|
|
92
|
+
modifiers: requestModifiers,
|
|
93
|
+
sessionId,
|
|
94
|
+
});
|
|
95
|
+
throwIfApiError(res);
|
|
96
|
+
return { success: true, locationUuid, enabled, warningMsg: res.warningMsg ?? undefined };
|
|
97
|
+
}
|
|
98
|
+
export async function changeThreatCaseState(threatCaseUuid, action, requestModifiers, sessionId) {
|
|
99
|
+
const routes = {
|
|
100
|
+
dismiss: "/alertmonitoring/dismissThreatCase",
|
|
101
|
+
cancel: "/alertmonitoring/cancelThreatCase",
|
|
102
|
+
escalate: "/alertmonitoring/escalateThreatCaseToAlarm",
|
|
103
|
+
};
|
|
104
|
+
const res = await postApi({
|
|
105
|
+
route: routes[action],
|
|
106
|
+
body: {
|
|
107
|
+
uuid: threatCaseUuid,
|
|
108
|
+
},
|
|
109
|
+
modifiers: requestModifiers,
|
|
110
|
+
sessionId,
|
|
111
|
+
});
|
|
112
|
+
throwIfApiError(res);
|
|
113
|
+
return { success: true, uuid: threatCaseUuid, action, warningMsg: apiWarning(res) };
|
|
114
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { postApi } from "../network/network.js";
|
|
1
|
+
import { postApi, throwIfApiError } from "../network/network.js";
|
|
2
2
|
import { ChatFollowUpActionEnum, } from "../types/automated-prompts-tool-types.js";
|
|
3
3
|
import { formatIsoWithOffset } from "../util.js";
|
|
4
4
|
function extractNotifyUserUuids(followUpActions) {
|
|
@@ -88,8 +88,7 @@ export async function listAutomatedPrompts(pageRequest, requestModifiers, sessio
|
|
|
88
88
|
modifiers: requestModifiers,
|
|
89
89
|
sessionId,
|
|
90
90
|
});
|
|
91
|
-
|
|
92
|
-
throw new Error(JSON.stringify(res));
|
|
91
|
+
throwIfApiError(res);
|
|
93
92
|
const settingsList = (res.settingsList ?? [])
|
|
94
93
|
.map((p) => toSummary(p, timeZone))
|
|
95
94
|
.filter((s) => s !== undefined);
|
|
@@ -105,8 +104,7 @@ export async function getAutomatedPrompt(promptUuid, requestModifiers, sessionId
|
|
|
105
104
|
modifiers: requestModifiers,
|
|
106
105
|
sessionId,
|
|
107
106
|
});
|
|
108
|
-
|
|
109
|
-
throw new Error(JSON.stringify(res));
|
|
107
|
+
throwIfApiError(res);
|
|
110
108
|
return toSummary(res.settings, timeZone);
|
|
111
109
|
}
|
|
112
110
|
export async function createAutomatedPrompt(input, requestModifiers, sessionId, timeZone) {
|
|
@@ -117,8 +115,7 @@ export async function createAutomatedPrompt(input, requestModifiers, sessionId,
|
|
|
117
115
|
modifiers: requestModifiers,
|
|
118
116
|
sessionId,
|
|
119
117
|
});
|
|
120
|
-
|
|
121
|
-
throw new Error(JSON.stringify(res));
|
|
118
|
+
throwIfApiError(res);
|
|
122
119
|
return toSummary(res.settings, timeZone);
|
|
123
120
|
}
|
|
124
121
|
export async function updateAutomatedPrompt(input, requestModifiers, sessionId, timeZone) {
|
|
@@ -129,8 +126,7 @@ export async function updateAutomatedPrompt(input, requestModifiers, sessionId,
|
|
|
129
126
|
modifiers: requestModifiers,
|
|
130
127
|
sessionId,
|
|
131
128
|
});
|
|
132
|
-
|
|
133
|
-
throw new Error(JSON.stringify(res));
|
|
129
|
+
throwIfApiError(res);
|
|
134
130
|
return toSummary(res.settings, timeZone);
|
|
135
131
|
}
|
|
136
132
|
export async function deleteAutomatedPrompt(promptUuid, requestModifiers, sessionId) {
|
|
@@ -140,8 +136,7 @@ export async function deleteAutomatedPrompt(promptUuid, requestModifiers, sessio
|
|
|
140
136
|
modifiers: requestModifiers,
|
|
141
137
|
sessionId,
|
|
142
138
|
});
|
|
143
|
-
|
|
144
|
-
throw new Error(JSON.stringify(res));
|
|
139
|
+
throwIfApiError(res);
|
|
145
140
|
return { ok: true, action: "deleted" };
|
|
146
141
|
}
|
|
147
142
|
export async function getAutomatedPromptChatHistory(promptUuid, pageRequest, requestModifiers, sessionId, timeZone) {
|
|
@@ -155,8 +150,7 @@ export async function getAutomatedPromptChatHistory(promptUuid, pageRequest, req
|
|
|
155
150
|
modifiers: requestModifiers,
|
|
156
151
|
sessionId,
|
|
157
152
|
});
|
|
158
|
-
|
|
159
|
-
throw new Error(JSON.stringify(res));
|
|
153
|
+
throwIfApiError(res);
|
|
160
154
|
const chatHistory = (res.chatHistory ?? [])
|
|
161
155
|
.map((r) => toHistoryEntry(r, timeZone))
|
|
162
156
|
.filter((e) => e !== undefined);
|
|
@@ -175,8 +169,7 @@ export async function shareAutomatedPromptResponse(chatUuid, visibility, request
|
|
|
175
169
|
modifiers: requestModifiers,
|
|
176
170
|
sessionId,
|
|
177
171
|
});
|
|
178
|
-
|
|
179
|
-
throw new Error(JSON.stringify(res));
|
|
172
|
+
throwIfApiError(res);
|
|
180
173
|
return { ok: true, action: `visibility set to ${visibility}` };
|
|
181
174
|
}
|
|
182
175
|
export async function verifyJobScheduled(promptUuid, requestModifiers, sessionId) {
|
|
@@ -186,8 +179,7 @@ export async function verifyJobScheduled(promptUuid, requestModifiers, sessionId
|
|
|
186
179
|
modifiers: requestModifiers,
|
|
187
180
|
sessionId,
|
|
188
181
|
});
|
|
189
|
-
|
|
190
|
-
throw new Error(JSON.stringify(res));
|
|
182
|
+
throwIfApiError(res);
|
|
191
183
|
return {
|
|
192
184
|
scheduleExpression: res.scheduleExpression ?? undefined,
|
|
193
185
|
scheduleTimezone: res.scheduleTimezone ?? undefined,
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { postApi, throwIfApiError } from "../network/network.js";
|
|
2
|
+
import { describeWeeklyInterval } from "../util.js";
|
|
3
|
+
function mapPolicy(raw) {
|
|
4
|
+
return {
|
|
5
|
+
uuid: raw.uuid ?? undefined,
|
|
6
|
+
name: raw.name ?? undefined,
|
|
7
|
+
description: raw.description ?? undefined,
|
|
8
|
+
defaultActivities: raw.defaultTriggers?.flatMap(trigger => trigger?.activity ? [trigger.activity] : []) ?? [],
|
|
9
|
+
scheduledTriggers: raw.scheduledTriggers?.flatMap(trigger => {
|
|
10
|
+
if (!trigger)
|
|
11
|
+
return [];
|
|
12
|
+
return [
|
|
13
|
+
{
|
|
14
|
+
scheduleUuid: trigger.schedule?.uuid ?? undefined,
|
|
15
|
+
scheduleName: trigger.schedule?.name ?? undefined,
|
|
16
|
+
scheduleWindows: trigger.schedule?.intervalList?.flatMap(interval => {
|
|
17
|
+
const window = describeWeeklyInterval(interval?.minuteOfWeekStart, interval?.minuteOfWeekStop);
|
|
18
|
+
return window ? [window] : [];
|
|
19
|
+
}) ?? [],
|
|
20
|
+
activities: trigger.triggerSet?.flatMap(entry => entry?.activity ? [entry.activity] : []) ?? [],
|
|
21
|
+
},
|
|
22
|
+
];
|
|
23
|
+
}) ?? [],
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export async function getCameraPolicies(requestModifiers, sessionId) {
|
|
27
|
+
const res = await postApi({
|
|
28
|
+
route: "/policy/getCameraPolicies",
|
|
29
|
+
body: {},
|
|
30
|
+
modifiers: requestModifiers,
|
|
31
|
+
sessionId,
|
|
32
|
+
});
|
|
33
|
+
throwIfApiError(res);
|
|
34
|
+
return {
|
|
35
|
+
policies: res.policies?.flatMap(raw => (raw ? [mapPolicy(raw)] : [])) ?? [],
|
|
36
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* api2's `updateCameraPolicy` REPLACES the whole policy object, so any field the
|
|
41
|
+
* caller leaves out is blanked. Callers must read the current policy and merge
|
|
42
|
+
* before calling this — see the tool's update branch.
|
|
43
|
+
*/
|
|
44
|
+
export async function updateCameraPolicy(policy, requestModifiers, sessionId) {
|
|
45
|
+
const res = await postApi({
|
|
46
|
+
route: "/policy/updateCameraPolicy",
|
|
47
|
+
body: { policy },
|
|
48
|
+
modifiers: requestModifiers,
|
|
49
|
+
sessionId,
|
|
50
|
+
});
|
|
51
|
+
throwIfApiError(res);
|
|
52
|
+
return {
|
|
53
|
+
updated: { success: true, policyUuid: policy.uuid },
|
|
54
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export async function deleteCameraPolicy(policyUuid, requestModifiers, sessionId) {
|
|
58
|
+
const res = await postApi({
|
|
59
|
+
route: "/policy/deleteCameraPolicy",
|
|
60
|
+
body: { policyUuid },
|
|
61
|
+
modifiers: requestModifiers,
|
|
62
|
+
sessionId,
|
|
63
|
+
});
|
|
64
|
+
throwIfApiError(res);
|
|
65
|
+
return {
|
|
66
|
+
deleted: { success: true, policyUuid },
|
|
67
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The camera holds its `policyUuid`, not the policy a camera list — so "which
|
|
72
|
+
* cameras does this policy cover" is only answerable by listing cameras and
|
|
73
|
+
* grouping. Callers use this both to enrich reads and to warn before a delete.
|
|
74
|
+
*/
|
|
75
|
+
export async function getCamerasByPolicy(requestModifiers, sessionId) {
|
|
76
|
+
const res = await postApi({
|
|
77
|
+
route: "/camera/getMinimalCameraStateList",
|
|
78
|
+
body: {},
|
|
79
|
+
modifiers: requestModifiers,
|
|
80
|
+
sessionId,
|
|
81
|
+
});
|
|
82
|
+
throwIfApiError(res);
|
|
83
|
+
const byPolicy = new Map();
|
|
84
|
+
for (const camera of res.cameraStates ?? []) {
|
|
85
|
+
if (!camera?.policyUuid)
|
|
86
|
+
continue;
|
|
87
|
+
const bucket = byPolicy.get(camera.policyUuid) ?? [];
|
|
88
|
+
bucket.push({ uuid: camera.uuid ?? undefined, name: camera.name ?? undefined });
|
|
89
|
+
byPolicy.set(camera.policyUuid, bucket);
|
|
90
|
+
}
|
|
91
|
+
return byPolicy;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Assign or clear the policy on a set of cameras. Passing a null `policyUuid`
|
|
95
|
+
* with `policyUuidUpdated: true` is how api2 spells "unassign", so the caller
|
|
96
|
+
* must be explicit about which one it means.
|
|
97
|
+
*
|
|
98
|
+
* It must be `null` and not `""`: `policyUuid` is declared
|
|
99
|
+
* `format: RUUID, nullable: true`, so an empty string fails deserialization and
|
|
100
|
+
* api2 rejects the whole bulk body with
|
|
101
|
+
* `HTTP 400 {"msg":"JSON doesn't match expected object structure"}`.
|
|
102
|
+
* Verified against api2.itg on 2026-08-05 — a mocked test cannot catch this.
|
|
103
|
+
*/
|
|
104
|
+
export async function setPolicyOnCameras(cameraUuids, policyUuid, requestModifiers, sessionId) {
|
|
105
|
+
const res = await postApi({
|
|
106
|
+
route: "/camera/updateDetailsBulkV2",
|
|
107
|
+
body: {
|
|
108
|
+
cameraBulkDetails: cameraUuids.map(uuid => ({
|
|
109
|
+
uuid,
|
|
110
|
+
policyUuid,
|
|
111
|
+
policyUuidUpdated: true,
|
|
112
|
+
})),
|
|
113
|
+
},
|
|
114
|
+
modifiers: requestModifiers,
|
|
115
|
+
sessionId,
|
|
116
|
+
});
|
|
117
|
+
throwIfApiError(res);
|
|
118
|
+
return {
|
|
119
|
+
cameraCount: cameraUuids.length,
|
|
120
|
+
warningMsg: res.warningMsg ?? undefined,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getLogger } from "../logger.js";
|
|
2
|
-
import { postApi } from "../network/network.js";
|
|
2
|
+
import { postApi, throwIfApiError } from "../network/network.js";
|
|
3
3
|
import { removeNullFields } from "../util.js";
|
|
4
4
|
const logger = getLogger("camera-tool");
|
|
5
5
|
const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
@@ -142,8 +142,7 @@ export async function getCameraMediaUris(cameraUuid, requestModifiers, sessionId
|
|
|
142
142
|
modifiers: requestModifiers,
|
|
143
143
|
sessionId,
|
|
144
144
|
});
|
|
145
|
-
|
|
146
|
-
throw new Error(JSON.stringify(res));
|
|
145
|
+
throwIfApiError(res);
|
|
147
146
|
return res;
|
|
148
147
|
}
|
|
149
148
|
export async function getCameraAIThresholds(cameraUuid, requestModifiers, sessionId) {
|
|
@@ -153,8 +152,7 @@ export async function getCameraAIThresholds(cameraUuid, requestModifiers, sessio
|
|
|
153
152
|
modifiers: requestModifiers,
|
|
154
153
|
sessionId,
|
|
155
154
|
});
|
|
156
|
-
|
|
157
|
-
throw new Error(JSON.stringify(res));
|
|
155
|
+
throwIfApiError(res);
|
|
158
156
|
return res;
|
|
159
157
|
}
|
|
160
158
|
export async function updateCameraSettings(cameraUuid, update, requestModifiers, sessionId) {
|