@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,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(pnpm build:*)"
5
+ ]
6
+ }
7
+ }
@@ -0,0 +1,45 @@
1
+ name: Deploy Documentation
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths:
7
+ - 'packages/cast2-protocol/docs/**'
8
+
9
+ permissions:
10
+ contents: read
11
+ pages: write
12
+ id-token: write
13
+
14
+ concurrency:
15
+ group: pages
16
+ cancel-in-progress: true
17
+
18
+ jobs:
19
+ build:
20
+ runs-on: ubuntu-latest
21
+ steps:
22
+ - uses: actions/checkout@v4
23
+
24
+ - name: Setup Pages
25
+ uses: actions/configure-pages@v5
26
+
27
+ - name: Build with Jekyll
28
+ uses: actions/jekyll-build-pages@v1
29
+ with:
30
+ source: packages/cast2-protocol/docs
31
+ destination: ./_site
32
+
33
+ - name: Upload artifact
34
+ uses: actions/upload-pages-artifact@v3
35
+
36
+ deploy:
37
+ environment:
38
+ name: github-pages
39
+ url: ${{ steps.deployment.outputs.page_url }}
40
+ runs-on: ubuntu-latest
41
+ needs: build
42
+ steps:
43
+ - name: Deploy to GitHub Pages
44
+ id: deployment
45
+ uses: actions/deploy-pages@v4
@@ -31,6 +31,9 @@ jobs:
31
31
  - name: Install dependencies
32
32
  run: pnpm install
33
33
 
34
+ - name: Build workspace packages
35
+ run: pnpm -r run build
36
+
34
37
  - name: Run tests
35
38
  run: pnpm test
36
39
 
@@ -43,4 +46,11 @@ jobs:
43
46
  - name: Create GitHub Release
44
47
  env:
45
48
  GH_TOKEN: ${{ github.token }}
46
- run: gh release create ${{ github.ref_name }} --generate-notes
49
+ run: |
50
+ PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
51
+ if [ -n "$PREV_TAG" ]; then
52
+ NOTES=$(git log --pretty=format:"- %s" "$PREV_TAG"..HEAD^)
53
+ else
54
+ NOTES="Initial release"
55
+ fi
56
+ gh release create "${{ github.ref_name }}" --notes "$NOTES"
package/README.md CHANGED
@@ -23,6 +23,33 @@ npm install -g @myerscarpenter/quest-dev
23
23
  npm install -g @myerscarpenter/cdp-cli
