create-spatius-app 0.1.0 → 0.1.2

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-spatius-app",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Create a Spatius app with a web frontend, Cloudflare Worker, and LiveKit agent.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -34,7 +34,7 @@
34
34
  "engines": {
35
35
  "node": ">=22"
36
36
  },
37
- "packageManager": "pnpm@11.1.2",
37
+ "packageManager": "pnpm@12.3.4",
38
38
  "publishConfig": {
39
39
  "access": "public",
40
40
  "provenance": true
@@ -0,0 +1,170 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "title": "create-spatius-app result v3",
4
+ "oneOf": [
5
+ {
6
+ "$ref": "#/$defs/success"
7
+ },
8
+ {
9
+ "$ref": "#/$defs/failure"
10
+ }
11
+ ],
12
+ "$defs": {
13
+ "success": {
14
+ "type": "object",
15
+ "additionalProperties": false,
16
+ "required": [
17
+ "schemaVersion",
18
+ "ok",
19
+ "command",
20
+ "dryRun",
21
+ "generatorVersion",
22
+ "projectDirectory",
23
+ "template",
24
+ "created",
25
+ "wouldCreate",
26
+ "actions",
27
+ "packageManagers",
28
+ "nextSteps",
29
+ "humanSteps"
30
+ ],
31
+ "properties": {
32
+ "schemaVersion": {
33
+ "const": 3
34
+ },
35
+ "ok": {
36
+ "const": true
37
+ },
38
+ "command": {
39
+ "const": "create"
40
+ },
41
+ "dryRun": {
42
+ "type": "boolean"
43
+ },
44
+ "generatorVersion": {
45
+ "type": "string"
46
+ },
47
+ "projectDirectory": {
48
+ "type": "string"
49
+ },
50
+ "template": {
51
+ "const": "default"
52
+ },
53
+ "created": {
54
+ "type": "array",
55
+ "items": {
56
+ "type": "string"
57
+ }
58
+ },
59
+ "wouldCreate": {
60
+ "type": "array",
61
+ "items": {
62
+ "type": "string"
63
+ }
64
+ },
65
+ "actions": {
66
+ "type": "object",
67
+ "additionalProperties": false,
68
+ "required": ["dependenciesInstalled", "gitInitialized", "deployed"],
69
+ "properties": {
70
+ "dependenciesInstalled": {
71
+ "type": "boolean"
72
+ },
73
+ "gitInitialized": {
74
+ "const": false
75
+ },
76
+ "deployed": {
77
+ "const": false
78
+ }
79
+ }
80
+ },
81
+ "packageManagers": {
82
+ "type": "object",
83
+ "additionalProperties": false,
84
+ "required": ["javascript", "python"],
85
+ "properties": {
86
+ "javascript": {
87
+ "enum": ["npm", "pnpm", "bun"]
88
+ },
89
+ "python": {
90
+ "enum": ["pip", "uv"]
91
+ }
92
+ }
93
+ },
94
+ "nextSteps": {
95
+ "type": "array",
96
+ "items": {
97
+ "type": "string"
98
+ }
99
+ },
100
+ "humanSteps": {
101
+ "type": "array",
102
+ "description": "Human prerequisites before running the app. Commands run from projectDirectory. On dry runs these are planned steps after creation, not actions already performed.",
103
+ "items": {
104
+ "type": "object",
105
+ "additionalProperties": false,
106
+ "required": ["command", "reason", "requiresHuman", "requiresTty"],
107
+ "properties": {
108
+ "command": {
109
+ "type": "string"
110
+ },
111
+ "reason": {
112
+ "type": "string"
113
+ },
114
+ "requiresHuman": {
115
+ "const": true
116
+ },
117
+ "requiresTty": {
118
+ "const": true
119
+ }
120
+ }
121
+ }
122
+ }
123
+ }
124
+ },
125
+ "failure": {
126
+ "type": "object",
127
+ "additionalProperties": false,
128
+ "required": ["schemaVersion", "ok", "error"],
129
+ "properties": {
130
+ "schemaVersion": {
131
+ "const": 3
132
+ },
133
+ "ok": {
134
+ "const": false
135
+ },
136
+ "error": {
137
+ "type": "object",
138
+ "additionalProperties": false,
139
+ "required": ["code", "message"],
140
+ "properties": {
141
+ "code": {
142
+ "enum": [
143
+ "CANCELLED",
144
+ "FILESYSTEM_ERROR",
145
+ "INSTALL_FAILED",
146
+ "INVALID_ARGUMENT",
147
+ "PACKAGE_MANAGER_NOT_FOUND",
148
+ "PACKAGE_MANAGER_UNAVAILABLE",
149
+ "TARGET_NOT_DIRECTORY",
150
+ "TARGET_NOT_EMPTY",
151
+ "TEMPLATE_NOT_FOUND",
152
+ "UNSAFE_TARGET",
153
+ "UNKNOWN_ERROR"
154
+ ]
155
+ },
156
+ "message": {
157
+ "type": "string"
158
+ },
159
+ "path": {
160
+ "type": "string"
161
+ },
162
+ "recovery": {
163
+ "type": "string"
164
+ }
165
+ }
166
+ }
167
+ }
168
+ }
169
+ }
170
+ }
@@ -18,6 +18,10 @@ Run from the project root:
18
18
  - Start only the Python agent: `__SPATIUS_AGENT_DEV_COMMAND__`
