@abitat_reece/host-daemon 0.1.20 → 0.1.22

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 (34) hide show
  1. package/dist/local-control/codex-bridge.d.ts.map +1 -1
  2. package/dist/local-control/codex-bridge.js +177 -18
  3. package/dist/local-control/codex-bridge.js.map +1 -1
  4. package/dist/local-control/push-notifications.d.ts +2 -2
  5. package/dist/local-control/push-notifications.d.ts.map +1 -1
  6. package/dist/local-control/push-notifications.js +3 -1
  7. package/dist/local-control/push-notifications.js.map +1 -1
  8. package/dist/local-control/relay-client.d.ts +10 -3
  9. package/dist/local-control/relay-client.d.ts.map +1 -1
  10. package/dist/local-control/relay-client.js.map +1 -1
  11. package/dist/local-control/remote-control/macos-driver.d.ts +16 -0
  12. package/dist/local-control/remote-control/macos-driver.d.ts.map +1 -0
  13. package/dist/local-control/remote-control/macos-driver.js +440 -0
  14. package/dist/local-control/remote-control/macos-driver.js.map +1 -0
  15. package/dist/local-control/remote-control/routes.d.ts +20 -0
  16. package/dist/local-control/remote-control/routes.d.ts.map +1 -0
  17. package/dist/local-control/remote-control/routes.js +110 -0
  18. package/dist/local-control/remote-control/routes.js.map +1 -0
  19. package/dist/local-control/remote-control/session-manager.d.ts +10 -0
  20. package/dist/local-control/remote-control/session-manager.d.ts.map +1 -0
  21. package/dist/local-control/remote-control/session-manager.js +259 -0
  22. package/dist/local-control/remote-control/session-manager.js.map +1 -0
  23. package/dist/local-control/remote-control/types.d.ts +49 -0
  24. package/dist/local-control/remote-control/types.d.ts.map +1 -0
  25. package/dist/local-control/remote-control/types.js +2 -0
  26. package/dist/local-control/remote-control/types.js.map +1 -0
  27. package/dist/local-control/server.d.ts +7 -2
  28. package/dist/local-control/server.d.ts.map +1 -1
  29. package/dist/local-control/server.js +24 -86
  30. package/dist/local-control/server.js.map +1 -1
  31. package/dist/local-control/transport.d.ts +6 -2
  32. package/dist/local-control/transport.d.ts.map +1 -1
  33. package/dist/local-control/transport.js.map +1 -1
  34. package/package.json +2 -2
