@elizaos/plugin-xr 2.0.3-beta.5

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 (39) hide show
  1. package/AGENTS.md +151 -0
  2. package/CLAUDE.md +151 -0
  3. package/LICENSE +21 -0
  4. package/README.md +106 -0
  5. package/package.json +57 -0
  6. package/simulator/bun.lock +159 -0
  7. package/simulator/package.json +28 -0
  8. package/simulator/src/emulator.ts +174 -0
  9. package/simulator/src/mock-agent.ts +233 -0
  10. package/simulator/src/node.ts +9 -0
  11. package/simulator/src/playwright-fixture.ts +169 -0
  12. package/simulator/src/types.ts +51 -0
  13. package/simulator/tsconfig.json +13 -0
  14. package/simulator/vite.config.ts +25 -0
  15. package/src/__tests__/audio-pipeline.test.ts +129 -0
  16. package/src/__tests__/protocol.test.ts +53 -0
  17. package/src/__tests__/routes-e2e.test.ts +276 -0
  18. package/src/__tests__/vision-pipeline.test.ts +73 -0
  19. package/src/__tests__/xr-bundle-coverage.test.ts +303 -0
  20. package/src/__tests__/xr-feature-parity.test.ts +524 -0
  21. package/src/__tests__/xr-functional-parity.test.ts +522 -0
  22. package/src/__tests__/xr-view-host-http.test.ts +239 -0
  23. package/src/__tests__/xr-view-host.test.ts +174 -0
  24. package/src/actions/xr-query-vision.ts +64 -0
  25. package/src/actions/xr-view-actions.ts +386 -0
  26. package/src/index.ts +55 -0
  27. package/src/protocol.ts +126 -0
  28. package/src/providers/xr-context.ts +49 -0
  29. package/src/routes/xr-connect.ts +89 -0
  30. package/src/routes/xr-simulator-route.ts +37 -0
  31. package/src/routes/xr-status.ts +36 -0
  32. package/src/routes/xr-view-host.ts +359 -0
  33. package/src/routes/xr-views.ts +43 -0
  34. package/src/services/audio-pipeline.ts +120 -0
  35. package/src/services/vision-pipeline.ts +57 -0
  36. package/src/services/xr-session-service.ts +388 -0
  37. package/tsconfig.build.json +9 -0
  38. package/tsconfig.json +30 -0
  39. package/vitest.config.ts +21 -0