19
19
  - Check frontend/Worker: `__SPATIUS_CHECK_COMMAND__`
20
20
  - Check Python: `__SPATIUS_AGENT_CHECK_COMMAND__`
21
+ - Check browser flows with mocks: `__SPATIUS_E2E_COMMAND__`
22
+
23
+ All checks can run after dependency installation, before credential setup.
24
+ Browser tests require Chromium; see README.md for the installation command.
21
25
 
22
26
  ## Working guidelines
23
27
 
@@ -28,10 +28,53 @@ npx create-spatius-app setup . --interactive
28
28
  Follow the prompts to connect LiveKit and Spatius, select an avatar, and choose
29
29
  a matching voice. This configures local development only, not deployment.
30
30
 
31
+ #### Manual credential setup
32
+
33
+ Alternatively, copy `.dev.vars.example` to `.dev.vars` and
34
+ `agent/.env.example` to `agent/.env.local`, then edit the copies locally:
35
+
36
+ - Use the same LiveKit Cloud project’s `LIVEKIT_URL`, `LIVEKIT_API_KEY`, and
37
+ `LIVEKIT_API_SECRET` in both files.
38
+ - Set the same `SPATIUS_APP_ID` in both files and set `SPATIUS_API_KEY` in
39
+ `agent/.env.local` to a key for that app.
40
+ - In `.dev.vars`, set `SPATIUS_AVATAR_ID` to your chosen avatar.
41
+ `SPATIUS_AVATAR_BACKGROUND_URL` is optional and can stay empty. Keep the
42
+ example `CARTESIA_VOICE_ID` to use the default voice.
43
+
44
+ Keep the example `LIVEKIT_AGENT_NAME` so the Worker dispatches the bundled
45
+ agent. Both credential files are ignored by Git; keep their values local.
46
+
31
47
  ### 3. Start the app
32
48
 
33
49
  ```sh
34
50
  __SPATIUS_DEV_COMMAND__
35
51
  ```
36
52
 
37
- This starts the frontend, Cloudflare Worker, and Python agent together.
53
+ This starts the Python agent first, then starts the frontend and Cloudflare
54
+ Worker once the agent has registered with LiveKit. If registration takes more
55
+ than 60 seconds, startup stops with an error.
56
+
57
+ ## Validate without credentials
58
+
59
+ After installing both dependency sets, you can run these checks before account
60
+ setup. Run them from the project root:
61
+
62
+ ```sh
63
+ __SPATIUS_CHECK_COMMAND__
64
+ __SPATIUS_AGENT_CHECK_COMMAND__
65
+ ```
66
+
67
+ These check formatting, lint, types, unit tests, development-process behavior,
68
+ and the production build. They do not require LiveKit or Spatius credentials.
69
+
70
+ Browser tests use mocked services and also run without credentials:
71
+
72
+ ```sh
73
+ npx playwright install chromium
74
+ __SPATIUS_E2E_COMMAND__
75
+ ```
76
+
77
+ The first command downloads Chromium if needed. On Linux, use
78
+ `npx playwright install --with-deps chromium` to also install required system
79
+ libraries. These tests verify the local UI flow; a real voice-avatar
80
+ conversation still requires account setup and starting the app.
@@ -22,10 +22,6 @@ LiveKit room as the browser.
22
22
  enhanced noise-cancellation model in the browser; standard browser echo
23
23
  cancellation can stay enabled.
24
24
 
