Add macOS companion node status and system.nodes APIs

This commit is contained in:
William Valentin
2026-02-16 12:41:58 -08:00
parent 8a0b4f3dbb
commit bea4c54f3b
14 changed files with 500 additions and 6 deletions
+50
View File
@@ -7,6 +7,7 @@ import {
parseNodeRegisterParams,
parseNodeLocationSetParams,
parseNodeLocationGetParams,
parseNodeStatusSetParams,
} from '../protocol.js';
export interface NodeRegistration {
@@ -21,6 +22,7 @@ export interface NodeConnectionState {
identity?: string;
node?: NodeRegistration;
location?: NodeLocation;
status?: NodeStatus;
}
export interface NodeLocation {
@@ -35,6 +37,16 @@ export interface NodeLocation {
receivedAt: number;
}
export interface NodeStatus {
platform: 'macos' | 'ios' | 'android' | 'linux' | 'windows' | 'unknown';
appVersion?: string;
deviceName?: string;
statusText?: string;
batteryPct?: number;
powerSource: 'ac' | 'battery' | 'unknown';
reportedAt: number;
}
export interface NodeHandlerDeps {
enabled: boolean;
locationEnabled: boolean;
@@ -43,6 +55,7 @@ export interface NodeHandlerDeps {
getConnectionState: (connectionId: string) => NodeConnectionState | undefined;
setNodeRegistration: (connectionId: string, registration: NodeRegistration) => void;
setNodeLocation: (connectionId: string, location: NodeLocation) => void;
setNodeStatus: (connectionId: string, status: NodeStatus) => void;
}
export function createNodeHandlers(deps: NodeHandlerDeps) {
@@ -198,6 +211,43 @@ export function createNodeHandlers(deps: NodeHandlerDeps) {
});
},
'node.status.set': async (request: GatewayRequest): Promise<OutboundMessage> => {
if (!deps.enabled) {
return makeError(request.id, ErrorCode.AuthFailed, 'Node RPC is disabled');
}
const parsed = parseNodeStatusSetParams(request.params);
if (!parsed) {
return makeError(request.id, ErrorCode.InvalidRequest, 'Invalid node.status.set params');
}
const state = deps.getConnectionState(parsed.connectionId);
if (!state?.node) {
return makeError(request.id, ErrorCode.AuthFailed, 'Node is not registered for this connection');
}
const status: NodeStatus = {
platform: parsed.platform,
appVersion: parsed.appVersion?.trim() || undefined,
deviceName: parsed.deviceName?.trim() || undefined,
statusText: parsed.statusText?.trim() || undefined,
batteryPct: parsed.batteryPct,
powerSource: parsed.powerSource ?? 'unknown',
reportedAt: Date.now(),
};
deps.setNodeStatus(parsed.connectionId, status);
return makeResponse(request.id, {
updated: true,
node: {
id: state.node.nodeId,
role: state.node.role,
},
status,
});
},
'system.capabilities': async (request: GatewayRequest): Promise<OutboundMessage> => {
const params = request.params as { connectionId?: string } | undefined;
const connectionId = params?.connectionId;