@myerscarpenter/quest-dev 1.4.1 → 2.0.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.
Files changed (142) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.github/workflows/docs.yml +45 -0
  3. package/.github/workflows/publish.yml +11 -1
  4. package/README.md +27 -0
  5. package/build/cast/decoder.d.ts +48 -0
  6. package/build/cast/decoder.d.ts.map +1 -0
  7. package/build/cast/decoder.js +152 -0
  8. package/build/cast/decoder.js.map +1 -0
  9. package/build/cast/session.d.ts +87 -0
  10. package/build/cast/session.d.ts.map +1 -0
  11. package/build/cast/session.js +565 -0
  12. package/build/cast/session.js.map +1 -0
  13. package/build/commands/logcat.d.ts.map +1 -1
  14. package/build/commands/logcat.js +7 -6
  15. package/build/commands/logcat.js.map +1 -1
  16. package/build/commands/screenshot.d.ts.map +1 -1
  17. package/build/commands/screenshot.js +17 -20
  18. package/build/commands/screenshot.js.map +1 -1
  19. package/build/commands/stay-awake.d.ts +2 -15
  20. package/build/commands/stay-awake.d.ts.map +1 -1
  21. package/build/commands/stay-awake.js +14 -77
  22. package/build/commands/stay-awake.js.map +1 -1
  23. package/build/daemon/cast-manager.d.ts +42 -0
  24. package/build/daemon/cast-manager.d.ts.map +1 -0
  25. package/build/daemon/cast-manager.js +243 -0
  26. package/build/daemon/cast-manager.js.map +1 -0
  27. package/build/daemon/client.d.ts +40 -0
  28. package/build/daemon/client.d.ts.map +1 -0
  29. package/build/daemon/client.js +133 -0
  30. package/build/daemon/client.js.map +1 -0
  31. package/build/daemon/daemon.d.ts +20 -0
  32. package/build/daemon/daemon.d.ts.map +1 -0
  33. package/build/daemon/daemon.js +130 -0
  34. package/build/daemon/daemon.js.map +1 -0
  35. package/build/daemon/deploy.d.ts +44 -0
  36. package/build/daemon/deploy.d.ts.map +1 -0
  37. package/build/daemon/deploy.js +230 -0
  38. package/build/daemon/deploy.js.map +1 -0
  39. package/build/daemon/logcat-manager.d.ts +39 -0
  40. package/build/daemon/logcat-manager.d.ts.map +1 -0
  41. package/build/daemon/logcat-manager.js +194 -0
  42. package/build/daemon/logcat-manager.js.map +1 -0
  43. package/build/daemon/server.d.ts +19 -0
  44. package/build/daemon/server.d.ts.map +1 -0
  45. package/build/daemon/server.js +482 -0
  46. package/build/daemon/server.js.map +1 -0
  47. package/build/daemon/stay-awake-manager.d.ts +22 -0
  48. package/build/daemon/stay-awake-manager.d.ts.map +1 -0
  49. package/build/daemon/stay-awake-manager.js +74 -0
  50. package/build/daemon/stay-awake-manager.js.map +1 -0
  51. package/build/index.js +272 -45
  52. package/build/index.js.map +1 -1
  53. package/build/public/dashboard.js +749 -0
  54. package/build/public/index.html +12 -0
  55. package/build/public/style.css +106 -0
  56. package/build/utils/adb.d.ts +6 -0
  57. package/build/utils/adb.d.ts.map +1 -1
  58. package/build/utils/adb.js +62 -66
  59. package/build/utils/adb.js.map +1 -1
  60. package/build/utils/casting-apk.d.ts +40 -0
  61. package/build/utils/casting-apk.d.ts.map +1 -0
  62. package/build/utils/casting-apk.js +252 -0
  63. package/build/utils/casting-apk.js.map +1 -0
  64. package/build/utils/config.d.ts +5 -3
  65. package/build/utils/config.d.ts.map +1 -1
  66. package/build/utils/config.js +18 -38
  67. package/build/utils/config.js.map +1 -1
  68. package/build/utils/exec.d.ts +5 -0
  69. package/build/utils/exec.d.ts.map +1 -1
  70. package/build/utils/exec.js +17 -0
  71. package/build/utils/exec.js.map +1 -1
  72. package/build/utils/filename.d.ts +7 -1
  73. package/build/utils/filename.d.ts.map +1 -1
  74. package/build/utils/filename.js +17 -2
  75. package/build/utils/filename.js.map +1 -1
  76. package/build/utils/filename.test.js +33 -1
  77. package/build/utils/filename.test.js.map +1 -1
  78. package/build/utils/jpeg-comment.d.ts +14 -0
  79. package/build/utils/jpeg-comment.d.ts.map +1 -0
  80. package/build/utils/jpeg-comment.js +28 -0
  81. package/build/utils/jpeg-comment.js.map +1 -0
  82. package/build/utils/test-properties.d.ts +34 -0
  83. package/build/utils/test-properties.d.ts.map +1 -0
  84. package/build/utils/test-properties.js +73 -0
  85. package/build/utils/test-properties.js.map +1 -0
  86. package/package.json +11 -5
  87. package/packages/cast2-protocol/README.md +86 -0
  88. package/packages/cast2-protocol/docs/_config.yml +4 -0
  89. package/packages/cast2-protocol/docs/feature-flags.md +102 -0
  90. package/packages/cast2-protocol/docs/index.md +24 -0
  91. package/packages/cast2-protocol/docs/open-investigations.md +149 -0
  92. package/packages/cast2-protocol/docs/protocol.md +602 -0
  93. package/packages/cast2-protocol/package.json +46 -0
  94. package/packages/cast2-protocol/src/constants.ts +65 -0
  95. package/packages/cast2-protocol/src/index.ts +7 -0
  96. package/packages/cast2-protocol/src/mgik.ts +69 -0
  97. package/packages/cast2-protocol/src/mud.ts +294 -0
  98. package/packages/cast2-protocol/src/pose.ts +99 -0
  99. package/packages/cast2-protocol/src/resolutions.ts +34 -0
  100. package/packages/cast2-protocol/src/types.ts +64 -0
  101. package/packages/cast2-protocol/src/xrsp.ts +73 -0
  102. package/packages/cast2-protocol/tests/mgik.test.ts +80 -0
  103. package/packages/cast2-protocol/tests/mud.test.ts +295 -0
  104. package/packages/cast2-protocol/tests/pose.test.ts +173 -0
  105. package/packages/cast2-protocol/tests/xrsp.test.ts +90 -0
  106. package/packages/cast2-protocol/tsconfig.json +20 -0
  107. package/pnpm-workspace.yaml +2 -0
  108. package/src/cast/decoder.ts +178 -0
  109. package/src/cast/session.ts +708 -0
  110. package/src/commands/logcat.ts +6 -5
  111. package/src/commands/screenshot.ts +19 -13
  112. package/src/commands/stay-awake.ts +22 -91
  113. package/src/daemon/adbkit-apkreader.d.ts +14 -0
  114. package/src/daemon/cast-manager.ts +282 -0
  115. package/src/daemon/client.ts +166 -0
  116. package/src/daemon/daemon.ts +169 -0
  117. package/src/daemon/deploy.ts +307 -0
  118. package/src/daemon/logcat-manager.ts +229 -0
  119. package/src/daemon/server.ts +595 -0
  120. package/src/daemon/stay-awake-manager.ts +83 -0
  121. package/src/index.ts +326 -56
  122. package/src/public/dashboard.js +288 -0
  123. package/src/public/index.html +12 -0
  124. package/src/public/style.css +106 -0
  125. package/src/utils/adb.ts +70 -57
  126. package/src/utils/casting-apk.ts +276 -0
  127. package/src/utils/config.ts +18 -36
  128. package/src/utils/exec.ts +20 -0
  129. package/src/utils/filename.test.ts +41 -1
  130. package/src/utils/filename.ts +18 -2
  131. package/src/utils/jpeg-comment.ts +30 -0
  132. package/src/utils/test-properties.ts +94 -0
  133. package/tests/cast/auto-layer.test.ts +87 -0
  134. package/tests/cast/decoder.test.ts +82 -0
  135. package/tests/cast/session-restart.test.ts +107 -0
  136. package/tests/config.test.ts +17 -22
  137. package/tests/daemon/api-status.test.ts +82 -0
  138. package/tests/daemon/cast-manager.test.ts +69 -0
  139. package/tests/daemon/mjpeg-stream.test.ts +144 -0
  140. package/tests/daemon/pose-endpoint.test.ts +63 -0
  141. package/tests/daemon/start-guard.test.ts +77 -0
  142. package/vitest.config.ts +10 -0