25
- The noise-cancellation dependency stays on the BVC-compatible `0.2.x` series.
26
- LiveKit's newer voice-isolation recommendation is Krisp VIVA; switching to that
27
- is a separate choice, not an implicit dependency upgrade.
28
-
29
25
  See [Cartesia TTS](https://docs.livekit.io/agents/models/tts/cartesia/),
30
26
  [expressive mode](https://docs.livekit.io/agents/models/tts/expressive/), and
31
27
  [noise cancellation](https://docs.livekit.io/transport/media/noise-cancellation/).
@@ -5,8 +5,8 @@ description = "A LiveKit voice agent with a Spatius avatar"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11,<3.15"
7
7
  dependencies = [
8
- "livekit-agents[spatius]~=1.7.1",
9
- "livekit-plugins-noise-cancellation~=0.2.6",
8
+ "livekit-agents[spatius]~=1.8.0",
9
+ "livekit-plugins-noise-cancellation~=0.3.0",
10
10
  "python-dotenv>=1.2.1,<2",
11
11
  ]
12
12
 
@@ -1,5 +1,7 @@
1
1
  import logging
2
+ import os
2
3
  import textwrap
4
+ from pathlib import Path
3
5
 
4
6
  from dotenv import load_dotenv
5
7
  from livekit import rtc
@@ -43,6 +45,14 @@ class Assistant(Agent):
43
45
  server = AgentServer()
44
46
 
45
47
 
48
+ @server.on("worker_registered")
49
+ def report_dev_ready(_worker_id: str, _server_info: object) -> None:
50
+ # Only the root dev supervisor supplies this per-run path. Registration,
51
+ # not process launch or the local health endpoint, releases web startup.
52
+ if ready_file := os.environ.get("SPATIUS_DEV_READY_FILE"):
53
+ Path(ready_file).touch()
54
+
55
+
46
56
  @server.rtc_session(agent_name="spatius-agent")
47
57
  async def spatius_agent(ctx: JobContext) -> None:
48
58
  ctx.log_context_fields = {"room": ctx.room.name}
@@ -207,5 +207,26 @@ class AgentStartupTests(unittest.IsolatedAsyncioTestCase):
207
207
  )
208
208
 
209
209
 
210
+ class DevReadinessTests(unittest.TestCase):
211
+ def test_registration_writes_only_the_supervisors_ready_file(self) -> None:
212
+ with (
213
+ patch.dict(
214
+ "os.environ", {"SPATIUS_DEV_READY_FILE": "/tmp/test-agent-ready"}
215
+ ),
216
+ patch.object(agent_module, "Path") as path,
217
+ ):
218
+ agent_module.server.emit("worker_registered", "worker-test", object())
219
+ path.assert_called_once_with("/tmp/test-agent-ready")
220
+ path.return_value.touch.assert_called_once_with()
221
+
222
+ def test_standalone_agent_does_not_write_a_ready_file(self) -> None:
223
+ with (
224
+ patch.dict("os.environ", {}, clear=True),
225
+ patch.object(agent_module, "Path") as path,
226
+ ):
227
+ agent_module.server.emit("worker_registered", "worker-test", object())
228
+ path.assert_not_called()
229
+
230
+
210
231
  if __name__ == "__main__":
211
232
  unittest.main()
@@ -777,7 +777,7 @@ wheels = [
777
777
 
778
778
  [[package]]
779
779
  name = "livekit"
780
- version = "1.1.15"
780
+ version = "1.1.17"
781
781
  source = { registry = "https://pypi.org/simple" }
782
782
  dependencies = [
783
783
  { name = "aiofiles" },
@@ -786,18 +786,18 @@ dependencies = [
786
786
  { name = "protobuf" },
787
787
  { name = "types-protobuf" },
788
788
  ]
789
- sdist = { url = "https://files.pythonhosted.org/packages/ae/0d/383cb1ff9dd0f5de8e8046f0a83aa37e7d137f47552973395aa17871163e/livekit-1.1.15.tar.gz", hash = "sha256:8fe6d06127cecbe8d56c83b0c09c4604ea33cbab826508785bfd8dea5d5e1a4a", size = 378304, upload-time = "2026-08-24T17:19:00.999Z" }
789
+ sdist = { url = "https://files.pythonhosted.org/packages/a6/bd/e20f97969cf21959061cdc6e1c89e3ad88e23693030398df283c236430fc/livekit-1.1.17.tar.gz", hash = "sha256:0553bfc9041dc8430029597a2278b43e4bb75594a72cb9dcc42bf3ee8e1689a0", size = 378467, upload-time = "2026-09-01T05:03:19.976Z" }
790
790
  wheels = [
791
- { url = "https://files.pythonhosted.org/packages/a6/8c/5cd2de868eea3065821a8ecb615b2676f7076db74cea437dd9ee83d98c22/livekit-1.1.15-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:f7e8485bb91d6f74c893ffc8545301e049d5c056f863e1ce72b857ed040db218", size = 10199958, upload-time = "2026-08-24T17:18:50.276Z" },
792
- { url = "https://files.pythonhosted.org/packages/48/e0/2027d03398c0b1d57345a040356109b3e49491e679605ae4f42f3ebc1bb9/livekit-1.1.15-py3-none-macosx_11_0_arm64.whl", hash = "sha256:285f3ef28e8ce78d8a9ae909571a67511e55ccaa81e7769822e024036d307f56", size = 9023382, upload-time = "2026-08-24T17:18:52.828Z" },
793
- { url = "https://files.pythonhosted.org/packages/a9/a5/72b4bedfc09df365d5e016979815b4791af1ca08781dd99e087626f3d550/livekit-1.1.15-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:4cb3894030cce1940ef5a6407d6e9d3049eb7150e204bf327fbc37eb903af4d5", size = 10046135, upload-time = "2026-08-24T17:18:54.602Z" },
794
- { url = "https://files.pythonhosted.org/packages/72/a4/563813a9282c827363e9add8f24785e65514f04c9d8e04d7ac3ad7a3b5b0/livekit-1.1.15-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:d0c72f36906f9d46b4c69d28cb39ecd847022e990df77037cbdd4ebf4d8db50c", size = 11449550, upload-time = "2026-08-24T17:18:56.744Z" },
795
- { url = "https://files.pythonhosted.org/packages/7c/14/88d9d90144b426e75d9ba6111d623063fae4ac714d36bd61349e8684bd3f/livekit-1.1.15-py3-none-win_amd64.whl", hash = "sha256:4b6027b986ec0abdc57fed0796b6ebb4d03011ada446a77efe158d9c2ed0bb5f", size = 10770308, upload-time = "2026-08-24T17:18:58.875Z" },
791
+ { url = "https://files.pythonhosted.org/packages/fd/74/d93c5dd19ff2139c070eacb634e075cd2aff239a9cf692002afaa529a630/livekit-1.1.17-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:39188ac6c805643c8ef952cccb96b2e81e430600e626dea9c6aa1faca7013fdb", size = 10468167, upload-time = "2026-09-01T05:03:06.001Z" },
792
+ { url = "https://files.pythonhosted.org/packages/d6/3a/9f4f5e3f0c325d0d5d19c5dc1ae345f4bf1353369db3b42e265a1ee4dbc5/livekit-1.1.17-py3-none-macosx_11_0_arm64.whl", hash = "sha256:078d249974aed5e845b60f840802e36dc9d2c9c1dd606341fb95ee018749169e", size = 9289118, upload-time = "2026-09-01T05:03:09.011Z" },
793
+ { url = "https://files.pythonhosted.org/packages/1a/d5/89293b55b7a719c7f7b7929d995c009bee7ce9f4d29876c77ee878fd9bf9/livekit-1.1.17-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:984c7c0efa3e1f917e973ce412a141ff8244baf98ff24587aa82c206b621054f", size = 10580945, upload-time = "2026-09-01T05:03:11.743Z" },
794
+ { url = "https://files.pythonhosted.org/packages/c9/4e/aad025688f8aceeecb4ec97aaae3541d64302e03454c84dc57e02bbaf5b9/livekit-1.1.17-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:100f2ddd8e433ea906ecf45f1d37063e43e72c7c2cff0b65ddddc11181bc8dd7", size = 11805780, upload-time = "2026-09-01T05:03:14.614Z" },
795
+ { url = "https://files.pythonhosted.org/packages/97/7a/5447238155494f2a2fd55ff0f7705b9769fe67e787b52150a5d269eeaa61/livekit-1.1.17-py3-none-win_amd64.whl", hash = "sha256:7a7b1bdb646775d8042ec97baec6c6b615c5828d6f9b6dd2adaa167841d6f6c2", size = 10968316, upload-time = "2026-09-01T05:03:17.671Z" },
796
796
  ]
797
797
 
798
798
  [[package]]
799
799
  name = "livekit-agents"
800
- version = "1.7.1"
800
+ version = "1.8.0"
801
801
  source = { registry = "https://pypi.org/simple" }
802
802
  dependencies = [
803
803
  { name = "aiofiles" },
@@ -833,9 +833,9 @@ dependencies = [
833
833
  { name = "typing-extensions" },
834
834
  { name = "watchfiles" },
835
835
  ]
836
- sdist = { url = "https://files.pythonhosted.org/packages/6d/ed/1d6909828103892de4ad94261a682d11f739a91ce0dd656432fab626d20e/livekit_agents-1.7.1.tar.gz", hash = "sha256:89bb7dc3d2b8b28671c846a80cec9265b699ddee805d790ef2d00a5e3129b8dd", size = 2662477, upload-time = "2026-08-27T03:33:39.986Z" }
836
+ sdist = { url = "https://files.pythonhosted.org/packages/99/88/ace06ebd95b8cc45e25430a2a10ec7ccfb6b386b67907192ad421493858f/livekit_agents-1.8.0.tar.gz", hash = "sha256:132029af152a585770b58623a1a2ac7c8b1b9241b045e8cd220b30415fbcf2ad", size = 2688509, upload-time = "2026-09-05T00:10:03.294Z" }
837
837
  wheels = [
838
- { url = "https://files.pythonhosted.org/packages/20/33/ec05c835192728f55aa571675912d81c01b423f8f24a98273f148c904e63/livekit_agents-1.7.1-py3-none-any.whl", hash = "sha256:f64ce088f22f115faa3b92448d335569b101d9d96fe6481781fd82f06c413b2e", size = 2777164, upload-time = "2026-08-27T03:33:37.048Z" },
838
+ { url = "https://files.pythonhosted.org/packages/f4/79/a1dce13ad49ab5b3248727b808ef265b2a535928c53161440d049a0b747d/livekit_agents-1.8.0-py3-none-any.whl", hash = "sha256:0da6c79c71eb7620af14d5c18adba1da796d6743338630813c341e9ce72f45c8", size = 2806075, upload-time = "2026-09-05T00:10:01.23Z" },
839
839
  ]
840
840
 
841
841
  [package.optional-dependencies]
@@ -920,30 +920,30 @@ wheels = [
920
920
 
921
921
  [[package]]
922
922
  name = "livekit-plugins-noise-cancellation"
923
- version = "0.2.6"
923
+ version = "0.3.0"
924
924
  source = { registry = "https://pypi.org/simple" }
925
925
  dependencies = [
926
926
  { name = "livekit" },
927
927
  ]
928
928
  wheels = [
929
- { url = "https://files.pythonhosted.org/packages/19/90/fdb859b5745feb0b130b89b2d6bf0433fb70346d87e0307518cfc9c57c11/livekit_plugins_noise_cancellation-0.2.6-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:6a607767abfc266a5fa9d90fc549371fc2f91401f761b4ca89227e04ce4fa8b5", size = 66080343, upload-time = "2026-06-26T11:14:11.815Z" },
930
- { url = "https://files.pythonhosted.org/packages/63/a7/36b7800a9dc7ef167b3e47cc8b53620cfe8fd3d6d2d374ae11ebbbd39ffc/livekit_plugins_noise_cancellation-0.2.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6c50b8ace36e5a75ea096af38c9a799b325676db7b9a5f0d67a228f222e0150a", size = 64620411, upload-time = "2026-06-26T11:14:15.308Z" },
931
- { url = "https://files.pythonhosted.org/packages/72/5c/8f4623bbc665a0552a1ee6089dc346f5bf0b929b49afb174cd1085411ac5/livekit_plugins_noise_cancellation-0.2.6-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:ae3598ac886b239faeed7bbe2ca1b20e6066d5744da80618d0a9d7fc7fa70fba", size = 70236105, upload-time = "2026-06-26T11:14:18.293Z" },
932
- { url = "https://files.pythonhosted.org/packages/ba/bd/ea16d2492cd159e4685ca4160864e7d55bfaf8d199dbd4b5433cb366d0d0/livekit_plugins_noise_cancellation-0.2.6-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:a01ebe5a610ef693a5b5b24e077c1d677bab365ac9316c1339850ad0960c87ec", size = 73017542, upload-time = "2026-06-26T11:14:22.325Z" },
933
- { url = "https://files.pythonhosted.org/packages/16/a4/2ec98ead17ae6cb9e488a11baf9c91b4c4767af16611eacf36e084122364/livekit_plugins_noise_cancellation-0.2.6-py3-none-win_amd64.whl", hash = "sha256:d8b7a0876124cdd861dec657c7d698b3a7cab09ccb11c3af0fc536b0b43c42b2", size = 65771262, upload-time = "2026-06-26T11:14:25.638Z" },
929
+ { url = "https://files.pythonhosted.org/packages/d8/ad/e049eeca9c5175cc70c036c6dc5ce048215949b5d7ea83dbea6e2e06260c/livekit_plugins_noise_cancellation-0.3.0-py3-none-macosx_10_9_x86_64.whl", hash = "sha256:6c877ca3f31278ce3b6ba7a3adc0eaaf813a53ba5a900445616be69ce7e4b3a8", size = 66079924, upload-time = "2026-07-17T10:35:02.671Z" },
930
+ { url = "https://files.pythonhosted.org/packages/07/61/7a860386cf0c9d27f4d3fdcfeee411153e1618552d6a15ad46d32df64ac5/livekit_plugins_noise_cancellation-0.3.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1d100f249d25556696d8fe358b7c22d81017df67f256ee3b5c9c36830409621f", size = 64625049, upload-time = "2026-07-17T10:35:06.186Z" },
931
+ { url = "https://files.pythonhosted.org/packages/2d/0d/df886e71b463911a9190cea948100f89c1e0546cff7e84dd93862b73e2d9/livekit_plugins_noise_cancellation-0.3.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:4b48de9ffdb8af47820ecd5e44ef0dbb910dfd05f17f6fadcbfd64deceb7a09a", size = 70250046, upload-time = "2026-07-17T10:35:09.376Z" },
932
+ { url = "https://files.pythonhosted.org/packages/d1/43/701df4746e4e8b660f1399ec0a3da80de211f1624725a7496d28f7443b4a/livekit_plugins_noise_cancellation-0.3.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:1703099d634d26171e2f0402c81fc84f13bf0bc92a7f9ba16f9af1d0eefee980", size = 73004876, upload-time = "2026-07-17T10:35:12.775Z" },
933
+ { url = "https://files.pythonhosted.org/packages/6f/b4/3ed7bac7aabec196c1e8969b407b71f69bdb0c942b1b3cd2aed07ae276cf/livekit_plugins_noise_cancellation-0.3.0-py3-none-win_amd64.whl", hash = "sha256:7f6001d0dd3b00c7748949b7a8349de002118fc03faa2d38411ad0d05180ccab", size = 65788455, upload-time = "2026-07-17T10:35:15.759Z" },
934
934
  ]
935
935
 
936
936
  [[package]]
937
937
  name = "livekit-plugins-spatius"
938
- version = "1.7.1"
938
+ version = "1.8.0"
939
939
  source = { registry = "https://pypi.org/simple" }
940
940
  dependencies = [
941
941
  { name = "livekit-agents" },
942
942
  { name = "spatius", extra = ["opus"] },
943
943
  ]
944
- sdist = { url = "https://files.pythonhosted.org/packages/35/9c/99d24076ffc28065a582ae87953d220967c7a816da69257152b93c2fb05b/livekit_plugins_spatius-1.7.1.tar.gz", hash = "sha256:c6239cf47ff87729ae22e1e3b2db8a1d32b16d26b711d530dc09cd0fc525bc25", size = 8601, upload-time = "2026-08-27T03:35:40.562Z" }
944
+ sdist = { url = "https://files.pythonhosted.org/packages/c0/40/ce00a90cdfd3fd0840ef452e8706e88ae5da9ab2c2cc900a615bfae7abcf/livekit_plugins_spatius-1.8.0.tar.gz", hash = "sha256:76d80c79db4673da4c1ce84f51fbec986a0bd7b888226d8fced68992c7d798e8", size = 8601, upload-time = "2026-09-05T00:12:14.914Z" }
945
945
  wheels = [
946
- { url = "https://files.pythonhosted.org/packages/17/94/5951e01cbd34d73c22e75c8c52471c01be996957ba434288afc851b1421e/livekit_plugins_spatius-1.7.1-py3-none-any.whl", hash = "sha256:59a0e4ca9f80fb81683e3d8fa4e05a212276ad79d478a0a0cb73c57f6655235e", size = 8712, upload-time = "2026-08-27T03:35:39.411Z" },
946
+ { url = "https://files.pythonhosted.org/packages/4d/34/bd44feacb043d16e18396bb7bb9ccb8f100348efce5090d853f8a6bfff1d/livekit_plugins_spatius-1.8.0-py3-none-any.whl", hash = "sha256:c852c7c1ec26b094cc207fa127de1f65e305c6def43d626ee65a250339a1bb69", size = 8712, upload-time = "2026-09-05T00:12:13.838Z" },
947
947
  ]
948
948
 
949
949
  [[package]]
@@ -1891,8 +1891,8 @@ dev = [
1891
1891
 
1892
1892
  [package.metadata]
1893
1893
  requires-dist = [
1894
- { name = "livekit-agents", extras = ["spatius"], specifier = "~=1.7.1" },
1895
- { name = "livekit-plugins-noise-cancellation", specifier = "~=0.2.6" },
1894
+ { name = "livekit-agents", extras = ["spatius"], specifier = "~=1.8.0" },
1895
+ { name = "livekit-plugins-noise-cancellation", specifier = "~=0.3.0" },
1896
1896
  { name = "python-dotenv", specifier = ">=1.2.1,<2" },
1897
1897
  { name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15,<1" },
1898
1898
  ]
@@ -1,4 +1 @@
1
- auto-install-peers=false
2
- prefer-frozen-lockfile=true
3
1
  save-exact=true
4
- strict-peer-dependencies=true
@@ -10,7 +10,7 @@
10
10
  "cf-typegen": "wrangler types --env-interface CloudflareBindings",
11
11
  "check": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm test && pnpm test:dev && pnpm build",
12
12
  "deploy": "pnpm build && wrangler deploy",
13
- "dev": "concurrently --kill-others --names web,agent \"pnpm run dev:web\" \"pnpm run agent:dev\"",
13
+ "dev": "node scripts/dev.mjs \"pnpm run dev:web\" \"pnpm run agent:dev\"",
14
14
  "dev:web": "node scripts/dev-web.mjs",
15
15
  "format": "prettier --write .",
16
16
  "format:check": "prettier --check .",
@@ -25,7 +25,7 @@
25
25
  "engines": {
26
26
  "node": ">=22.12"
27
27
  },
28
- "packageManager": "pnpm@11.1.2",
28
+ "packageManager": "pnpm@12.3.4",
29
29
  "dependencies": {
30
30
  "@livekit/components-react": "2.9.24",
31
31
  "@spatius/avatarkit": "1.3.8",
@@ -1,3 +1,104 @@
1
+ ---
2
+ lockfileVersion: '9.0'
3
+
4
+ importers:
5
+
6
+ .:
7
+ configDependencies: {}
8
+ packageManagerDependencies:
9
+ pnpm:
10
+ specifier: 12.3.4
11
+ version: 12.3.4
12
+
13
+ packages:
14
+
15
+ '@pnpm/exe.darwin-arm64@12.3.4':
16
+ resolution: {integrity: sha512-PAyUol8T1+/+ViOiXAt51ECA+QnfXCqz6foL4bW+LsoX0NcVd5XVEM2mRQu+LV4oc7uRz9zf9U0P+XFfuQeDAw==}
17
+ cpu: [arm64]
18
+ os: [darwin]
19
+
20
+ '@pnpm/exe.darwin-x64@12.3.4':
21
+ resolution: {integrity: sha512-fxP9JCk0Cdye+ePuj+GJJLMUMTqHGWRdb1dtv4How876uQ2ehxvenpgiYAir/ceO9PsYUZkFTtyZdx+rRu5QOA==}
22
+ cpu: [x64]
23
+ os: [darwin]
24
+
25
+ '@pnpm/exe.linux-arm64-musl@12.3.4':
26
+ resolution: {integrity: sha512-FBOt0/7ye6O6q4AllVV5QMviB6qE6fqkeczV/+MDWQsmo+QJrlfsh6X7CpH/tClVpBZEyIbjpUoT8bNhCYBxEg==}
27
+ cpu: [arm64]
28
+ os: [linux]
29
+ libc: [musl]
30
+
31
+ '@pnpm/exe.linux-arm64@12.3.4':
32
+ resolution: {integrity: sha512-t71AVA7LRqiKTyZ5xMYaZc2n5DfdpMbfokZuiIOXHBOM03ECnF0t4iYwaBDqJgVjlKYUOwaF/bRQajGNA4cJ4w==}
33
+ cpu: [arm64]
34
+ os: [linux]
35
+ libc: [glibc]
36
+
37
+ '@pnpm/exe.linux-x64-musl@12.3.4':
38
+ resolution: {integrity: sha512-RPmk7Jb/aYaFvL2iyDN/AtMY+hUEsue732WmXpcuQ9tBpMnGyA5py7Z3+e+qmQaJ0zY/4ni9jJiyPBQHujmv6w==}
39
+ cpu: [x64]
40
+ os: [linux]
41
+ libc: [musl]
42
+
43
+ '@pnpm/exe.linux-x64@12.3.4':
44
+ resolution: {integrity: sha512-2ZqOlSPkfwX1h5cR+FPiWf8+F+2hZT/3TvhUK5sigHqwaQCIiq8R7CGxhndKs63JtcLi2a1Qpo+wX/EoyfjyJQ==}
45
+ cpu: [x64]
46
+ os: [linux]
47
+ libc: [glibc]
48
+
49
+ '@pnpm/exe.win32-arm64@12.3.4':
50
+ resolution: {integrity: sha512-ANyrHqyqco6SXBysUTRF74itDyyraea7IbFsKFdNXTjcFnfycTDx37EwuhdpPYFNSIh2JhUG4fByclsRfiHX7w==}
51
+ cpu: [arm64]
52
+ os: [win32]
53
+
54
+ '@pnpm/exe.win32-x64@12.3.4':
55
+ resolution: {integrity: sha512-WH/KqBPY/hq2Tb7SgQltEZytimcjgKRaCRL/aM9CI0c67iKc5TVmHUhIiL3Ux9FB4bWn36i6XewUcScQI+zG8w==}
56
+ cpu: [x64]
57
+ os: [win32]
58
+
59
+ pnpm@12.3.4:
60
+ resolution: {integrity: sha512-lhqkH7B32joEpEHZ+OFevAyW2o73ELLrZ7+e58sGEOq9SPH9hfUc/+c4RnhfoPh8VqOocqHYk/hEZ0G1zORUVw==}
61
+ engines: {node: '>=18.*'}
62
+ hasBin: true
63
+
64
+ snapshots:
65
+
66
+ '@pnpm/exe.darwin-arm64@12.3.4':
67
+ optional: true
68
+
69
+ '@pnpm/exe.darwin-x64@12.3.4':
70
+ optional: true
71
+
72
+ '@pnpm/exe.linux-arm64-musl@12.3.4':
73
+ optional: true
74
+
75
+ '@pnpm/exe.linux-arm64@12.3.4':
76
+ optional: true
77
+
78
+ '@pnpm/exe.linux-x64-musl@12.3.4':
79
+ optional: true
80
+
81
+ '@pnpm/exe.linux-x64@12.3.4':
82
+ optional: true
83
+
84
+ '@pnpm/exe.win32-arm64@12.3.4':
85
+ optional: true
86
+
87
+ '@pnpm/exe.win32-x64@12.3.4':
88
+ optional: true
89
+
90
+ pnpm@12.3.4:
91
+ optionalDependencies:
92
+ '@pnpm/exe.darwin-arm64': 12.3.4
93
+ '@pnpm/exe.darwin-x64': 12.3.4
94
+ '@pnpm/exe.linux-arm64': 12.3.4
95
+ '@pnpm/exe.linux-arm64-musl': 12.3.4
96
+ '@pnpm/exe.linux-x64': 12.3.4
97
+ '@pnpm/exe.linux-x64-musl': 12.3.4
98
+ '@pnpm/exe.win32-arm64': 12.3.4
99
+ '@pnpm/exe.win32-x64': 12.3.4
100
+
101
+ ---
1
102
  lockfileVersion: '9.0'
2
103
 
3
104
  settings:
@@ -1,7 +1,19 @@
1
1
  import { join } from 'node:path';
2
2
  import { projectDirectory } from './agent-runtime.mjs';
3
+ import { waitForAgent } from './wait-for-agent.mjs';
3
4
  import { runProcess } from './managed-process.mjs';
4
5
 
6
+ try {
7
+ if (process.env.SPATIUS_DEV_READY_FILE) {
8
+ console.log('Waiting for the LiveKit agent to register…');
9
+ await waitForAgent(process.env.SPATIUS_DEV_READY_FILE);
10
+ console.log('LiveKit agent registered. Starting frontend and Worker…');
11
+ }
12
+ } catch (error) {
13
+ console.error(error.message);
14
+ process.exit(1);
15
+ }
16
+
5
17
  const result = await runProcess(
6
18
  process.execPath,
7
19
  [
@@ -0,0 +1,29 @@
1
+ import { mkdtemp, rm } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { projectDirectory } from './agent-runtime.mjs';
5
+ import { runProcess } from './managed-process.mjs';
6
+
7
+ // A fresh directory prevents a previous run or another project from releasing
8
+ // this run's startup gate. Keep it outside the agent's auto-reload watch tree.
9
+ const directory = await mkdtemp(join(tmpdir(), 'spatius-dev-'));
10
+ try {
11
+ const result = await runProcess(
12
+ 'concurrently',
13
+ ['--kill-others', '--names', 'web,agent', ...process.argv.slice(2)],
14
+ {
15
+ cwd: projectDirectory,
16
+ env: {
17
+ ...process.env,
18
+ SPATIUS_DEV_READY_FILE: join(directory, 'agent-ready'),
19
+ },
20
+ },
21
+ );
22
+ if (result.failed)
23
+ console.error(
24
+ 'Could not start development services. Install JavaScript dependencies and try again.',
25
+ );
26
+ process.exitCode = result.code;
27
+ } finally {
28
+ await rm(directory, { recursive: true, force: true });
29
+ }
@@ -0,0 +1,20 @@
1
+ import { access } from 'node:fs/promises';
2
+ import { performance } from 'node:perf_hooks';
3
+ import { setTimeout as delay } from 'node:timers/promises';
4
+
5
+ export async function waitForAgent(path, timeout = 60_000) {
6
+ const deadline = performance.now() + timeout;
7
+ while (true) {
8
+ try {
9
+ await access(path);
10
+ return;
11
+ } catch (error) {
12
+ if (error.code !== 'ENOENT') throw error;
13
+ }
14
+ if (performance.now() >= deadline)
15
+ throw new Error(
16
+ 'LiveKit agent did not register within 60 seconds. Check the agent logs, credentials, and network connection, then run dev again.',
17
+ );
18
+ await delay(Math.min(100, Math.max(0, deadline - performance.now())));
19
+ }
20
+ }