@@ -0,0 +1,259 @@
1
+ import { randomUUID } from "node:crypto";
2
+ const DEFAULT_FRAME_INTERVAL_MS = 120;
3
+ const stoppedStatuses = new Set(["ended", "failed"]);
4
+ export function createLocalRemoteControlManager(options) {
5
+ const states = new Map();
6
+ const now = options.now ?? (() => new Date());
7
+ const idGenerator = options.idGenerator ??
8
+ ((prefix) => `${prefix}_${randomUUID().replaceAll("-", "").slice(0, 16)}`);
9
+ const frameIntervalMs = options.frameIntervalMs ?? DEFAULT_FRAME_INTERVAL_MS;
10
+ async function captureNextFrame(sessionId) {
11
+ const state = states.get(sessionId);
12
+ if (!state || stoppedStatuses.has(state.session.status)) {
13
+ return;
14
+ }
15
+ if (state.isFrameCaptureInFlight) {
16
+ return;
17
+ }
18
+ state.isFrameCaptureInFlight = true;
19
+ try {
20
+ const nextFrame = await options.driver.captureFrame(state.session);
21
+ if (stoppedStatuses.has(state.session.status)) {
22
+ return;
23
+ }
24
+ state.latestFrame = nextFrame;
25
+ state.session = {
26
+ ...state.session,
27
+ errorMessage: null,
28
+ permissionState: {
29
+ ...state.session.permissionState,
30
+ screenRecording: "granted"
31
+ },
32
+ status: "active",
33
+ updatedAt: now().toISOString()
34
+ };
35
+ }
36
+ catch (error) {
37
+ if (stoppedStatuses.has(state.session.status)) {
38
+ return;
39
+ }
40
+ const permission = permissionKind(error);
41
+ state.session = {
42
+ ...state.session,
43
+ errorMessage: error instanceof Error ? error.message : "Unable to capture Mac screen",
44
+ permissionState: {
45
+ ...state.session.permissionState,
46
+ screenRecording: permission === "screenRecording" ? "needed" : state.session.permissionState.screenRecording
47
+ },
48
+ status: permission === "screenRecording" ? "failed" : state.session.status,
49
+ updatedAt: now().toISOString()
50
+ };
51
+ }
52
+ finally {
53
+ state.isFrameCaptureInFlight = false;
54
+ }
55
+ }
56
+ function startFrameLoop(state) {
57
+ if (!state.session.screenEnabled || state.frameTimer) {
58
+ return;
59
+ }
60
+ state.frameTimer = setInterval(() => {
61
+ void captureNextFrame(state.session.id);
62
+ }, frameIntervalMs);
63
+ state.frameTimer.unref?.();
64
+ void captureNextFrame(state.session.id);
65
+ }
66
+ function requireSession(sessionId, clientMachineId) {
67
+ const state = states.get(sessionId);
68
+ if (!state || state.session.clientMachineId !== clientMachineId) {
69
+ throw Object.assign(new Error("Remote-control session not found"), { statusCode: 404 });
70
+ }
71
+ return state;
72
+ }
73
+ function activeSessionFor(input) {
74
+ return [...states.values()].find((state) => state.session.clientMachineId === input.clientMachineId &&
75
+ state.session.hostMachineId === input.hostMachineId &&
76
+ !stoppedStatuses.has(state.session.status));
77
+ }
78
+ async function readCursorPosition() {
79
+ if (!options.driver.getCursorPosition) {
80
+ return null;
81
+ }
82
+ try {
83
+ return await options.driver.getCursorPosition();
84
+ }
85
+ catch {
86
+ return null;
87
+ }
88
+ }
89
+ async function readTextInputTarget() {
90
+ if (!options.driver.getTextInputTarget) {
91
+ return null;
92
+ }
93
+ return options.driver.getTextInputTarget();
94
+ }
95
+ return {
96
+ async applyInput(sessionId, clientMachineId, event) {
97
+ const state = requireSession(sessionId, clientMachineId);
98
+ if (stoppedStatuses.has(state.session.status)) {
99
+ throw Object.assign(new Error("Remote-control session is not active"), {
100
+ statusCode: 409
101
+ });
102
+ }
103
+ if (!state.session.inputEnabled) {
104
+ throw Object.assign(new Error("Remote-control input is disabled for this session"), {
105
+ statusCode: 403
106
+ });
107
+ }
108
+ try {
109
+ const inputResult = await options.driver.applyInput(state.session, event);
110
+ const cursorPosition = inputResult && "cursorPosition" in inputResult
111
+ ? inputResult.cursorPosition
112
+ : state.session.cursorPosition;
113
+ state.session = {
114
+ ...state.session,
115
+ cursorPosition,
116
+ errorMessage: null,
117
+ permissionState: {
118
+ ...state.session.permissionState,
119
+ accessibility: "granted"
120
+ },
121
+ updatedAt: now().toISOString()
122
+ };
123
+ return state.session;
124
+ }
125
+ catch (error) {
126
+ const permission = permissionKind(error);
127
+ state.session = {
128
+ ...state.session,
129
+ errorMessage: error instanceof Error ? error.message : "Unable to send Mac input",
130
+ permissionState: {
131
+ ...state.session.permissionState,
132
+ accessibility: permission === "accessibility" ? "needed" : state.session.permissionState.accessibility
133
+ },
134
+ updatedAt: now().toISOString()
135
+ };
136
+ throw error;
137
+ }
138
+ },
139
+ async captureNextFrameForTest(sessionId) {
140
+ await captureNextFrame(sessionId);
141
+ },
142
+ async endSession(sessionId, clientMachineId) {
143
+ const state = requireSession(sessionId, clientMachineId);
144
+ if (state.frameTimer) {
145
+ clearInterval(state.frameTimer);
146
+ state.frameTimer = null;
147
+ }
148
+ state.session = {
149
+ ...state.session,
150
+ status: "ended",
151
+ updatedAt: now().toISOString()
152
+ };
153
+ state.latestFrame = null;
154
+ await options.driver.closeSession(state.session);
155
+ return state.session;
156
+ },
157
+ getLatestFrame(sessionId, clientMachineId, afterSequence = -1) {
158
+ const state = requireSession(sessionId, clientMachineId);
159
+ return {
160
+ frame: state.latestFrame && state.latestFrame.sequence > afterSequence ? state.latestFrame : null,
161
+ session: state.session
162
+ };
163
+ },
164
+ getSession(sessionId, clientMachineId) {
165
+ return requireSession(sessionId, clientMachineId).session;
166
+ },
167
+ async getTextInputTarget(sessionId, clientMachineId) {
168
+ const state = requireSession(sessionId, clientMachineId);
169
+ if (stoppedStatuses.has(state.session.status)) {
170
+ throw Object.assign(new Error("Remote-control session is not active"), {
171
+ statusCode: 409
172
+ });
173
+ }
174
+ try {
175
+ const target = await readTextInputTarget();
176
+ state.session = {
177
+ ...state.session,
178
+ errorMessage: null,
179
+ permissionState: {
180
+ ...state.session.permissionState,
181
+ accessibility: options.driver.getTextInputTarget
182
+ ? "granted"
183
+ : state.session.permissionState.accessibility
184
+ },
185
+ updatedAt: now().toISOString()
186
+ };
187
+ return target;
188
+ }
189
+ catch (error) {
190
+ const permission = permissionKind(error);
191
+ state.session = {
192
+ ...state.session,
193
+ errorMessage: error instanceof Error ? error.message : "Unable to inspect Mac text input focus",
194
+ permissionState: {
195
+ ...state.session.permissionState,
196
+ accessibility: permission === "accessibility" ? "needed" : state.session.permissionState.accessibility
197
+ },
198
+ updatedAt: now().toISOString()
199
+ };
200
+ throw error;
201
+ }
202
+ },
203
+ listSessions(clientMachineId) {
204
+ return [...states.values()]
205
+ .map((state) => state.session)
206
+ .filter((session) => session.clientMachineId === clientMachineId);
207
+ },
208
+ async startSession(input) {
209
+ const existing = activeSessionFor(input);
210
+ if (existing) {
211
+ return existing.session;
212
+ }
213
+ const timestamp = now().toISOString();
214
+ const cursorPosition = await readCursorPosition();
215
+ const session = {
216
+ id: idGenerator("remote"),
217
+ status: "connecting",
218
+ hostMachineId: input.hostMachineId,
219
+ clientMachineId: input.clientMachineId,
220
+ screenEnabled: input.screenEnabled,
221
+ inputEnabled: input.inputEnabled,
222
+ permissionState: {
223
+ accessibility: "unknown",
224
+ screenRecording: "unknown"
225
+ },
226
+ cursorPosition,
227
+ errorMessage: null,
228
+ createdAt: timestamp,
229
+ updatedAt: timestamp
230
+ };
231
+ const state = {
232
+ frameTimer: null,
233
+ isFrameCaptureInFlight: false,
234
+ latestFrame: null,
235
+ session
236
+ };
237
+ states.set(session.id, state);
238
+ startFrameLoop(state);
239
+ return session;
240
+ },
241
+ async stopAll() {
242
+ await Promise.all([...states.values()].map(async (state) => {
243
+ if (state.frameTimer) {
244
+ clearInterval(state.frameTimer);
245
+ state.frameTimer = null;
246
+ }
247
+ await options.driver.closeSession(state.session);
248
+ }));
249
+ }
250
+ };
251
+ }
252
+ function permissionKind(error) {
253
+ if (typeof error === "object" && error && "permission" in error) {
254
+ const value = error.permission;
255
+ return value === "screenRecording" || value === "accessibility" ? value : null;
256
+ }
257
+ return null;
258
+ }
259
+ //# sourceMappingURL=session-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-manager.js","sourceRoot":"","sources":["../../../src/local-control/remote-control/session-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { RemoteControlCursorPosition, RemoteControlFrame, RemoteControlStatus, RemoteControlTextTarget, RemoteInputEvent } from "@abitat_reece/shared";
2
+ export type RemoteControlPermissionState = "unknown" | "granted" | "needed";
3
+ export interface LocalRemoteControlPermissionState {
4
+ accessibility: RemoteControlPermissionState;
5
+ screenRecording: RemoteControlPermissionState;
6
+ }
7
+ export interface LocalRemoteControlSession {
8
+ id: string;
9
+ status: RemoteControlStatus;
10
+ hostMachineId: string;
11
+ clientMachineId: string;
12
+ screenEnabled: boolean;
13
+ inputEnabled: boolean;
14
+ permissionState: LocalRemoteControlPermissionState;
15
+ cursorPosition?: RemoteControlCursorPosition | null;
16
+ errorMessage?: string | null;
17
+ createdAt: string;
18
+ updatedAt: string;
19
+ }
20
+ export interface LocalRemoteControlStartInput {
21
+ hostMachineId: string;
22
+ clientMachineId: string;
23
+ screenEnabled: boolean;
24
+ inputEnabled: boolean;
25
+ }
26
+ export interface LocalRemoteControlDriver {
27
+ captureFrame(session: LocalRemoteControlSession): Promise<RemoteControlFrame>;
28
+ applyInput(session: LocalRemoteControlSession, event: RemoteInputEvent): Promise<{
29
+ cursorPosition?: RemoteControlCursorPosition | null;
30
+ } | void>;
31
+ closeSession(session: LocalRemoteControlSession): Promise<void>;
32
+ getCursorPosition?(): Promise<RemoteControlCursorPosition | null>;
33
+ getTextInputTarget?(): Promise<RemoteControlTextTarget | null>;
34
+ }
35
+ export interface LocalRemoteControlManager {
36
+ applyInput(sessionId: string, clientMachineId: string, event: RemoteInputEvent): Promise<LocalRemoteControlSession>;
37
+ captureNextFrameForTest(sessionId: string): Promise<void>;
38
+ endSession(sessionId: string, clientMachineId: string): Promise<LocalRemoteControlSession>;
39
+ getLatestFrame(sessionId: string, clientMachineId: string, afterSequence?: number): {
40
+ frame: RemoteControlFrame | null;
41
+ session: LocalRemoteControlSession;
42
+ };
43
+ getSession(sessionId: string, clientMachineId: string): LocalRemoteControlSession;
44
+ getTextInputTarget(sessionId: string, clientMachineId: string): Promise<RemoteControlTextTarget | null>;
45
+ listSessions(clientMachineId: string): LocalRemoteControlSession[];
46
+ startSession(input: LocalRemoteControlStartInput): Promise<LocalRemoteControlSession>;
47
+ stopAll(): Promise<void>;
48
+ }
49
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/local-control/remote-control/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,2BAA2B,EAC3B,kBAAkB,EAClB,mBAAmB,EACnB,uBAAuB,EACvB,gBAAgB,EACjB,MAAM,sBAAsB,CAAC;AAE9B,MAAM,MAAM,4BAA4B,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE5E,MAAM,WAAW,iCAAiC;IAChD,aAAa,EAAE,4BAA4B,CAAC;IAC5C,eAAe,EAAE,4BAA4B,CAAC;CAC/C;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,mBAAmB,CAAC;IAC5B,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,EAAE,iCAAiC,CAAC;IACnD,cAAc,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IACpD,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,4BAA4B;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC9E,UAAU,CACR,OAAO,EAAE,yBAAyB,EAClC,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC;QAAE,cAAc,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC3E,YAAY,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,CAAC,IAAI,OAAO,CAAC,2BAA2B,GAAG,IAAI,CAAC,CAAC;IAClE,kBAAkB,CAAC,IAAI,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,yBAAyB;IACxC,UAAU,CACR,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,KAAK,EAAE,gBAAgB,GACtB,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACtC,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;IAC3F,cAAc,CACZ,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,EACvB,aAAa,CAAC,EAAE,MAAM,GACrB;QACD,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;QACjC,OAAO,EAAE,yBAAyB,CAAC;KACpC,CAAC;IACF,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,yBAAyB,CAAC;IAClF,kBAAkB,CAChB,SAAS,EAAE,MAAM,EACjB,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CAAC;IAC3C,YAAY,CAAC,eAAe,EAAE,MAAM,GAAG,yBAAyB,EAAE,CAAC;IACnE,YAAY,CAAC,KAAK,EAAE,4BAA4B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACtF,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/local-control/remote-control/types.ts"],"names":[],"mappings":""}
@@ -3,6 +3,7 @@ import type { CodexMobileModelSettings, CodexModelOption, ConversationStatus } f
3
3
  import { type LocalControlStore, type LocalControlTransport } from "./state.js";