@@ -0,0 +1,565 @@
1
+ /**
2
+ * CastSession: manages TCP connections to Quest, protocol handshake,
3
+ * H.264 video decoding, pose control, and stay-awake lifecycle.
4
+ */
5
+ import { createServer } from "node:net";
6
+ import { EventEmitter } from "node:events";
7
+ import { randomUUID } from "node:crypto";
8
+ import { FrameDecoder } from "./decoder.js";
9
+ import { createPoseState, updatePose, setPoseOffset, parseXrspHeader, xrspPayloadSize, packXrsp, XRSP_HEADER_SIZE, ACK_MARKER, CONFIG_MARKER, VIDEO_META_MARKER, CAST_PORT, QUEST_CAST_PORT, CMD_SHORT_ACK_65, CMD_SHORT_ACK_CD, CMD_SHORT_ACK_12D, KEEPALIVE_ACK_INCREMENT, LAYER_PANEL_APP, detectSubMagic, isMgikMagic, buildInit, buildConfig, buildKeepalive, buildShortAck, buildPose, buildDisplayConfig, buildDisconnect, buildSetProperty, buildVirtualMouse, buildInputForwardingState, buildStartInputForwarding, buildActivateLayer, buildMud, parseLayerConfiguration, RESOLUTIONS, DEFAULT_RESOLUTION, } from "@myerscarpenter/cast2-protocol";
10
+ import { execCommand } from "../utils/exec.js";
11
+ import { verbose } from "../utils/verbose.js";
12
+ export class CastSession extends EventEmitter {
13
+ // Configuration
14
+ _listenPort;
15
+ // Connection state
16
+ _connected = false;
17
+ _running = false;
18
+ server = null;
19
+ controlSocket = null;
20
+ videoSocket = null;
21
+ controlSeq = 0;
22
+ // Protocol state
23
+ subMagic = null;
24
+ controlMsgSeq = 0;
25
+ sessionUuid;
26
+ sessionTimestamp;
27
+ questMsgSeq = 0;
28
+ // Video state
29
+ decoder;
30
+ currentSpsPps = null;
31
+ currentIdr = null;
32
+ _frameCount = 0;
33
+ _byteCount = 0;
34
+ startTime = 0;
35
+ // Display
36
+ _width;
37
+ _height;
38
+ // Pose
39
+ _pose;
40
+ _poseLoopTimer = null;
41
+ _poseLoopActive = false;
42
+ // Eye mode
43
+ _eye = 1; // EYE_LEFT default
44
+ // Input forwarding
45
+ inputForwardingStarted = false;
46
+ _layerId = 0;
47
+ _layerAutoSelected = false;
48
+ _layers = new Map();
49
+ constructor(options = {}) {
50
+ super();
51
+ this._listenPort = options.listenPort ?? CAST_PORT;
52
+ const def = RESOLUTIONS[DEFAULT_RESOLUTION];
53
+ this._width = options.width ?? def.width;
54
+ this._height = options.height ?? def.height;
55
+ this.sessionUuid = randomUUID();
56
+ this.sessionTimestamp = String(Date.now());
57
+ this._pose = createPoseState();
58
+ this.decoder = new FrameDecoder();
59
+ this.decoder.on("frame", () => {
60
+ this.emit("frame");
61
+ });
62
+ }
63
+ // --- Public getters ---
64
+ get listenPort() { return this._listenPort; }
65
+ get connected() { return this._connected; }
66
+ get running() { return this._running; }
67
+ get frameCount() { return this._frameCount; }
68
+ get byteCount() { return this._byteCount; }
69
+ get width() { return this._width; }
70
+ get height() { return this._height; }
71
+ get pose() { return this._pose; }
72
+ get layers() { return this._layers; }
73
+ get layerId() { return this._layerId; }
74
+ get poseLoopActive() { return this._poseLoopActive; }
75
+ get eye() { return this._eye; }
76
+ get elapsedSeconds() {
77
+ return this.startTime > 0 ? Math.round((Date.now() - this.startTime) / 100) / 10 : 0;
78
+ }
79
+ get fps() {
80
+ const elapsed = this.startTime > 0 ? (Date.now() - this.startTime) / 1000 : 0;
81
+ return elapsed > 0 ? Math.round((this._frameCount / elapsed) * 10) / 10 : 0;
82
+ }
83
+ // --- Lifecycle ---
84
+ /** Bind the TCP server, trying successive ports on EADDRINUSE. */
85
+ async bind() {
86
+ FrameDecoder.checkFfmpeg();
87
+ const maxAttempts = 10;
88
+ for (let attempt = 0; attempt < maxAttempts; attempt++) {
89
+ try {
90
+ await this.tryBind(this._listenPort);
91
+ verbose(`TCP server listening on port ${this._listenPort}`);
92
+ return;
93
+ }
94
+ catch (err) {
95
+ if (err.code === "EADDRINUSE") {
96
+ verbose(`Port ${this._listenPort} in use, trying ${this._listenPort + 1}`);
97
+ this._listenPort++;
98
+ }
99
+ else {
100
+ throw err;
101
+ }
102
+ }
103
+ }
104
+ throw new Error(`No available port found (tried ${maxAttempts} ports)`);
105
+ }
106
+ tryBind(port) {
107
+ return new Promise((resolve, reject) => {
108
+ this.server = createServer();
109
+ this.server.listen(port, "0.0.0.0", () => resolve());
110
+ this.server.on("error", reject);
111
+ });
112
+ }
113
+ async start(questIp) {
114
+ this._running = true;
115
+ this.startTime = Date.now();
116
+ await this.waitForConnections(questIp);
117
+ }
118
+ async stop() {
119
+ this._running = false;
120
+ if (this._connected && this.subMagic) {
121
+ try {
122
+ this.sendXrsp(buildDisconnect(this.subMagic, this.nextSeq()));
123
+ }
124
+ catch {
125
+ // Best effort
126
+ }
127
+ }
128
+ this.stopPoseLoop();
129
+ this.decoder.stop();
130
+ this.controlSocket?.destroy();
131
+ this.videoSocket?.destroy();
132
+ this.server?.close();
133
+ this._connected = false;
134
+ this.emit("disconnected");
135
+ }
136
+ async restart() {
137
+ verbose("Restarting cast session...");
138
+ await this.stop();
139
+ // Reset protocol state
140
+ this.sessionUuid = randomUUID();
141
+ this.sessionTimestamp = String(Date.now());
142
+ this.subMagic = null;
143
+ this.controlMsgSeq = 0;
144
+ this.controlSeq = 0;
145
+ this._frameCount = 0;
146
+ this._byteCount = 0;
147
+ this.currentSpsPps = null;
148
+ this.currentIdr = null;
149
+ this._pose = createPoseState();
150
+ this.inputForwardingStarted = false;
151
+ this._layerAutoSelected = false;
152
+ this._layers.clear();
153
+ await new Promise((r) => setTimeout(r, 1000));
154
+ // Re-bind the TCP server (stop() closed it)
155
+ if (this.server) {
156
+ await new Promise((resolve, reject) => {
157
+ this.server.listen(this._listenPort, "0.0.0.0", () => resolve());
158
+ this.server.once("error", reject);
159
+ });
160
+ verbose(`TCP server re-listening on port ${this._listenPort}`);
161
+ }
162
+ else {
163
+ await this.bind();
164
+ }
165
+ await this.start();
166
+ }
167
+ // --- ADB Setup ---
168
+ async adbSetup(questIp) {
169
+ verbose("Setting up ADB for Quest at", questIp);
170
+ const device = `${questIp}:5555`;
171
+ await execCommand("adb", ["connect", device]);
172
+ await execCommand("adb", [
173
+ "-s", device, "shell",
174
+ "setprop debug.oculus.command_line_media_capture true",
175
+ ]);
176
+ // Set the port the Quest casting service will connect to
177
+ await execCommand("adb", [
178
+ "-s", device, "shell",
179
+ `setprop debug.oculus.magic.port ${QUEST_CAST_PORT}`,
180
+ ]);
181
+ // Set up reverse mappings for both ports (matching MQDH)
182
+ verbose("Setting up ADB reverse port forward...");
183
+ for (const port of [CAST_PORT, QUEST_CAST_PORT]) {
184
+ try {
185
+ await execCommand("adb", ["-s", device, "reverse", "--remove", `tcp:${port}`]);
186
+ }
187
+ catch {
188
+ // No existing mapping — that's fine
189
+ }
190
+ await execCommand("adb", [
191
+ "-s", device, "reverse",
192
+ `tcp:${port}`, `tcp:${this.listenPort}`,
193
+ ]);
194
+ }
195
+ }
196
+ async startCastService(questIp) {
197
+ const device = `${questIp}:5555`;
198
+ verbose("Starting cast service on Quest...");
199
+ await execCommand("adb", [
200
+ "-s", device, "shell",
201
+ "am start-foreground-service -n com.oculus.magicislandcastingservice/.CastingService",
202
+ ]);
203
+ await new Promise((r) => setTimeout(r, 1000));
204
+ await execCommand("adb", [
205
+ "-s", device, "shell",
206
+ "am broadcast -a com.oculus.magicislandcastingservice.CONNECT",
207
+ ]);
208
+ }
209
+ // --- TCP ---
210
+ waitForConnections(questIp) {
211
+ return new Promise((resolve, reject) => {
212
+ if (!this.server) {
213
+ reject(new Error("TCP server not bound — call bind() first"));
214
+ return;
215
+ }
216
+ const connections = [];
217
+ let resolved = false;
218
+ this.server.on("connection", (socket) => {
219
+ const idx = connections.length + 1;
220
+ verbose(`Connection #${idx} from ${socket.remoteAddress}`);
221
+ connections.push(socket);
222
+ if (connections.length === 2) {
223
+ this.controlSocket = connections[0];
224
+ this.videoSocket = connections[1];
225
+ this._connected = true;
226
+ verbose("Both connections established");
227
+ this.emit("connected");
228
+ this.handleVideo();
229
+ if (!resolved) {
230
+ resolved = true;
231
+ resolve();
232
+ }
233
+ }
234
+ });
235
+ // Trigger the cast service now that we're listening
236
+ if (questIp) {
237
+ this.startCastService(questIp).catch((err) => {
238
+ if (!resolved) {
239
+ resolved = true;
240
+ reject(err);
241
+ }
242
+ });
243
+ }
244
+ // Timeout after 15 seconds
245
+ const timeout = setTimeout(() => {
246
+ if (!resolved) {
247
+ resolved = true;
248
+ if (connections.length < 2) {
249
+ verbose(`Timed out waiting for Quest connections (got ${connections.length})`);
250
+ this.server?.close();
251
+ reject(new Error(`Timed out waiting for Quest connections (got ${connections.length}/2)`));
252
+ }
253
+ }
254
+ }, 15000);
255
+ this.server.on("error", (err) => {
256
+ clearTimeout(timeout);
257
+ if (!resolved) {
258
+ resolved = true;
259
+ reject(err);
260
+ }
261
+ });
262
+ // Clear timeout once we get both connections
263
+ this.once("connected", () => clearTimeout(timeout));
264
+ });
265
+ }
266
+ // --- XRSP I/O ---
267
+ sendXrsp(payload, flags) {
268
+ if (!this.controlSocket || this.controlSocket.destroyed)
269
+ return;
270
+ const pkt = packXrsp(this.controlSeq, payload, flags);
271
+ this.controlSeq++;
272
+ this.controlSocket.write(pkt);
273
+ }
274
+ nextSeq() {
275
+ return this.controlMsgSeq++;
276
+ }
277
+ // --- Video handling (main protocol loop) ---
278
+ handleVideo() {
279
+ if (!this.videoSocket)
280
+ return;
281
+ let buffer = Buffer.alloc(0);
282
+ let handshakeStage = 0;
283
+ let ackVal = 0;
284
+ this.videoSocket.on("data", (chunk) => {
285
+ buffer = Buffer.concat([buffer, chunk]);
286
+ while (buffer.length >= XRSP_HEADER_SIZE) {
287
+ const header = parseXrspHeader(buffer);
288
+ const payloadSize = xrspPayloadSize(header);
289
+ const totalSize = XRSP_HEADER_SIZE + payloadSize;
290
+ if (buffer.length < totalSize)
291
+ break; // Need more data
292
+ const payload = buffer.subarray(XRSP_HEADER_SIZE, totalSize);
293
+ buffer = buffer.subarray(totalSize);
294
+ this.processVideoPayload(payload, header, handshakeStage, ackVal, (newStage, newAck) => {
295
+ handshakeStage = newStage;
296
+ ackVal = newAck;
297
+ });
298
+ }
299
+ });
300
+ this.videoSocket.on("end", () => {
301
+ verbose("Video connection EOF");
302
+ this._running = false;
303
+ this.emit("disconnected");
304
+ });
305
+ this.videoSocket.on("error", (err) => {
306
+ verbose("Video connection error:", err.message);
307
+ this._running = false;
308
+ this.emit("error", err);
309
+ });
310
+ }
311
+ processVideoPayload(payload, _header, handshakeStage, ackVal, setState) {
312
+ if (payload.length < 4)
313
+ return;
314
+ const first4 = payload.readUInt32BE(0);
315
+ // Skip MGIK magic header
316
+ if (isMgikMagic(payload))
317
+ return;
318
+ // Learn sub-magic from first Quest message
319
+ if (this.subMagic === null && payload.length === 24) {
320
+ const detected = detectSubMagic(payload);
321
+ if (detected !== null) {
322
+ this.subMagic = detected;
323
+ this.questMsgSeq = payload.readUInt32BE(4);
324
+ verbose(`Learned sub-magic: 0x${this.subMagic.toString(16).padStart(8, "0")}`);
325
+ return;
326
+ }
327
+ }
328
+ // Sub-magic echo (update quest msg seq)
329
+ if (this.subMagic && first4 === this.subMagic) {
330
+ this.questMsgSeq = payload.readUInt32BE(4);
331
+ return;
332
+ }
333
+ // Transport string
334
+ if (first4 === 0x00000001 && payload.length >= 12) {
335
+ const strLen = payload.readUInt32BE(8);
336
+ if (12 + strLen <= payload.length && strLen > 4) {
337
+ const candidate = payload.subarray(12, 12 + strLen);
338
+ if (candidate.every((b) => b >= 32 && b < 127)) {
339
+ verbose("Quest transport:", candidate.toString("ascii"));
340
+ if (this.subMagic && handshakeStage === 0) {
341
+ this.sendXrsp(buildInit(this.subMagic, this.nextSeq()));
342
+ setState(1, ackVal);
343
+ }
344
+ return;
345
+ }
346
+ }
347
+ // H.264 NAL unit
348
+ const nalType = payload[4] & 0x1f;
349
+ if (nalType === 7 || nalType === 5 || nalType === 1 || nalType === 6 || nalType === 8 ||
350
+ (nalType > 0 && payload.length > 50)) {
351
+ if (nalType === 7) {
352
+ this.currentSpsPps = Buffer.from(payload);
353
+ verbose(`SPS+PPS (${payload.length} bytes)`);
354
+ if (!this.decoder.isRunning) {
355
+ this.decoder.start();
356
+ }
357
+ }
358
+ else if (nalType === 5) {
359
+ this.currentIdr = Buffer.from(payload);
360
+ verbose(`IDR keyframe (${payload.length} bytes)`);
361
+ }
362
+ this._frameCount++;
363
+ this._byteCount += payload.length;
364
+ this.decoder.feed(payload);
365
+ }
366
+ return;
367
+ }
368
+ // Quest ACK
369
+ if (first4 === ACK_MARKER && payload.length === 8) {
370
+ const val = payload.readUInt32BE(4);
371
+ verbose(`Quest ACK: ${val} (stage=${handshakeStage})`);
372
+ if (this.subMagic) {
373
+ if (handshakeStage === 1) {
374
+ this.sendXrsp(buildConfig(this.subMagic, this.nextSeq(), this._width, this._height, this.sessionUuid, this.sessionTimestamp));
375
+ setState(2, ackVal);
376
+ }
377
+ else if (handshakeStage === 3) {
378
+ this.sendXrsp(buildShortAck(this.subMagic, this.nextSeq(), CMD_SHORT_ACK_65));
379
+ ackVal = 0x195;
380
+ this.sendXrsp(buildKeepalive(this.subMagic, this.questMsgSeq, ackVal, this.sessionUuid, this.sessionTimestamp));
381
+ setState(4, ackVal);
382
+ }
383
+ else if (handshakeStage === 4) {
384
+ ackVal = 0x25d;
385
+ this.sendXrsp(buildKeepalive(this.subMagic, this.questMsgSeq, ackVal, this.sessionUuid, this.sessionTimestamp));
386
+ setState(5, ackVal);
387
+ }
388
+ else if (handshakeStage >= 5) {
389
+ ackVal += KEEPALIVE_ACK_INCREMENT;
390
+ this.sendXrsp(buildKeepalive(this.subMagic, this.questMsgSeq, ackVal, this.sessionUuid, this.sessionTimestamp));
391
+ setState(handshakeStage, ackVal);
392
+ }
393
+ }
394
+ return;
395
+ }
396
+ // LayerConfiguration (type 300 = 0x12C)
397
+ if (first4 === CONFIG_MARKER) {
398
+ const layer = parseLayerConfiguration(payload);
399
+ if (layer) {
400
+ verbose(`LayerConfig: id=${layer.id} ${layer.width}x${layer.height} type=${layer.typeName} app=${layer.app}`);
401
+ this._layers.set(layer.id, layer);
402
+ this._width = layer.width;
403
+ this._height = layer.height;
404
+ if (layer.type === LAYER_PANEL_APP && !this._layerAutoSelected) {
405
+ this._layerId = layer.id;
406
+ this._layerAutoSelected = true;
407
+ verbose(`Auto-selected PANEL_APP layer ${layer.id} for input`);
408
+ }
409
+ this.emit("layer", layer);
410
+ }
411
+ else if (payload.length >= 16) {
412
+ this._width = payload.readUInt32BE(8);
413
+ this._height = payload.readUInt32BE(12);
414
+ verbose(`Quest config: ${this._width}x${this._height} (short payload)`);
415
+ }
416
+ if (this.subMagic && handshakeStage === 2) {
417
+ ackVal = KEEPALIVE_ACK_INCREMENT; // 0xC8
418
+ this.sendXrsp(buildKeepalive(this.subMagic, this.questMsgSeq, ackVal, this.sessionUuid, this.sessionTimestamp));
419
+ for (let i = 0; i < 3; i++) {
420
+ this.sendXrsp(buildShortAck(this.subMagic, this.nextSeq(), CMD_SHORT_ACK_CD));
421
+ }
422
+ this.sendXrsp(buildShortAck(this.subMagic, this.nextSeq(), CMD_SHORT_ACK_12D));
423
+ setState(3, ackVal);
424
+ }
425
+ return;
426
+ }
427
+ // VideoMeta
428
+ if (first4 === VIDEO_META_MARKER && payload.length === 16) {
429
+ if (this._frameCount > 0 && this._frameCount % 6 === 0 && this.subMagic) {
430
+ ackVal += KEEPALIVE_ACK_INCREMENT;
431
+ this.sendXrsp(buildKeepalive(this.subMagic, this.questMsgSeq, ackVal, this.sessionUuid, this.sessionTimestamp));
432
+ setState(handshakeStage, ackVal);
433
+ }
434
+ }
435
+ }
436
+ // --- Public control methods ---
437
+ getScreenshot() {
438
+ return this.decoder.getFrame();
439
+ }
440
+ sendPose(pose) {
441
+ this._pose = pose;
442
+ if (this._connected && this.subMagic) {
443
+ this.sendXrsp(buildPose(this.subMagic, this.nextSeq(), this._pose));
444
+ }
445
+ // Auto-start periodic pose loop on first pose send so the Quest
446
+ // accepts our camera override (requires continuous ~27 Hz updates).
447
+ if (!this._poseLoopActive) {
448
+ this.startPoseLoop();
449
+ }
450
+ }
451
+ applyPoseDelta(delta) {
452
+ this._pose = updatePose(this._pose, delta);
453
+ this.sendPose(this._pose);
454
+ }
455
+ setPoseOffset(abs) {
456
+ this._pose = setPoseOffset(this._pose, abs);
457
+ this.sendPose(this._pose);
458
+ }
459
+ /** Start periodic pose refresh at ~27 Hz (matching MQDH cadence). */
460
+ startPoseLoop() {
461
+ if (this._poseLoopActive)
462
+ return;
463
+ this._poseLoopActive = true;
464
+ verbose("Pose loop started (~27 Hz)");
465
+ this._poseLoopTimer = setInterval(() => {
466
+ if (this._connected && this.subMagic) {
467
+ this.sendXrsp(buildPose(this.subMagic, this.nextSeq(), this._pose));
468
+ }
469
+ }, 37);
470
+ this.emit("pose-loop", true);
471
+ }
472
+ /** Stop periodic pose refresh. */
473
+ stopPoseLoop() {
474
+ if (!this._poseLoopActive)
475
+ return;
476
+ this._poseLoopActive = false;
477
+ if (this._poseLoopTimer) {
478
+ clearInterval(this._poseLoopTimer);
479
+ this._poseLoopTimer = null;
480
+ }
481
+ verbose("Pose loop stopped");
482
+ this.emit("pose-loop", false);
483
+ }
484
+ sendDisplayConfig(width, height, eye) {
485
+ if (this._connected && this.subMagic) {
486
+ // Reset first, then config (matching Python behavior)
487
+ this.sendXrsp(buildInit(this.subMagic, this.nextSeq()));
488
+ this.sendXrsp(buildDisplayConfig(this.subMagic, this.nextSeq(), width, height, eye));
489
+ this._width = width;
490
+ this._height = height;
491
+ if (eye !== undefined)
492
+ this._eye = eye;
493
+ }
494
+ }
495
+ async sendClick(x = 0.5, y = 0.5, layerId, holdMs = 50) {
496
+ if (!this._connected || !this.subMagic)
497
+ return;
498
+ // Auto-select layer
499
+ const lid = layerId ?? this.autoSelectLayer();
500
+ // MOVE events to position virtual cursor
501
+ for (let i = 0; i < 3; i++) {
502
+ this.sendXrsp(buildVirtualMouse(this.subMagic, this.nextSeq(), lid, 1, x, y));
503
+ await new Promise((r) => setTimeout(r, 16));
504
+ }
505
+ // DOWN
506
+ this.sendXrsp(buildVirtualMouse(this.subMagic, this.nextSeq(), lid, 3, x, y));
507
+ await new Promise((r) => setTimeout(r, holdMs));
508
+ // UP
509
+ this.sendXrsp(buildVirtualMouse(this.subMagic, this.nextSeq(), lid, 4, x, y));
510
+ }
511
+ ensureInputForwarding() {
512
+ if (this.inputForwardingStarted)
513
+ return;
514
+ if (!this._connected || !this.subMagic)
515
+ return;
516
+ this.sendXrsp(buildSetProperty(this.subMagic, this.nextSeq(), "input_forwarding_gaze_click", "true"));
517
+ this.sendXrsp(buildInputForwardingState(this.subMagic, this.nextSeq(), 1));
518
+ this.sendXrsp(buildStartInputForwarding(this.subMagic, this.nextSeq()));
519
+ this.sendXrsp(buildActivateLayer(this.subMagic, this.nextSeq(), this._layerId));
520
+ this.inputForwardingStarted = true;
521
+ verbose("Input forwarding enabled (CAMERA mode, gaze_click=true)");
522
+ }
523
+ sendInputForwardingState(state) {
524
+ if (this._connected && this.subMagic) {
525
+ this.sendXrsp(buildInputForwardingState(this.subMagic, this.nextSeq(), state));
526
+ if (state === 0) {
527
+ this.inputForwardingStarted = false;
528
+ }
529
+ }
530
+ }
531
+ resetView() {
532
+ this.stopPoseLoop();
533
+ this.sendInputForwardingState(0);
534
+ this._pose = createPoseState();
535
+ }
536
+ sendSetProperty(name, value) {
537
+ if (this._connected && this.subMagic) {
538
+ this.sendXrsp(buildSetProperty(this.subMagic, this.nextSeq(), name, value));
539
+ }
540
+ }
541
+ sendMud(typeId, payload) {
542
+ if (this._connected && this.subMagic) {
543
+ this.sendXrsp(buildMud(this.subMagic, this.nextSeq(), typeId, payload));
544
+ }
545
+ }
546
+ sendRotation(pitch = 0, yaw = 0, forward = 0, strafe = 0) {
547
+ this.applyPoseDelta({ dYaw: yaw, dPitch: pitch, dForward: forward, dStrafe: strafe });
548
+ }
549
+ // --- Helpers ---
550
+ autoSelectLayer() {
551
+ let bestId = this._layerId;
552
+ let bestArea = 0;
553
+ for (const [id, info] of this._layers) {
554
+ if (info.type === 0 || info.type === 3) { // PANEL_APP or VOLUMETRIC_WINDOW
555
+ const area = info.width * info.height;
556
+ if (area > bestArea) {
557
+ bestId = id;
558
+ bestArea = area;
559
+ }
560
+ }
561
+ }
562
+ return bestId;
563
+ }
564
+ }
565
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/cast/session.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"logcat.d.ts","sourceRoot":"","sources":["../../src/commands/logcat.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA8GH;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEjE;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA+BjD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAkCnD;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBjD"}
1
+ {"version":3,"file":"logcat.d.ts","sourceRoot":"","sources":["../../src/commands/logcat.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA8GH;;GAEG;AACH,wBAAsB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuEjE;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA+BjD;AAED;;GAEG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAkCnD;AAED;;GAEG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAoBjD"}
@@ -5,12 +5,12 @@
5
5
  * CRITICAL: Quest's ring buffer fills in seconds under VR load.
6
6
  * Always capture to a file BEFORE testing to avoid losing crash logs.
7
7
  */
8
- import { join } from 'path';
9
- import { readFileSync, writeFileSync, unlinkSync, existsSync, mkdirSync, symlinkSync, statSync, readlinkSync, openSync } from 'fs';
8
+ import { resolve, join } from 'path';
9
+ import { readFileSync, writeFileSync, unlinkSync, existsSync, mkdirSync, symlinkSync, statSync, readlinkSync, openSync, closeSync } from 'fs';
10
10
  import { spawn } from 'child_process';
11
- import { checkADBPath, checkADBDevices } from '../utils/adb.js';
11
+ import { checkADBPath, checkADBDevices, adbArgs } from '../utils/adb.js';
12
12
  import { execCommand } from '../utils/exec.js';
13
- const LOG_DIR = process.env.LOG_DIR || 'logs/logcat';
13
+ const LOG_DIR = resolve(process.env.LOG_DIR || 'logs/logcat');
14
14
  const PID_FILE = join(LOG_DIR, '.logcat_pid');
15
15
  const LOGFILE_LINK = join(LOG_DIR, 'latest.txt');
16
16
  /**
@@ -131,7 +131,7 @@ export async function startCommand(filter) {
131
131
  console.log(`Starting capture to: ${logFile}`);
132
132
  // Clear the buffer first - critical for Quest
133
133
  try {
134
- await execCommand('adb', ['logcat', '-c']);
134
+ await execCommand('adb', adbArgs('logcat', '-c'));
135
135
  console.log('Ring buffer cleared.');
136
136
  }
137
137
  catch (error) {
@@ -142,7 +142,7 @@ export async function startCommand(filter) {
142
142
  console.log(`Filter: ${filter}`);
143
143
  }
144
144
  // Start background logcat process
145
- const args = ['logcat', '-v', 'threadtime'];
145
+ const args = adbArgs('logcat', '-v', 'threadtime');
146
146
  if (filter) {
147
147
  args.push(filter);
148
148
  }
@@ -152,6 +152,7 @@ export async function startCommand(filter) {
152
152
  stdio: ['ignore', fd, fd],
153
153
  detached: true
154
154
  });
155
+ closeSync(fd); // child process owns the fd now
155
156
  // Unref so parent can exit immediately
156
157
  proc.unref();
157
158
  // Save PID