package/AGENTS.md ADDED
@@ -0,0 +1,151 @@
1
+ # @elizaos/plugin-xr
2
+
3
+ WebXR audio/video streaming for elizaOS — Quest 3 and XReal glasses.
4
+
5
+ ## Purpose / role
6
+
7
+ Adds a WebXR streaming surface to an Eliza agent: a WebSocket server accepts
8
+ connections from XR headsets (Quest 3, XReal, or a browser simulator), pipes
9
+ microphone audio through the runtime's TRANSCRIPTION model, and routes the
10
+ transcript through the standard message pipeline. Voice responses are generated
11
+ via the TEXT_TO_SPEECH model and sent back as binary audio frames. The plugin
12
+ is opt-in — register `xrPlugin` in your character's plugins array.
13
+
14
+ ## Plugin surface
15
+
16
+ **Service**
17
+ - `XRSessionService` (`xr-session`) — WebSocket server on port 31338 (default).
18
+ Manages per-connection lifecycle, delegates audio to `AudioPipeline` and
19
+ camera frames to `VisionPipeline`, routes transcripts into the agent message
20
+ pipeline, and sends TTS audio back to the headset.
21
+
22
+ **Actions**
23
+ - `XR_QUERY_VISION` — describes what the user's XR camera currently sees
24
+ (calls `VisionPipeline.describeFrame` → `ModelType.IMAGE_DESCRIPTION`).
25
+ Only validates when a recent camera frame exists.
26
+ - `XR_OPEN_VIEW` — opens a named view panel on the headset (sends
27
+ `view_open` control message).
28
+ - `XR_CLOSE_VIEW` — closes a named view (or all views if no id given).
29
+ - `XR_SWITCH_VIEW` — brings a view to the foreground without closing others.
30
+ - `XR_LIST_VIEWS` — enumerates views with `viewType: "xr"` from all loaded
31
+ plugins and optionally sends the catalog to the device.
32
+ - `XR_RESIZE_VIEW` — resizes/repositions the active panel (`scale`,
33
+ `distance`, `fullscreen` options).
34
+
35
+ **Provider**
36
+ - `XR_SESSION` (`xr-context.ts`) — injects connected device list and camera
37
+ state into the agent context block when at least one headset is connected.
38
+
39
+ **Routes** (all under `/api/xr/`)
40
+ - `GET /xr/status` — JSON list of connected sessions and camera-frame state.
41
+ - `GET /xr/connect` — HTML page with QR code for pairing a headset.
42
+ - `GET /xr/views` — JSON list of all `viewType: "xr"` views registered by
43
+ loaded plugins, plus active connections.
44
+ - `GET /xr/view-host/:id` — self-contained HTML shell that dynamically imports
45
+ a plugin view bundle and renders it with an XR-optimised chrome.
46
+ - `GET /xr/simulator.js` — serves the built WebXR emulator bundle (only
47
+ available after `bun run build:all`).
48
+
49
+ ## Layout
50
+
51
+ ```
52
+ plugins/plugin-xr/
53
+ src/
54
+ index.ts Plugin entry — exports xrPlugin and public types
55
+ protocol.ts Wire types: XRClientControl, XRServerControl,
56
+ XRBinaryHeader, XRTTSAudioHeader; encode/decode helpers
57
+ actions/
58
+ xr-query-vision.ts XR_QUERY_VISION action
59
+ xr-view-actions.ts XR_OPEN/CLOSE/SWITCH/LIST/RESIZE_VIEW actions
60
+ providers/
61
+ xr-context.ts XR_SESSION provider
62
+ services/
63
+ xr-session-service.ts XRSessionService (WebSocket server, main orchestrator)
64
+ audio-pipeline.ts AudioPipeline — buffers audio chunks, flushes to
65
+ TRANSCRIPTION model after 2 s or 1.5 s silence
66
+ vision-pipeline.ts VisionPipeline — stores latest camera frame (max age
67
+ 10 s), calls IMAGE_DESCRIPTION model on demand
68
+ routes/
69
+ xr-status.ts GET /xr/status
70
+ xr-connect.ts GET /xr/connect
71
+ xr-views.ts GET /xr/views
72
+ xr-view-host.ts GET /xr/view-host/:id
73
+ xr-simulator-route.ts GET /xr/simulator.js
74
+ __tests__/
75
+ audio-pipeline.test.ts
76
+ protocol.test.ts
77
+ vision-pipeline.test.ts
78
+ xr-bundle-coverage.test.ts
79
+ xr-feature-parity.test.ts
80
+ xr-functional-parity.test.ts
81
+ xr-view-host.test.ts
82
+ xr-view-host-http.test.ts
83
+ simulator/ Browser-side WebXR emulator (Vite build)
84
+ ```
85
+
86
+ ## Commands
87
+
88
+ ```bash
89
+ bun run --cwd plugins/plugin-xr typecheck
90
+ bun run --cwd plugins/plugin-xr lint
91
+ bun run --cwd plugins/plugin-xr test
92
+ bun run --cwd plugins/plugin-xr build
93
+ bun run --cwd plugins/plugin-xr build:all # also builds simulator/
94
+ bun run --cwd plugins/plugin-xr simulator:build
95
+ bun run --cwd plugins/plugin-xr simulator:watch
96
+ bun run --cwd plugins/plugin-xr clean
97
+ ```
98
+
99
+ ## Config / env vars
100
+
101
+ | Var | Default | Required | Purpose |
102
+ |-----|---------|----------|---------|
103
+ | `XR_WS_PORT` | `31338` | no | WebSocket server port |
104
+ | `XR_AGENT_URL` | `http://localhost:<agent-port>` | no | Public base URL sent to the headset for view bundles |
105
+ | `XR_APP_URL` | derived from `VITE_PORT` | no | URL shown on the `/xr/connect` pairing page |
106
+
107
+ The plugin sets `config: { XR_WS_PORT: 31338 }` in the Plugin object so the
108
+ runtime exposes it via `runtime.getSetting("XR_WS_PORT")`.
109
+
110
+ The agent must have a TRANSCRIPTION model and TEXT_TO_SPEECH model configured
111
+ (e.g., via `@elizaos/plugin-openai` or a local inference plugin) for audio
112
+ streaming to work. IMAGE_DESCRIPTION is required for `XR_QUERY_VISION`.
113
+
114
+ ## How to extend
115
+
116
+ **Add an action** — create `src/actions/<name>.ts`, implement `Action` from
117
+ `@elizaos/core`, get `XRSessionService` via
118
+ `runtime.getService<XRSessionService>(XR_SERVICE_TYPE)`, then add the import
119
+ and the object to the `actions` array in `src/index.ts`.
120
+
121
+ **Add a provider** — create `src/providers/<name>.ts`, implement `Provider`,
122
+ add to `providers` array in `src/index.ts`.
123
+
124
+ **Add a route** — create `src/routes/<name>.ts`, implement `Route`, add to
125
+ `routes` array in `src/index.ts`.
126
+
127
+ **Expose a view in XR** — in any other plugin, add a `views` array entry with
128
+ `viewType: "xr"`. `XR_LIST_VIEWS` and `GET /xr/views` collect views with this
129
+ field across all loaded plugins at runtime.
130
+
131
+ ## Conventions / gotchas
132
+
133
+ - **WebXR requires HTTPS on device.** The `/xr/connect` page warns when the
134
+ URL is plain HTTP. Use a local tunnel (e.g., `cloudflared`) and set
135
+ `XR_APP_URL` to the HTTPS tunnel URL.
136
+ - **Binary frame framing** is defined in `src/protocol.ts`: 4-byte big-endian
137
+ header length, then UTF-8 JSON header, then raw payload. Use
138
+ `encodeBinaryFrame` / `decodeBinaryFrame` from that module — do not
139
+ reimplement the framing.
140
+ - **Audio buffering** — `AudioPipeline` accumulates chunks and flushes after
141
+ 2 000 ms of audio or 1 500 ms of silence. Chunks shorter than 512 bytes are
142
+ dropped. `pcm-f32` encoding (ScriptProcessorNode fallback) is wrapped in a
143
+ WAV header before being passed to TRANSCRIPTION.
144
+ - **Simulator bundle** — `simulator/` is a separate Vite project. Run
145
+ `bun run build:all` (or `simulator:build`) before the `/xr/simulator.js`
146
+ route will serve anything. The route returns 404 until the bundle exists.
147
+ - **`XR_AGENT_URL`** must be a reachable URL from inside the XR headset
148
+ browser when loading view bundles. `localhost` will only work when testing
149
+ on the same machine via the browser simulator.
150
+ - See repo root `AGENTS.md` for repo-wide architecture rules, logger
151
+ conventions, ESM requirements, and naming standards.
package/CLAUDE.md ADDED
@@ -0,0 +1,151 @@
1
+ # @elizaos/plugin-xr
2
+
3
+ WebXR audio/video streaming for elizaOS — Quest 3 and XReal glasses.
4
+
5
+ ## Purpose / role
6
+
7
+ Adds a WebXR streaming surface to an Eliza agent: a WebSocket server accepts
8
+ connections from XR headsets (Quest 3, XReal, or a browser simulator), pipes
9
+ microphone audio through the runtime's TRANSCRIPTION model, and routes the
10
+ transcript through the standard message pipeline. Voice responses are generated
11
+ via the TEXT_TO_SPEECH model and sent back as binary audio frames. The plugin
12
+ is opt-in — register `xrPlugin` in your character's plugins array.
13
+
14
+ ## Plugin surface
15
+
16
+ **Service**
17
+ - `XRSessionService` (`xr-session`) — WebSocket server on port 31338 (default).
18
+ Manages per-connection lifecycle, delegates audio to `AudioPipeline` and
19
+ camera frames to `VisionPipeline`, routes transcripts into the agent message
20
+ pipeline, and sends TTS audio back to the headset.
21
+
22
+ **Actions**
23
+ - `XR_QUERY_VISION` — describes what the user's XR camera currently sees
24
+ (calls `VisionPipeline.describeFrame` → `ModelType.IMAGE_DESCRIPTION`).
25
+ Only validates when a recent camera frame exists.
26
+ - `XR_OPEN_VIEW` — opens a named view panel on the headset (sends
27
+ `view_open` control message).
28
+ - `XR_CLOSE_VIEW` — closes a named view (or all views if no id given).
29
+ - `XR_SWITCH_VIEW` — brings a view to the foreground without closing others.
30
+ - `XR_LIST_VIEWS` — enumerates views with `viewType: "xr"` from all loaded
31
+ plugins and optionally sends the catalog to the device.
32
+ - `XR_RESIZE_VIEW` — resizes/repositions the active panel (`scale`,
33
+ `distance`, `fullscreen` options).
34
+
35
+ **Provider**
36
+ - `XR_SESSION` (`xr-context.ts`) — injects connected device list and camera
37
+ state into the agent context block when at least one headset is connected.
38
+
39
+ **Routes** (all under `/api/xr/`)
40
+ - `GET /xr/status` — JSON list of connected sessions and camera-frame state.
41
+ - `GET /xr/connect` — HTML page with QR code for pairing a headset.
42
+ - `GET /xr/views` — JSON list of all `viewType: "xr"` views registered by
43
+ loaded plugins, plus active connections.
44
+ - `GET /xr/view-host/:id` — self-contained HTML shell that dynamically imports
45
+ a plugin view bundle and renders it with an XR-optimised chrome.
46
+ - `GET /xr/simulator.js` — serves the built WebXR emulator bundle (only
47
+ available after `bun run build:all`).
48
+
49
+ ## Layout
50
+
51
+ ```
52
+ plugins/plugin-xr/
53
+ src/
54
+ index.ts Plugin entry — exports xrPlugin and public types
55
+ protocol.ts Wire types: XRClientControl, XRServerControl,
56
+ XRBinaryHeader, XRTTSAudioHeader; encode/decode helpers
57
+ actions/
58
+ xr-query-vision.ts XR_QUERY_VISION action
59
+ xr-view-actions.ts XR_OPEN/CLOSE/SWITCH/LIST/RESIZE_VIEW actions
60
+ providers/
61
+ xr-context.ts XR_SESSION provider
62
+ services/
63
+ xr-session-service.ts XRSessionService (WebSocket server, main orchestrator)
64
+ audio-pipeline.ts AudioPipeline — buffers audio chunks, flushes to
65
+ TRANSCRIPTION model after 2 s or 1.5 s silence
66
+ vision-pipeline.ts VisionPipeline — stores latest camera frame (max age
67
+ 10 s), calls IMAGE_DESCRIPTION model on demand
68
+ routes/
69
+ xr-status.ts GET /xr/status
70
+ xr-connect.ts GET /xr/connect
71
+ xr-views.ts GET /xr/views
72
+ xr-view-host.ts GET /xr/view-host/:id
73
+ xr-simulator-route.ts GET /xr/simulator.js
74
+ __tests__/
75
+ audio-pipeline.test.ts
76
+ protocol.test.ts
77
+ vision-pipeline.test.ts
78
+ xr-bundle-coverage.test.ts
79
+ xr-feature-parity.test.ts
80
+ xr-functional-parity.test.ts
81
+ xr-view-host.test.ts
82
+ xr-view-host-http.test.ts
83
+ simulator/ Browser-side WebXR emulator (Vite build)
84
+ ```
85
+
86
+ ## Commands
87
+
88
+ ```bash
89
+ bun run --cwd plugins/plugin-xr typecheck
90
+ bun run --cwd plugins/plugin-xr lint
91
+ bun run --cwd plugins/plugin-xr test
92
+ bun run --cwd plugins/plugin-xr build
93
+ bun run --cwd plugins/plugin-xr build:all # also builds simulator/
94
+ bun run --cwd plugins/plugin-xr simulator:build
95
+ bun run --cwd plugins/plugin-xr simulator:watch
96
+ bun run --cwd plugins/plugin-xr clean
97
+ ```
98
+
99
+ ## Config / env vars
100
+
101
+ | Var | Default | Required | Purpose |
102
+ |-----|---------|----------|---------|
103
+ | `XR_WS_PORT` | `31338` | no | WebSocket server port |
104
+ | `XR_AGENT_URL` | `http://localhost:<agent-port>` | no | Public base URL sent to the headset for view bundles |
105
+ | `XR_APP_URL` | derived from `VITE_PORT` | no | URL shown on the `/xr/connect` pairing page |
106
+
107
+ The plugin sets `config: { XR_WS_PORT: 31338 }` in the Plugin object so the
108
+ runtime exposes it via `runtime.getSetting("XR_WS_PORT")`.
109
+
110
+ The agent must have a TRANSCRIPTION model and TEXT_TO_SPEECH model configured
111
+ (e.g., via `@elizaos/plugin-openai` or a local inference plugin) for audio
112
+ streaming to work. IMAGE_DESCRIPTION is required for `XR_QUERY_VISION`.
113
+
114
+ ## How to extend
115
+
116
+ **Add an action** — create `src/actions/<name>.ts`, implement `Action` from
117
+ `@elizaos/core`, get `XRSessionService` via
118
+ `runtime.getService<XRSessionService>(XR_SERVICE_TYPE)`, then add the import
119
+ and the object to the `actions` array in `src/index.ts`.
120
+
121
+ **Add a provider** — create `src/providers/<name>.ts`, implement `Provider`,
122
+ add to `providers` array in `src/index.ts`.
123
+
124
+ **Add a route** — create `src/routes/<name>.ts`, implement `Route`, add to
125
+ `routes` array in `src/index.ts`.
126
+
127
+ **Expose a view in XR** — in any other plugin, add a `views` array entry with
128
+ `viewType: "xr"`. `XR_LIST_VIEWS` and `GET /xr/views` collect views with this
129
+ field across all loaded plugins at runtime.
130
+
131
+ ## Conventions / gotchas
132
+
133
+ - **WebXR requires HTTPS on device.** The `/xr/connect` page warns when the
134
+ URL is plain HTTP. Use a local tunnel (e.g., `cloudflared`) and set
135
+ `XR_APP_URL` to the HTTPS tunnel URL.
136
+ - **Binary frame framing** is defined in `src/protocol.ts`: 4-byte big-endian
137
+ header length, then UTF-8 JSON header, then raw payload. Use
138
+ `encodeBinaryFrame` / `decodeBinaryFrame` from that module — do not
139
+ reimplement the framing.
140
+ - **Audio buffering** — `AudioPipeline` accumulates chunks and flushes after
141
+ 2 000 ms of audio or 1 500 ms of silence. Chunks shorter than 512 bytes are
142
+ dropped. `pcm-f32` encoding (ScriptProcessorNode fallback) is wrapped in a
143
+ WAV header before being passed to TRANSCRIPTION.
144
+ - **Simulator bundle** — `simulator/` is a separate Vite project. Run
145
+ `bun run build:all` (or `simulator:build`) before the `/xr/simulator.js`
146
+ route will serve anything. The route returns 404 until the bundle exists.
147
+ - **`XR_AGENT_URL`** must be a reachable URL from inside the XR headset
148
+ browser when loading view bundles. `localhost` will only work when testing
149
+ on the same machine via the browser simulator.
150
+ - See repo root `AGENTS.md` for repo-wide architecture rules, logger
151
+ conventions, ESM requirements, and naming standards.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Shaw Walters and elizaOS Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,106 @@
1
+ # @elizaos/plugin-xr
2
+
3
+ WebXR audio/video streaming for elizaOS — connects Quest 3, XReal glasses, or
4
+ any WebXR-capable browser to an Eliza agent.
5
+
6
+ ## What it does
7
+
8
+ - Opens a WebSocket server (default port 31338) that XR headsets connect to.
9
+ - Streams microphone audio from the headset, transcribes it via the runtime's
10
+ TRANSCRIPTION model, and routes the transcript through the agent's normal
11
+ message pipeline.
12
+ - Sends voice responses back to the headset as TTS audio (TEXT_TO_SPEECH model).
13
+ - Captures camera frames from the headset and can describe them using the
14
+ IMAGE_DESCRIPTION model (`XR_QUERY_VISION` action).
15
+ - Manages floating view panels on the headset — open, close, switch, resize,
16
+ and list views that other plugins have declared with `viewType: "xr"`.
17
+
18
+ ## Capabilities added to an Eliza agent
19
+
20
+ | Action | What it does |
21
+ |--------|-------------|
22
+ | `XR_QUERY_VISION` | Describes what the user's XR camera currently sees |
23
+ | `XR_OPEN_VIEW` | Opens a named floating panel on the headset |
24
+ | `XR_CLOSE_VIEW` | Closes a named panel (or all panels) |
25
+ | `XR_SWITCH_VIEW` | Brings a panel to the foreground |
26
+ | `XR_LIST_VIEWS` | Lists all XR-capable views and sends a launcher catalog to the device |
27
+ | `XR_RESIZE_VIEW` | Resizes or repositions the active panel |
28
+
29
+ The `XR_SESSION` context provider automatically injects connected-device status
30
+ and camera state into every conversation turn when a headset is connected.
31
+
32
+ ## HTTP routes
33
+
34
+ | Route | Purpose |
35
+ |-------|---------|
36
+ | `GET /api/xr/status` | JSON status of connected sessions |
37
+ | `GET /api/xr/connect` | HTML pairing page with QR code for the headset browser |
38
+ | `GET /api/xr/views` | JSON list of all registered XR views |
39
+ | `GET /api/xr/view-host/:id` | Self-contained XR shell page for a specific view |
40
+ | `GET /api/xr/simulator.js` | WebXR emulator bundle for testing (after build) |
41
+
42
+ ## Enabling the plugin
43
+
44
+ Add `@elizaos/plugin-xr` to your character's plugin list:
45
+
46
+ ```json
47
+ {
48
+ "name": "my-agent",
49
+ "plugins": ["@elizaos/plugin-xr"]
50
+ }
51
+ ```
52
+
53
+ The plugin also requires a character configuration that loads:
54
+ - A TRANSCRIPTION model provider (for speech-to-text)
55
+ - A TEXT_TO_SPEECH model provider (for voice responses)
56
+ - An IMAGE_DESCRIPTION model provider (for `XR_QUERY_VISION`)
57
+
58
+ ## Configuration
59
+
60
+ | Environment variable | Default | Purpose |
61
+ |---------------------|---------|---------|
62
+ | `XR_WS_PORT` | `31338` | WebSocket server port |
63
+ | `XR_AGENT_URL` | `http://localhost:<agent-port>` | Public base URL used by the headset to load view bundles — must be reachable from the device |
64
+ | `XR_APP_URL` | derived from `VITE_PORT` | URL shown on the `/api/xr/connect` pairing page |
65
+
66
+ ## Connecting a headset
67
+
68
+ 1. Start the agent with this plugin enabled.
69
+ 2. Open `http://localhost:31337/api/xr/connect` in a browser (adjust port to
70
+ your agent's API port).
71
+ 3. Scan the QR code with your Quest 3 or XReal browser, or navigate to the
72
+ shown URL.
73
+ 4. Allow microphone and camera access when prompted.
74
+ 5. The agent will start receiving audio immediately.
75
+
76
+ > **Note:** WebXR APIs require HTTPS on physical devices. For local
77
+ > development, run a tunnel (e.g., `cloudflared tunnel`) and set `XR_APP_URL`
78
+ > to the HTTPS tunnel URL before starting the agent.
79
+
80
+ ## Building the simulator
81
+
82
+ The `simulator/` sub-project provides a browser-side WebXR emulator for
83
+ Playwright tests.
84
+
85
+ ```bash
86
+ bun run --cwd plugins/plugin-xr build:all
87
+ # or just the simulator:
88
+ bun run --cwd plugins/plugin-xr simulator:build
89
+ ```
90
+
91
+ After building, `GET /api/xr/simulator.js` serves the emulator bundle.
92
+
93
+ ## Wire protocol
94
+
95
+ The plugin uses a custom binary framing on top of WebSocket:
96
+
97
+ - **Text frames** — JSON control messages (`XRClientControl` / `XRServerControl`).
98
+ - **Binary frames** — 4-byte big-endian header length, UTF-8 JSON header
99
+ (`XRAudioHeader` or `XRFrameHeader`), then raw payload (audio or JPEG/WebP).
100
+
101
+ The wire types (`XRClientControl`, `XRServerControl`, `XRBinaryHeader`,
102
+ `XRTTSAudioHeader`, `XRPanelConfig`, and the rest of `protocol.ts`) are
103
+ re-exported as types from the package root via `export type *`. The framing
104
+ helpers `encodeBinaryFrame` / `decodeBinaryFrame` are runtime functions that
105
+ live in `src/protocol.ts` and are used internally by the WebSocket server;
106
+ they are not part of the package's public runtime exports.
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@elizaos/plugin-xr",
3
+ "version": "2.0.3-beta.5",
4
+ "type": "module",
5
+ "description": "WebXR audio/video streaming for elizaOS — Quest 3 and XReal glasses",
6
+ "main": "./dist/index.js",
7
+ "exports": {
8
+ "./package.json": "./package.json",
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "eliza-source": {
12
+ "types": "./src/index.ts",
13
+ "import": "./src/index.ts",
14
+ "default": "./src/index.ts"
15
+ },
16
+ "import": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
+ }
19
+ },
20
+ "scripts": {
21
+ "typecheck": "tsgo --noEmit -p tsconfig.json",
22
+ "lint": "bunx @biomejs/biome check src",
23
+ "test": "bunx vitest run --config ./vitest.config.ts",
24
+ "build": "bun run build:js && bun run build:types",
25
+ "build:all": "bun run build && bun run simulator:build",
26
+ "clean": "node ../../packages/scripts/rm-path-recursive.mjs dist simulator/dist",
27
+ "build:js": "tsup --config ../tsup.plugin-packages.shared.ts",
28
+ "build:types": "tsc --noCheck -p tsconfig.build.json",
29
+ "simulator:build": "cd simulator && bun install && bun run build",
30
+ "simulator:watch": "cd simulator && bun run build:watch"
31
+ },
32
+ "dependencies": {
33
+ "@elizaos/agent": "2.0.3-beta.5",
34
+ "@elizaos/core": "2.0.3-beta.5",
35
+ "ws": "^8.18.0",
36
+ "zod": "^4.4.3"
37
+ },
38
+ "devDependencies": {
39
+ "@biomejs/biome": "^2.4.14",
40
+ "@types/node": "^25.0.3",
41
+ "@types/ws": "^8.5.10",
42
+ "bun-types": "^1.3.14",
43
+ "tsup": "^8.5.1",
44
+ "typescript": "^6.0.3",
45
+ "vitest": "^4.0.17"
46
+ },
47
+ "elizaos": {
48
+ "plugin": {
49
+ "displayName": "XR Streaming",
50
+ "category": "media"
51
+ }
52
+ },
53
+ "publishConfig": {
54
+ "access": "public"
55
+ },
56
+ "gitHead": "ff6157011c9459670021cc28a6797592a78b8817"
57
+ }