4
4
  import { type MobileControlDiagnosticsLogger } from "./diagnostics-log.js";
5
5
  import { type CodexTokenUsageSummary } from "./token-usage.js";
6
+ import { type LocalRemoteControlManager } from "./remote-control/session-manager.js";
6
7
  export interface LocalCodexProjectSummary {
7
8
  id: string;
8
9
  workspaceId: string;
@@ -67,6 +68,9 @@ export interface LocalCodexCompletionState {
67
68
  updatedAt: string;
68
69
  workspaceId: string;
69
70
  }
71
+ export interface LocalCodexCompletionStateOptions {
72
+ hydrateIdleSummariesSince?: number;
73
+ }
70
74
  export interface LocalCodexBridge {
71
75
  bootstrap(): Promise<{
72
76
  available: boolean;
@@ -97,7 +101,7 @@ export interface LocalCodexBridge {
97
101
  status: ConversationStatus | string;
98
102
  updated: boolean;
99
103
  }>;
100
- listCompletionStates(): Promise<LocalCodexCompletionState[]>;
104
+ listCompletionStates(options?: LocalCodexCompletionStateOptions): Promise<LocalCodexCompletionState[]>;
101
105
  downloadGeneratedFile(conversationId: string, fileId: string): Promise<LocalGeneratedFileDownload>;
102
106
  listMessages(conversationId: string, options?: {
103
107
  afterSequence?: number;
@@ -129,6 +133,7 @@ interface StartLocalControlServerInput {
129
133
  diagnostics?: MobileControlDiagnosticsLogger;
130
134
  endpoint: string;
131
135
  port: number;
136
+ remoteControl?: LocalRemoteControlManager;
132
137
  store: LocalControlStore;
133
138
  tokenUsageProvider?: () => Promise<CodexTokenUsageSummary>;
134
139
  transport: LocalControlTransport;
@@ -136,7 +141,7 @@ interface StartLocalControlServerInput {
136
141
  export declare function startLocalControlServer(input: StartLocalControlServerInput): Promise<{
137
142
  readonly endpoint: string;
138
143
  server: Server<typeof IncomingMessage, typeof ServerResponse>;
139
- close: () => Promise<void>;
144
+ close(): Promise<void>;
140
145
  }>;
141
146
  export {};
142
147
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/local-control/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/local-control/server.ts"],"names":[],"mappings":"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"}
@@ -5,13 +5,18 @@ import { dirname, join } from "node:path";
5
5
  import { defaultLocalAttachmentDirectory } from "./state.js";
6
6
  import { attachmentDiagnostics, errorDiagnostics, logDiagnostics, mobileControlDiagnosticsLogPath, promptDiagnostics } from "./diagnostics-log.js";
7
7
  import { readCodexTokenUsageSummary } from "./token-usage.js";
8
+ import { createMacOsRemoteControlDriver } from "./remote-control/macos-driver.js";
9
+ import { handleLocalRemoteControlRoute } from "./remote-control/routes.js";
10
+ import { createLocalRemoteControlManager } from "./remote-control/session-manager.js";
8
11
  const LOCAL_WORKSPACE_ID = "local";
9
12
  const MOBILE_DIAGNOSTICS_LOG_LIMIT_BYTES = 3 * 1024 * 1024;
10
13
  export async function startLocalControlServer(input) {
11
14
  const identity = await input.store.getMacIdentity();
12
15
  const diagnostics = input.diagnostics;
13
- const sessions = new Map();
14
- const signals = [];
16
+ const remoteControl = input.remoteControl ??
17
+ createLocalRemoteControlManager({
18
+ driver: createMacOsRemoteControlDriver()
19
+ });
15
20
  let endpoint = input.endpoint;
16
21
  const server = createServer(async (request, response) => {
17
22
  const method = request.method ?? "GET";
@@ -377,77 +382,19 @@ export async function startLocalControlServer(input) {
377
382
  writeJson(response, 201, { attachment });
378
383
  return;
379
384
  }
380
- if (method === "GET" && path === "/remote-control/status") {
381
- writeJson(response, 200, {
382
- sessions: Array.from(sessions.values()).filter((session) => session.clientMachineId === actor.id)
383
- });
384
- return;
385
- }
386
- if (method === "GET" &&
387
- (path === "/api/remote-control/sessions" || path === "/remote-control/sessions")) {
388
- writeJson(response, 200, {
389
- sessions: Array.from(sessions.values()).filter((session) => session.clientMachineId === actor.id)
390
- });
391
- return;
392
- }
393
- if (method === "POST" &&
394
- (path === "/api/remote-control/sessions" || path === "/remote-control/start")) {
395
- const body = await readJson(request);
396
- const hostMachineId = stringValue(body.hostMachineId) || identity.macId;
397
- if (hostMachineId !== identity.macId) {
398
- throw Object.assign(new Error("Phone is not paired to this Mac host"), {
399
- statusCode: 403
400
- });
401
- }
402
- const timestamp = new Date().toISOString();
403
- const session = {
404
- id: `remote_${randomBytes(8).toString("hex")}`,
405
- status: "requested",
406
- hostMachineId,
407
- clientMachineId: actor.id,
408
- screenEnabled: body.screenEnabled !== false,
409
- inputEnabled: body.inputEnabled !== false,
410
- errorMessage: null,
411
- createdAt: timestamp,
412
- updatedAt: timestamp
413
- };
414
- sessions.set(session.id, session);
415
- writeJson(response, 201, { session });
416
- return;
417
- }
418
- const remoteSessionMatch = matchPath(path, "/api/remote-control/sessions/:sessionId");
419
- if (remoteSessionMatch && method === "DELETE") {
420
- const session = requireRemoteSession(sessions, remoteSessionMatch.sessionId, actor.id);
421
- const ended = { ...session, status: "ended", updatedAt: new Date().toISOString() };
422
- sessions.set(session.id, ended);
423
- writeJson(response, 200, { session: ended });
424
- return;
425
- }
426
- const signalMatch = matchPath(path, "/api/remote-control/sessions/:sessionId/signals");
427
- if (signalMatch && method === "GET") {
428
- requireRemoteSession(sessions, signalMatch.sessionId, actor.id);
429
- writeJson(response, 200, {
430
- signals: signals.filter((signal) => signal.sessionId === signalMatch.sessionId &&
431
- (!signal.recipientMachineId || signal.recipientMachineId === actor.id))
432
- });
433
- return;
434
- }
435
- if ((signalMatch && method === "POST") ||
436
- (path === "/remote-control/input" && method === "POST")) {
437
- const body = await readJson(request);
438
- const sessionId = signalMatch?.sessionId ?? stringValue(body.sessionId);
439
- const session = requireRemoteSession(sessions, sessionId, actor.id);
440
- const signal = {
441
- id: `signal_${randomBytes(8).toString("hex")}`,
442
- sessionId: session.id,
443
- senderMachineId: actor.id,
444
- recipientMachineId: stringValue(body.recipientMachineId) || identity.macId,
445
- type: stringValue(body.type) || "input",
446
- payload: recordValue(body.payload) ?? { event: body.event },
447
- createdAt: new Date().toISOString()
448
- };
449
- signals.push(signal);
450
- writeJson(response, 201, { signal });
385
+ if (await handleLocalRemoteControlRoute({
386
+ actor,
387
+ diagnostics,
388
+ hostMachineId: identity.macId,
389
+ manager: remoteControl,
390
+ method,
391
+ path,
392
+ readJson,
393
+ request,
394
+ response,
395
+ searchParams: url.searchParams,
396
+ writeJson
397
+ })) {
451
398
  return;
452
399
  }
453
400
  writeJson(response, 404, { error: "Not found" });
@@ -487,7 +434,10 @@ export async function startLocalControlServer(input) {
487
434
  return endpoint;
488
435
  },
489
436
  server,
490
- close: () => closeServer(server)
437
+ async close() {
438
+ await remoteControl.stopAll();
439
+ await closeServer(server);
440
+ }
491
441
  };
492
442
  }
493
443
  async function readAndClearMobileDiagnosticsLog(diagnostics) {
@@ -578,13 +528,6 @@ async function requireMobileDevice(request, store) {
578
528
  function hasAuthorizationHeader(request) {
579
529
  return typeof request.headers.authorization === "string" && request.headers.authorization !== "";
580
530
  }
581
- function requireRemoteSession(sessions, sessionId, clientMachineId) {
582
- const session = sessions.get(sessionId);
583
- if (!session || session.clientMachineId !== clientMachineId) {
584
- throw Object.assign(new Error("Remote-control session not found"), { statusCode: 404 });
585
- }
586
- return session;
587
- }
588
531
  async function readJson(request) {
589
532
  const chunks = [];
590
533
  for await (const chunk of request) {
@@ -691,11 +634,6 @@ function requiredString(value, message) {
691
634
  }
692
635
  return parsed;
693
636
  }
694
- function recordValue(value) {
695
- return value && typeof value === "object" && !Array.isArray(value)
696
- ? value
697
- : null;
698
- }
699
637
  function numberQuery(value) {
700
638
  if (value === null) {
701
639
  return undefined;