create-spatius-app 0.1.0-beta.1 → 0.1.1
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/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/templates.js +1 -1
- package/dist/templates.js.map +1 -1
- package/package.json +2 -2
- package/templates/cloudflare-livekit/README.md +3 -1
- package/templates/cloudflare-livekit/agent/pyproject.toml +1 -1
- package/templates/cloudflare-livekit/agent/src/agent.py +10 -0
- package/templates/cloudflare-livekit/agent/tests/test_agent_startup.py +21 -0
- package/templates/cloudflare-livekit/agent/uv.lock +14 -14
- package/templates/cloudflare-livekit/npmrc +0 -3
- package/templates/cloudflare-livekit/package.json +2 -2
- package/templates/cloudflare-livekit/scripts/dev-web.mjs +12 -0
- package/templates/cloudflare-livekit/scripts/dev.mjs +29 -0
- package/templates/cloudflare-livekit/scripts/wait-for-agent.mjs +20 -0
- package/templates/cloudflare-livekit/tests/dev-fixtures.mjs +12 -1
- package/templates/cloudflare-livekit/tests/dev-supervision.test.mjs +108 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-spatius-app",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
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.
|
|
37
|
+
"packageManager": "pnpm@11.25.0",
|
|
38
38
|
"publishConfig": {
|
|
39
39
|
"access": "public",
|
|
40
40
|
"provenance": true
|
|
@@ -34,4 +34,6 @@ a matching voice. This configures local development only, not deployment.
|
|
|
34
34
|
__SPATIUS_DEV_COMMAND__
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
This starts the
|
|
37
|
+
This starts the Python agent first, then starts the frontend and Cloudflare
|
|
38
|
+
Worker once the agent has registered with LiveKit. If registration takes more
|
|
39
|
+
than 60 seconds, startup stops with an error.
|
|
@@ -5,7 +5,7 @@ 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.
|
|
8
|
+
"livekit-agents[spatius]~=1.8.0",
|
|
9
9
|
"livekit-plugins-noise-cancellation~=0.2.6",
|
|
10
10
|
"python-dotenv>=1.2.1,<2",
|
|
11
11
|
]
|
|
@@ -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.
|
|
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/
|
|
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/
|
|
792
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
793
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
794
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
795
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
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.
|
|
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/
|
|
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/
|
|
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]
|
|
@@ -935,15 +935,15 @@ wheels = [
|
|
|
935
935
|
|
|
936
936
|
[[package]]
|
|
937
937
|
name = "livekit-plugins-spatius"
|
|
938
|
-
version = "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/
|
|
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/
|
|
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,7 +1891,7 @@ dev = [
|
|
|
1891
1891
|
|
|
1892
1892
|
[package.metadata]
|
|
1893
1893
|
requires-dist = [
|
|
1894
|
-
{ name = "livekit-agents", extras = ["spatius"], specifier = "~=1.
|
|
1894
|
+
{ name = "livekit-agents", extras = ["spatius"], specifier = "~=1.8.0" },
|
|
1895
1895
|
{ name = "livekit-plugins-noise-cancellation", specifier = "~=0.2.6" },
|
|
1896
1896
|
{ name = "python-dotenv", specifier = ">=1.2.1,<2" },
|
|
1897
1897
|
{ name = "ruff", marker = "extra == 'dev'", specifier = ">=0.15,<1" },
|
|
@@ -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": "
|
|
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.
|
|
28
|
+
"packageManager": "pnpm@11.25.0",
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@livekit/components-react": "2.9.24",
|
|
31
31
|
"@spatius/avatarkit": "1.3.8",
|
|
@@ -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
|
+
}
|
|
@@ -25,7 +25,7 @@ export const credentials = {
|
|
|
25
25
|
};
|
|
26
26
|
|
|
27
27
|
const fakeRuntime = `
|
|
28
|
-
import { appendFileSync, existsSync } from 'node:fs';
|
|
28
|
+
import { appendFileSync, existsSync, writeFileSync } from 'node:fs';
|
|
29
29
|
import { spawn } from 'node:child_process';
|
|
30
30
|
const role = process.argv[2];
|
|
31
31
|
const args = process.argv.slice(3);
|
|
@@ -57,6 +57,17 @@ if (role === 'lk' && args[0] === '--version') {
|
|
|
57
57
|
} else if (role === 'grandchild') {
|
|
58
58
|
hold(); ready();
|
|
59
59
|
} else {
|
|
60
|
+
if (role !== 'web' && process.env.SPATIUS_DEV_READY_FILE && process.env.DEV_TEST_RUNTIME_HOLD === '1') {
|
|
61
|
+
const register = () => {
|
|
62
|
+
event('registered');
|
|
63
|
+
writeFileSync(process.env.SPATIUS_DEV_READY_FILE, '');
|
|
64
|
+
};
|
|
65
|
+
if (process.env.DEV_TEST_REGISTER) {
|
|
66
|
+
const poll = setInterval(() => {
|
|
67
|
+
if (existsSync(process.env.DEV_TEST_REGISTER)) { clearInterval(poll); register(); }
|
|
68
|
+
}, 20);
|
|
69
|
+
} else register();
|
|
70
|
+
}
|
|
60
71
|
if (process.env.DEV_TEST_GRANDCHILD === '1') {
|
|
61
72
|
spawnGrandchild();
|
|
62
73
|
}
|
|
@@ -37,7 +37,7 @@ async function supervise(f, extraEnv) {
|
|
|
37
37
|
const sourcePackage = JSON.parse(
|
|
38
38
|
await readFile(join(templateDirectory, 'package.json'), 'utf8'),
|
|
39
39
|
);
|
|
40
|
-
// Exercise the production
|
|
40
|
+
// Exercise the production startup gate and supervisor with npm, available alongside
|
|
41
41
|
// Node in both generated projects and root CI. Only PM selection changes.
|
|
42
42
|
const scripts = Object.fromEntries(
|
|
43
43
|
['dev', 'dev:web', 'agent:dev'].map((name) => [
|
|
@@ -102,7 +102,7 @@ for (const [name, side, code] of [
|
|
|
102
102
|
['agent fails', 'agent', '29'],
|
|
103
103
|
]) {
|
|
104
104
|
test(
|
|
105
|
-
`
|
|
105
|
+
`root dev supervisor: ${name} stops its sibling and all grandchildren`,
|
|
106
106
|
{ timeout: 15000 },
|
|
107
107
|
async (t) => {
|
|
108
108
|
const f = await fixture(t);
|
|
@@ -128,12 +128,14 @@ for (const [name, side, code] of [
|
|
|
128
128
|
assert.match(result.output, /SIGTERM/);
|
|
129
129
|
assertNoCredentials(result.output);
|
|
130
130
|
await assertStopped(events.map(({ pid }) => pid));
|
|
131
|
+
// A bounded CLI probe may select Python fallback on a busy machine.
|
|
132
|
+
// Supervision must launch exactly one runtime through either supported path.
|
|
131
133
|
assert.equal(
|
|
132
134
|
(await readEvents(f.events)).filter(
|
|
133
135
|
({ role, args, type }) =>
|
|
134
|
-
|
|
136
|
+
['lk', 'uv', 'python'].includes(role) &&
|
|
135
137
|
type === 'invoked' &&
|
|
136
|
-
args.
|
|
138
|
+
args.includes('src/agent.py'),
|
|
137
139
|
).length,
|
|
138
140
|
1,
|
|
139
141
|
);
|
|
@@ -142,7 +144,7 @@ for (const [name, side, code] of [
|
|
|
142
144
|
}
|
|
143
145
|
|
|
144
146
|
test(
|
|
145
|
-
'
|
|
147
|
+
'root dev supervisor: Ctrl+C stops both services and their grandchildren',
|
|
146
148
|
{
|
|
147
149
|
timeout: 15000,
|
|
148
150
|
skip:
|
|
@@ -201,3 +203,104 @@ test(
|
|
|
201
203
|
assertNoCredentials(result.output);
|
|
202
204
|
},
|
|
203
205
|
);
|
|
206
|
+
|
|
207
|
+
for (const fallback of [false, true]) {
|
|
208
|
+
test(
|
|
209
|
+
`web waits for registration (${fallback ? 'Python fallback' : 'lk'})`,
|
|
210
|
+
{ timeout: 15000 },
|
|
211
|
+
async (t) => {
|
|
212
|
+
const f = await fixture(t, { uv: true });
|
|
213
|
+
const registration = join(f.root, 'register');
|
|
214
|
+
const run = await supervise(f, {
|
|
215
|
+
DEV_TEST_RUNTIME_HOLD: '1',
|
|
216
|
+
DEV_TEST_REGISTER: registration,
|
|
217
|
+
DEV_TEST_WEB_EXIT: '0',
|
|
218
|
+
...(fallback ? { DEV_TEST_VERSION: 'lk version 2.17.0' } : {}),
|
|
219
|
+
});
|
|
220
|
+
const before = await waitForEvents(f.events, (events) =>
|
|
221
|
+
events.some(
|
|
222
|
+
({ role, type }) =>
|
|
223
|
+
role === (fallback ? 'uv' : 'lk') && type === 'ready',
|
|
224
|
+
),
|
|
225
|
+
);
|
|
226
|
+
await delay(200);
|
|
227
|
+
assert.ok(
|
|
228
|
+
!(await readEvents(f.events)).some(({ role }) => role === 'web'),
|
|
229
|
+
);
|
|
230
|
+
const readyPath = before.find(({ env }) => env.SPATIUS_DEV_READY_FILE)
|
|
231
|
+
?.env.SPATIUS_DEV_READY_FILE;
|
|
232
|
+
assert.ok(readyPath);
|
|
233
|
+
await writeFile(registration, 'register');
|
|
234
|
+
const after = await waitForEvents(f.events, (events) =>
|
|
235
|
+
events.some(({ role, type }) => role === 'web' && type === 'ready'),
|
|
236
|
+
);
|
|
237
|
+
assert.ok(
|
|
238
|
+
after.findIndex(({ type }) => type === 'registered') <
|
|
239
|
+
after.findIndex(({ role }) => role === 'web'),
|
|
240
|
+
);
|
|
241
|
+
await writeFile(f.env.DEV_TEST_RELEASE, 'exit');
|
|
242
|
+
const result = await run.done;
|
|
243
|
+
assert.match(result.output, /LiveKit agent registered/);
|
|
244
|
+
assertNoCredentials(result.output);
|
|
245
|
+
await assert.rejects(readFile(readyPath), { code: 'ENOENT' });
|
|
246
|
+
await assertStopped(after.map(({ pid }) => pid));
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
test(
|
|
252
|
+
'agent failure before registration never starts web',
|
|
253
|
+
{ timeout: 15000 },
|
|
254
|
+
async (t) => {
|
|
255
|
+
const f = await fixture(t);
|
|
256
|
+
const run = await supervise(f, { DEV_TEST_RUNTIME_EXIT: '29' });
|
|
257
|
+
const result = await run.done;
|
|
258
|
+
assert.notEqual(result.code, 0);
|
|
259
|
+
assert.ok(!(await readEvents(f.events)).some(({ role }) => role === 'web'));
|
|
260
|
+
assertNoCredentials(result.output);
|
|
261
|
+
},
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
test(
|
|
265
|
+
'registration timeout stops the agent without starting web',
|
|
266
|
+
{ timeout: 15000 },
|
|
267
|
+
async (t) => {
|
|
268
|
+
const f = await fixture(t);
|
|
269
|
+
// Accelerate only the copied fixture's deadline; exercise the real web gate.
|
|
270
|
+
const path = join(f.root, 'scripts', 'wait-for-agent.mjs');
|
|
271
|
+
await writeFile(
|
|
272
|
+
path,
|
|
273
|
+
(await readFile(path, 'utf8')).replace('60_000', '1500'),
|
|
274
|
+
);
|
|
275
|
+
const run = await supervise(f, {
|
|
276
|
+
DEV_TEST_RUNTIME_HOLD: '1',
|
|
277
|
+
DEV_TEST_REGISTER: join(f.root, 'never-register'),
|
|
278
|
+
});
|
|
279
|
+
const result = await run.done;
|
|
280
|
+
assert.notEqual(result.code, 0);
|
|
281
|
+
assert.match(result.output, /LiveKit agent did not register/);
|
|
282
|
+
const events = await readEvents(f.events);
|
|
283
|
+
assert.ok(!events.some(({ role }) => role === 'web'));
|
|
284
|
+
await assertStopped(events.map(({ pid }) => pid));
|
|
285
|
+
assertNoCredentials(result.output);
|
|
286
|
+
},
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
test(
|
|
290
|
+
'Ctrl+C while waiting for registration stops the agent without starting web',
|
|
291
|
+
{ timeout: 15000, skip: windows },
|
|
292
|
+
async (t) => {
|
|
293
|
+
const f = await fixture(t);
|
|
294
|
+
const run = await supervise(f, {
|
|
295
|
+
DEV_TEST_RUNTIME_HOLD: '1',
|
|
296
|
+
DEV_TEST_REGISTER: join(f.root, 'never-register'),
|
|
297
|
+
});
|
|
298
|
+
const events = await waitForEvents(f.events, (events) =>
|
|
299
|
+
events.some(({ role, type }) => role === 'lk' && type === 'ready'),
|
|
300
|
+
);
|
|
301
|
+
process.kill(-run.child.pid, 'SIGINT');
|
|
302
|
+
await run.done;
|
|
303
|
+
assert.ok(!(await readEvents(f.events)).some(({ role }) => role === 'web'));
|
|
304
|
+
await assertStopped(events.map(({ pid }) => pid));
|
|
305
|
+
},
|
|
306
|
+
);
|