feat(nodes): expose device diagnostics and notification actions

This commit is contained in:
Ayaan Zaidi
2026-02-27 09:40:32 +05:30
committed by Ayaan Zaidi
parent d0ec3de588
commit 29f5da5b2a
3 changed files with 152 additions and 4 deletions

View File

@@ -42,14 +42,18 @@ const NODES_TOOL_ACTIONS = [
"screen_record",
"location_get",
"notifications_list",
"notifications_action",
"device_status",
"device_info",
"device_permissions",
"device_health",
"run",
"invoke",
] as const;
const NOTIFY_PRIORITIES = ["passive", "active", "timeSensitive"] as const;
const NOTIFY_DELIVERIES = ["system", "overlay", "auto"] as const;
const NOTIFICATIONS_ACTIONS = ["open", "dismiss", "reply"] as const;
const CAMERA_FACING = ["front", "back", "both"] as const;
const LOCATION_ACCURACY = ["coarse", "balanced", "precise"] as const;
type GatewayCallOptions = ReturnType<typeof readGatewayCallOptions>;
@@ -117,6 +121,10 @@ const NodesToolSchema = Type.Object({
maxAgeMs: Type.Optional(Type.Number()),
locationTimeoutMs: Type.Optional(Type.Number()),
desiredAccuracy: optionalStringEnum(LOCATION_ACCURACY),
// notifications_action
notificationAction: optionalStringEnum(NOTIFICATIONS_ACTIONS),
notificationKey: Type.Optional(Type.String()),
notificationReplyText: Type.Optional(Type.String()),
// run
command: Type.Optional(Type.Array(Type.String())),
cwd: Type.Optional(Type.String()),
@@ -302,7 +310,9 @@ export function createNodesTool(options?: {
case "camera_list":
case "notifications_list":
case "device_status":
case "device_info": {
case "device_info":
case "device_permissions":
case "device_health": {
const node = readStringParam(params, "node", { required: true });
const command =
action === "camera_list"
@@ -311,7 +321,11 @@ export function createNodesTool(options?: {
? "notifications.list"
: action === "device_status"
? "device.status"
: "device.info";
: action === "device_info"
? "device.info"
: action === "device_permissions"
? "device.permissions"
: "device.health";
const payloadRaw = await invokeNodeCommandPayload({
gatewayOpts,
node,
@@ -321,6 +335,41 @@ export function createNodesTool(options?: {
payloadRaw && typeof payloadRaw === "object" && payloadRaw !== null ? payloadRaw : {};
return jsonResult(payload);
}
case "notifications_action": {
const node = readStringParam(params, "node", { required: true });
const notificationKey = readStringParam(params, "notificationKey", { required: true });
const notificationAction =
typeof params.notificationAction === "string"
? params.notificationAction.trim().toLowerCase()
: "";
if (
notificationAction !== "open" &&
notificationAction !== "dismiss" &&
notificationAction !== "reply"
) {
throw new Error("notificationAction must be open|dismiss|reply");
}
const notificationReplyText =
typeof params.notificationReplyText === "string"
? params.notificationReplyText.trim()
: undefined;
if (notificationAction === "reply" && !notificationReplyText) {
throw new Error("notificationReplyText required when notificationAction=reply");
}
const payloadRaw = await invokeNodeCommandPayload({
gatewayOpts,
node,
command: "notifications.actions",
commandParams: {
key: notificationKey,
action: notificationAction,
replyText: notificationReplyText,
},
});
const payload =
payloadRaw && typeof payloadRaw === "object" && payloadRaw !== null ? payloadRaw : {};
return jsonResult(payload);
}
case "camera_clip": {
const node = readStringParam(params, "node", { required: true });
const nodeId = await resolveNodeId(gatewayOpts, node);