24
24
  ```
25
25
 
26
+ ## Casting Setup
27
+
28
+ quest-dev can cast your Quest's screen to your computer. This requires a casting APK that ships inside Meta Quest Developer Hub (MQDH).
29
+
30
+ **One-time setup:**
31
+
32
+ ```bash
33
+ quest-dev setup-cast
34
+ ```
35
+
36
+ On macOS, this auto-detects MQDH if it's installed in `/Applications`. Otherwise, download MQDH first and point to it:
37
+
38
+ ```bash
39
+ # macOS — .app bundle (easiest)
40
+ quest-dev setup-cast "/Applications/Meta Quest Developer Hub.app"
41
+
42
+ # macOS — .dmg download
43
+ quest-dev setup-cast ~/Downloads/MetaQuestDeveloperHub.dmg
44
+
45
+ # Windows/Linux — .exe.zip download (requires 7z)
46
+ quest-dev setup-cast ~/Downloads/Meta-Quest-Developer-Hub.exe.zip
47
+ ```
48
+
49
+ Download MQDH from: https://developer.oculus.com/meta-quest-developer-hub
50
+
51
+ The APK is extracted once and cached at `~/.local/share/quest-dev/`. When you start casting, it's auto-installed on your Quest if needed.
52
+
26
53
  ## Usage
27
54
 
28
55
  ### Screenshot
@@ -0,0 +1,48 @@
1
+ /**
2
+ * H.264 to JPEG frame decoder using a persistent ffmpeg subprocess.
3
+ *
4
+ * Spawns ffmpeg with H.264 stdin → MJPEG stdout, extracts individual
5
+ * JPEG frames by scanning for SOI (FF D8) / EOI (FF D9) markers.
6
+ */
7
+ import { EventEmitter } from "node:events";
8
+ /**
9
+ * Extract complete JPEG frames from a buffer.
10
+ * Returns extracted frames and the remaining unprocessed bytes.
11
+ */
12
+ export declare function extractJpegFrames(buf: Buffer): {
13
+ frames: Buffer[];
14
+ remainder: Buffer;
15
+ };
16
+ export interface FrameDecoderEvents {
17
+ frame: [jpeg: Buffer];
18
+ error: [err: Error];
19
+ }
20
+ /**
21
+ * Persistent ffmpeg-based H.264 → JPEG decoder.
22
+ *
23
+ * Usage:
24
+ * const decoder = new FrameDecoder();
25
+ * decoder.start();
26
+ * decoder.feed(nalData);
27
+ * const jpeg = decoder.getFrame();
28
+ * decoder.stop();
29
+ */
30
+ export declare class FrameDecoder extends EventEmitter {
31
+ private proc;
32
+ private latestFrame;
33
+ private readBuffer;
34
+ private running;
35
+ /** Check that ffmpeg is available on PATH. */
36
+ static checkFfmpeg(): void;
37
+ /** Start the persistent ffmpeg decoder process. */
38
+ start(): void;
39
+ /** Feed H.264 NAL data to the decoder. */
40
+ feed(nalData: Buffer): void;
41
+ /** Get the latest decoded JPEG frame, or null if none available. */
42
+ getFrame(): Buffer | null;
43
+ /** Stop the decoder and kill the ffmpeg process. */
44
+ stop(): void;
45
+ /** Whether the decoder is currently running. */
46
+ get isRunning(): boolean;
47
+ }
48
+ //# sourceMappingURL=decoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decoder.d.ts","sourceRoot":"","sources":["../../src/cast/decoder.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAS3C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC9C,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB,CAkCA;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtB,KAAK,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;CACrB;AAED;;;;;;;;;GASG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAO,CAAC,IAAI,CAA6B;IACzC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,OAAO,CAAS;IAExB,8CAA8C;IAC9C,MAAM,CAAC,WAAW,IAAI,IAAI;IAU1B,mDAAmD;IACnD,KAAK,IAAI,IAAI;IA2Cb,0CAA0C;IAC1C,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAgB3B,oEAAoE;IACpE,QAAQ,IAAI,MAAM,GAAG,IAAI;IAIzB,oDAAoD;IACpD,IAAI,IAAI,IAAI;IAcZ,gDAAgD;IAChD,IAAI,SAAS,IAAI,OAAO,CAEvB;CACF"}
@@ -0,0 +1,152 @@
1
+ /**
2
+ * H.264 to JPEG frame decoder using a persistent ffmpeg subprocess.
3
+ *
4
+ * Spawns ffmpeg with H.264 stdin → MJPEG stdout, extracts individual
5
+ * JPEG frames by scanning for SOI (FF D8) / EOI (FF D9) markers.
6
+ */
7
+ import { spawn } from "node:child_process";
8
+ import { EventEmitter } from "node:events";
9
+ import which from "which";
10
+ import { verbose } from "../utils/verbose.js";
11
+ // JPEG markers
12
+ const JPEG_SOI = 0xffd8;
13
+ const JPEG_EOI_0 = 0xff;
14
+ const JPEG_EOI_1 = 0xd9;
15
+ /**
16
+ * Extract complete JPEG frames from a buffer.
17
+ * Returns extracted frames and the remaining unprocessed bytes.
18
+ */
19
+ export function extractJpegFrames(buf) {
20
+ const frames = [];
21
+ let data = buf;
22
+ while (true) {
23
+ // Find SOI marker (FF D8)
24
+ let start = -1;
25
+ for (let i = 0; i < data.length - 1; i++) {
26
+ if (data[i] === 0xff && data[i + 1] === 0xd8) {
27
+ start = i;
28
+ break;
29
+ }
30
+ }
31
+ if (start < 0) {
32
+ // No SOI found — discard everything
33
+ return { frames, remainder: Buffer.alloc(0) };
34
+ }
35
+ // Find EOI marker (FF D9) after SOI
36
+ let end = -1;
37
+ for (let i = start + 2; i < data.length - 1; i++) {
38
+ if (data[i] === JPEG_EOI_0 && data[i + 1] === JPEG_EOI_1) {
39
+ end = i + 2; // include both EOI bytes
40
+ break;
41
+ }
42
+ }
43
+ if (end < 0) {
44
+ // Incomplete frame — keep from SOI onward
45
+ return { frames, remainder: data.subarray(start) };
46
+ }
47
+ frames.push(Buffer.from(data.subarray(start, end)));
48
+ data = data.subarray(end);
49
+ }
50
+ }
51
+ /**
52
+ * Persistent ffmpeg-based H.264 → JPEG decoder.
53
+ *
54
+ * Usage:
55
+ * const decoder = new FrameDecoder();
56
+ * decoder.start();
57
+ * decoder.feed(nalData);
58
+ * const jpeg = decoder.getFrame();
59
+ * decoder.stop();
60
+ */
61
+ export class FrameDecoder extends EventEmitter {
62
+ proc = null;
63
+ latestFrame = null;
64
+ readBuffer = Buffer.alloc(0);
65
+ running = false;
66
+ /** Check that ffmpeg is available on PATH. */
67
+ static checkFfmpeg() {
68
+ try {
69
+ which.sync("ffmpeg");
70
+ }
71
+ catch {
72
+ throw new Error("ffmpeg not found on PATH. Install ffmpeg to enable video decoding.");
73
+ }
74
+ }
75
+ /** Start the persistent ffmpeg decoder process. */
76
+ start() {
77
+ if (this.running)
78
+ return;
79
+ this.running = true;
80
+ this.readBuffer = Buffer.alloc(0);
81
+ this.proc = spawn("ffmpeg", [
82
+ "-loglevel", "error",
83
+ "-f", "h264", "-i", "pipe:0",
84
+ "-q:v", "3",
85
+ "-f", "image2pipe", "-vcodec", "mjpeg", "pipe:1",
86
+ ], { stdio: ["pipe", "pipe", "pipe"] });
87
+ this.proc.stdout.on("data", (chunk) => {
88
+ this.readBuffer = Buffer.concat([this.readBuffer, chunk]);
89
+ const { frames, remainder } = extractJpegFrames(this.readBuffer);
90
+ this.readBuffer = remainder;
91
+ if (frames.length > 0) {
92
+ this.latestFrame = frames[frames.length - 1];
93
+ this.emit("frame", this.latestFrame);
94
+ }
95
+ });
96
+ this.proc.stderr.on("data", (chunk) => {
97
+ verbose("ffmpeg stderr:", chunk.toString().trim());
98
+ });
99
+ this.proc.on("exit", (code) => {
100
+ verbose("ffmpeg decoder exited with code", code);
101
+ this.running = false;
102
+ });
103
+ this.proc.on("error", (err) => {
104
+ this.emit("error", err);
105
+ this.running = false;
106
+ });
107
+ verbose("Started persistent ffmpeg decoder");
108
+ }
109
+ /** Feed H.264 NAL data to the decoder. */
110
+ feed(nalData) {
111
+ if (!this.proc?.stdin?.writable)
112
+ return;
113
+ try {
114
+ this.proc.stdin.write(nalData);
115
+ }
116
+ catch {
117
+ verbose("Decoder pipe broken, restarting...");
118
+ this.stop();
119
+ this.start();
120
+ try {
121
+ this.proc?.stdin?.write(nalData);
122
+ }
123
+ catch {
124
+ // Give up on this NAL
125
+ }
126
+ }
127
+ }
128
+ /** Get the latest decoded JPEG frame, or null if none available. */
129
+ getFrame() {
130
+ return this.latestFrame;
131
+ }
132
+ /** Stop the decoder and kill the ffmpeg process. */
133
+ stop() {
134
+ this.running = false;
135
+ if (this.proc) {
136
+ try {
137
+ this.proc.stdin?.end();
138
+ this.proc.kill();
139
+ }
140
+ catch {
141
+ // Already dead
142
+ }
143
+ this.proc = null;
144
+ }
145
+ this.readBuffer = Buffer.alloc(0);
146
+ }
147
+ /** Whether the decoder is currently running. */
148
+ get isRunning() {
149
+ return this.running;
150
+ }
151
+ }
152
+ //# sourceMappingURL=decoder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decoder.js","sourceRoot":"","sources":["../../src/cast/decoder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * CastSession: manages TCP connections to Quest, protocol handshake,
3
+ * H.264 video decoding, pose control, and stay-awake lifecycle.
4
+ */
5
+ import { EventEmitter } from "node:events";
6
+ import { type LayerInfo, type PoseOffset, type PoseDelta, type PoseState } from "@myerscarpenter/cast2-protocol";
7
+ export interface CastSessionOptions {
8
+ listenPort?: number;
9
+ width?: number;
10
+ height?: number;
11
+ }
12
+ export declare class CastSession extends EventEmitter {
13
+ private _listenPort;
14
+ private _connected;
15
+ private _running;
16
+ private server;
17
+ private controlSocket;
18
+ private videoSocket;
19
+ private controlSeq;
20
+ private subMagic;
21
+ private controlMsgSeq;
22
+ private sessionUuid;
23
+ private sessionTimestamp;
24
+ private questMsgSeq;
25
+ private decoder;
26
+ private currentSpsPps;
27
+ private currentIdr;
28
+ private _frameCount;
29
+ private _byteCount;
30
+ private startTime;
31
+ private _width;
32
+ private _height;
33
+ private _pose;
34
+ private _poseLoopTimer;
35
+ private _poseLoopActive;
36
+ private _eye;
37
+ private inputForwardingStarted;
38
+ private _layerId;
39
+ private _layerAutoSelected;
40
+ private _layers;
41
+ constructor(options?: CastSessionOptions);
42
+ get listenPort(): number;
43
+ get connected(): boolean;
44
+ get running(): boolean;
45
+ get frameCount(): number;
46
+ get byteCount(): number;
47
+ get width(): number;
48
+ get height(): number;
49
+ get pose(): PoseState;
50
+ get layers(): Map<number, LayerInfo>;
51
+ get layerId(): number;
52
+ get poseLoopActive(): boolean;
53
+ get eye(): number;
54
+ get elapsedSeconds(): number;
55
+ get fps(): number;
56
+ /** Bind the TCP server, trying successive ports on EADDRINUSE. */
57
+ bind(): Promise<void>;
58
+ private tryBind;
59
+ start(questIp?: string): Promise<void>;
60
+ stop(): Promise<void>;
61
+ restart(): Promise<void>;
62
+ adbSetup(questIp: string): Promise<void>;
63
+ startCastService(questIp: string): Promise<void>;
64
+ private waitForConnections;
65
+ private sendXrsp;
66
+ private nextSeq;
67
+ private handleVideo;
68
+ private processVideoPayload;
69
+ getScreenshot(): Buffer | null;
70
+ sendPose(pose: PoseState): void;
71
+ applyPoseDelta(delta: PoseDelta): void;
72
+ setPoseOffset(abs: PoseOffset): void;
73
+ /** Start periodic pose refresh at ~27 Hz (matching MQDH cadence). */
74
+ startPoseLoop(): void;
75
+ /** Stop periodic pose refresh. */
76
+ stopPoseLoop(): void;
77
+ sendDisplayConfig(width: number, height: number, eye?: number): void;
78
+ sendClick(x?: number, y?: number, layerId?: number, holdMs?: number): Promise<void>;
79
+ ensureInputForwarding(): void;
80
+ sendInputForwardingState(state: number): void;
81
+ resetView(): void;
82
+ sendSetProperty(name: string, value: string): void;
83
+ sendMud(typeId: number, payload?: Buffer): void;
84
+ sendRotation(pitch?: number, yaw?: number, forward?: number, strafe?: number): void;
85
+ private autoSelectLayer;
86
+ }
87
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/cast/session.ts"],"names":[],"mappings":"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"}