create-spatius-app 0.1.3-beta.1 → 0.1.3-beta.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/README.md +11 -10
- package/dist/cli.js +87 -162
- package/dist/templates.js +71 -154
- package/package.json +2 -2
- package/templates/cloudflare-livekit/AGENTS.md +2 -0
- package/templates/cloudflare-livekit/agent/README.md +13 -0
- package/templates/cloudflare-livekit/agent/pyproject.toml +1 -1
- package/templates/cloudflare-livekit/agent/src/agent.py +2 -1
- package/templates/cloudflare-livekit/agent/tests/test_agent_startup.py +49 -0
- package/templates/cloudflare-livekit/agent/uv.lock +14 -14
- package/templates/cloudflare-livekit/gitignore +1 -0
- package/templates/cloudflare-livekit/package-lock.json +177 -16
- package/templates/cloudflare-livekit/package.json +8 -7
- package/templates/cloudflare-livekit/pnpm-lock.yaml +104 -24
- package/templates/cloudflare-livekit/tests/dev-fixtures.mjs +7 -2
- package/templates/cloudflare-livekit/tests/dev-supervision.test.mjs +17 -14
- package/templates/layers/agora/AGENTS.md +9 -4
- package/templates/layers/agora/README.md +14 -8
- package/templates/layers/agora/package-lock.json +1344 -424
- package/templates/layers/agora/package.json +17 -11
- package/templates/layers/agora/pnpm-lock.yaml +1069 -237
- package/templates/layers/agora/scripts/check-worker.mjs +98 -0
- package/templates/layers/agora/tsconfig.worker.json +23 -0
- package/templates/layers/agora/worker/agora.test.ts +2 -0
- package/templates/layers/agora/worker/agora.ts +1 -10
- package/templates/layers/agora/worker/index.test.ts +66 -0
- package/templates/layers/agora/worker/index.ts +4 -0
- package/templates/layers/agora/wrangler.jsonc +24 -0
- package/templates/cloudflare-livekit/worker-configuration.d.ts +0 -17184
- package/templates/layers/agora/tsconfig.json +0 -16
- package/templates/layers/node/Dockerfile +0 -12
- package/templates/layers/node/dockerignore +0 -10
- package/templates/layers/node/package-lock.json +0 -5569
- package/templates/layers/node/pnpm-lock.yaml +0 -3470
- package/templates/layers/node/railway.json +0 -8
- package/templates/layers/node/scripts/dev-service.mjs +0 -14
- package/templates/layers/node/scripts/dev-web.mjs +0 -22
- package/templates/layers/node/server/index.ts +0 -92
- package/templates/layers/node/vite.config.ts +0 -17
- package/templates/layers/railway-agent/agent/railway.json +0 -5
- /package/templates/layers/agora/{env.example → dev.vars.example} +0 -0
package/README.md
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
# create-spatius-app
|
|
2
2
|
|
|
3
|
-
Create a voice-avatar app with Spatius and React. Choose LiveKit
|
|
3
|
+
Create a voice-avatar app with Spatius and React. Choose LiveKit or Agora Conversational AI. Both templates run the frontend and API on Cloudflare Workers.
|
|
4
4
|
|
|
5
5
|
## Quick start
|
|
6
6
|
|
|
7
7
|
Requires Node.js 22.12+ and a [Spatius Studio](https://app.spatius.ai) account.
|
|
8
|
-
LiveKit
|
|
9
|
-
The Agora
|
|
8
|
+
The LiveKit template also needs Python 3.11–3.14 and a LiveKit Cloud project.
|
|
9
|
+
The Agora template needs an Agora account and no Python tooling.
|
|
10
10
|
|
|
11
11
|
```sh
|
|
12
12
|
npx create-spatius-app my-app
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
Choose
|
|
15
|
+
Choose LiveKit or Agora Conversational AI. Follow the remaining prompts to choose package managers, install dependencies, and
|
|
16
16
|
configure your provider and Spatius credentials.
|
|
17
17
|
If LiveKit CLI is missing, setup offers to install it using Homebrew on macOS,
|
|
18
18
|
WinGet on Windows, or LiveKit's Linux installer. You can also enter credentials
|
|
@@ -26,7 +26,7 @@ pnpm dev
|
|
|
26
26
|
# Or: npm run dev / bun run dev
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
This starts the frontend, API, and, for LiveKit
|
|
29
|
+
This starts the frontend, API, and, for LiveKit, the Python agent together. Generated DEPLOYMENT.md covers deployment to Cloudflare Workers and, for LiveKit, LiveKit Cloud.
|
|
30
30
|
Open the URL printed in your terminal and start a conversation.
|
|
31
31
|
|
|
32
32
|
## Use with a coding agent
|
|
@@ -47,15 +47,16 @@ npx create-spatius-app setup . --interactive
|
|
|
47
47
|
|
|
48
48
|
This configures local development only; it does not deploy your app.
|
|
49
49
|
|
|
50
|
-
## Choose a
|
|
50
|
+
## Choose a provider
|
|
51
51
|
|
|
52
52
|
```sh
|
|
53
|
-
npx create-spatius-app my-app --stack
|
|
53
|
+
npx create-spatius-app my-app --stack cloudflare-livekit
|
|
54
|
+
# Or:
|
|
55
|
+
npx create-spatius-app my-app --stack cloudflare-agora
|
|
54
56
|
```
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
The `zeabur-agora` stack requires no Python tooling.
|
|
58
|
+
LiveKit is the default and deploys its Python agent to LiveKit Cloud.
|
|
59
|
+
Agora Conversational AI hosts the agent itself; its template needs no Python.
|
|
59
60
|
|
|
60
61
|
## More options
|
|
61
62
|
|
package/dist/cli.js
CHANGED
|
@@ -107,7 +107,11 @@ async function selectCatalog(options) {
|
|
|
107
107
|
label: stack.label
|
|
108
108
|
}));
|
|
109
109
|
return validateSelection(
|
|
110
|
-
options.interactive ? await options.prompts.choose(
|
|
110
|
+
options.interactive ? await options.prompts.choose(
|
|
111
|
+
"Which voice AI provider?",
|
|
112
|
+
choices,
|
|
113
|
+
DEFAULT_STACK
|
|
114
|
+
) : DEFAULT_STACK
|
|
111
115
|
);
|
|
112
116
|
}
|
|
113
117
|
var stacks, DEFAULT_STACK;
|
|
@@ -117,32 +121,14 @@ var init_catalog = __esm({
|
|
|
117
121
|
init_errors();
|
|
118
122
|
stacks = {
|
|
119
123
|
"cloudflare-livekit": {
|
|
120
|
-
label: "
|
|
124
|
+
label: "LiveKit",
|
|
121
125
|
web: "cloudflare",
|
|
122
126
|
agent: "livekit",
|
|
123
127
|
provider: "livekit"
|
|
124
128
|
},
|
|
125
|
-
"cloudflare-
|
|
126
|
-
label: "
|
|
129
|
+
"cloudflare-agora": {
|
|
130
|
+
label: "Agora Conversational AI",
|
|
127
131
|
web: "cloudflare",
|
|
128
|
-
agent: "railway",
|
|
129
|
-
provider: "livekit"
|
|
130
|
-
},
|
|
131
|
-
"railway-livekit-cloud": {
|
|
132
|
-
label: "Railway + LiveKit Cloud Agents",
|
|
133
|
-
web: "railway",
|
|
134
|
-
agent: "livekit",
|
|
135
|
-
provider: "livekit"
|
|
136
|
-
},
|
|
137
|
-
"railway-livekit": {
|
|
138
|
-
label: "Railway + LiveKit Agents on Railway",
|
|
139
|
-
web: "railway",
|
|
140
|
-
agent: "railway",
|
|
141
|
-
provider: "livekit"
|
|
142
|
-
},
|
|
143
|
-
"zeabur-agora": {
|
|
144
|
-
label: "Zeabur + Agora Conversational AI",
|
|
145
|
-
web: "zeabur",
|
|
146
132
|
agent: "agora",
|
|
147
133
|
provider: "agora"
|
|
148
134
|
}
|
|
@@ -661,8 +647,8 @@ async function readProjectConfig(directory) {
|
|
|
661
647
|
}
|
|
662
648
|
}
|
|
663
649
|
async function webEnvironmentPath(directory) {
|
|
664
|
-
|
|
665
|
-
return
|
|
650
|
+
await readProjectConfig(directory);
|
|
651
|
+
return ".dev.vars";
|
|
666
652
|
}
|
|
667
653
|
var init_project_config = __esm({
|
|
668
654
|
"src/project-config.ts"() {
|
|
@@ -14817,7 +14803,7 @@ var program = new Command();
|
|
|
14817
14803
|
|
|
14818
14804
|
// src/cli.ts
|
|
14819
14805
|
init_errors();
|
|
14820
|
-
import { readFile as
|
|
14806
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
14821
14807
|
import process5 from "process";
|
|
14822
14808
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14823
14809
|
|
|
@@ -14865,6 +14851,10 @@ import { spawn as spawn3 } from "child_process";
|
|
|
14865
14851
|
init_project_config();
|
|
14866
14852
|
init_catalog();
|
|
14867
14853
|
|
|
14854
|
+
// src/templates/composed.ts
|
|
14855
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
14856
|
+
import { join as join9 } from "path";
|
|
14857
|
+
|
|
14868
14858
|
// src/templates/agora.ts
|
|
14869
14859
|
init_package_managers();
|
|
14870
14860
|
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
@@ -15146,6 +15136,7 @@ var cloudflareLivekitTemplate = {
|
|
|
15146
15136
|
"- agent/ Python LiveKit agent"
|
|
15147
15137
|
],
|
|
15148
15138
|
includeFile(path2, configuration) {
|
|
15139
|
+
if (path2 === "worker-configuration.d.ts") return false;
|
|
15149
15140
|
const javascript = configuration?.packageManagers.javascript.name ?? "pnpm";
|
|
15150
15141
|
const python = configuration?.packageManagers.python?.name ?? "uv";
|
|
15151
15142
|
if (path2 === "package-lock.json") return javascript === "npm";
|
|
@@ -15225,17 +15216,13 @@ var runAgoraSetup = async ({
|
|
|
15225
15216
|
{ exitCode: EXIT_CODES.invalidArgument }
|
|
15226
15217
|
);
|
|
15227
15218
|
const config = await readProjectConfig(targetDirectory);
|
|
15228
|
-
if (config?.stack !== "
|
|
15219
|
+
if (config?.stack !== "cloudflare-agora")
|
|
15229
15220
|
throw new CliError(
|
|
15230
15221
|
"INVALID_ARGUMENT",
|
|
15231
|
-
"Expected a
|
|
15222
|
+
"Expected a Cloudflare + Agora project.",
|
|
15232
15223
|
{ exitCode: EXIT_CODES.invalidArgument }
|
|
15233
15224
|
);
|
|
15234
|
-
for (const name of [
|
|
15235
|
-
"package.json",
|
|
15236
|
-
".env.local.example",
|
|
15237
|
-
"worker/agora.ts"
|
|
15238
|
-
]) {
|
|
15225
|
+
for (const name of ["package.json", ".dev.vars.example", "worker/agora.ts"]) {
|
|
15239
15226
|
const stats = await lstat4(join7(targetDirectory, name));
|
|
15240
15227
|
if (!stats.isFile() || stats.isSymbolicLink())
|
|
15241
15228
|
throw new CliError(
|
|
@@ -15244,7 +15231,7 @@ var runAgoraSetup = async ({
|
|
|
15244
15231
|
{ exitCode: EXIT_CODES.invalidArgument }
|
|
15245
15232
|
);
|
|
15246
15233
|
}
|
|
15247
|
-
const path2 = join7(targetDirectory, ".
|
|
15234
|
+
const path2 = join7(targetDirectory, ".dev.vars");
|
|
15248
15235
|
let existing;
|
|
15249
15236
|
try {
|
|
15250
15237
|
const stats = await lstat4(path2);
|
|
@@ -15253,7 +15240,7 @@ var runAgoraSetup = async ({
|
|
|
15253
15240
|
existing = await readFile5(path2, "utf8");
|
|
15254
15241
|
} catch (error) {
|
|
15255
15242
|
if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
|
|
15256
|
-
throw new CliError("FILESYSTEM_ERROR", "Cannot safely read .
|
|
15243
|
+
throw new CliError("FILESYSTEM_ERROR", "Cannot safely read .dev.vars.", {
|
|
15257
15244
|
exitCode: EXIT_CODES.filesystem,
|
|
15258
15245
|
path: path2
|
|
15259
15246
|
});
|
|
@@ -15305,10 +15292,10 @@ var runAgoraSetup = async ({
|
|
|
15305
15292
|
}
|
|
15306
15293
|
}
|
|
15307
15294
|
const rendered = mergeDotenv(
|
|
15308
|
-
existing ?? await readFile5(join7(targetDirectory, ".
|
|
15295
|
+
existing ?? await readFile5(join7(targetDirectory, ".dev.vars.example"), "utf8"),
|
|
15309
15296
|
values
|
|
15310
15297
|
);
|
|
15311
|
-
const temporary = join7(targetDirectory, `.
|
|
15298
|
+
const temporary = join7(targetDirectory, `.dev.vars.${randomUUID3()}.tmp`);
|
|
15312
15299
|
try {
|
|
15313
15300
|
await writeFile4(temporary, rendered, { flag: "wx", mode: 384 });
|
|
15314
15301
|
await rename2(temporary, path2);
|
|
@@ -15316,7 +15303,7 @@ var runAgoraSetup = async ({
|
|
|
15316
15303
|
await rm3(temporary, { force: true });
|
|
15317
15304
|
}
|
|
15318
15305
|
onStatus(
|
|
15319
|
-
"Saved .
|
|
15306
|
+
"Saved .dev.vars. Configure an English assistant in the published Agora pipeline, with its TTS sample rate matching AGORA_AVATAR_SAMPLE_RATE. No cloud services were started."
|
|
15320
15307
|
);
|
|
15321
15308
|
return "configured";
|
|
15322
15309
|
};
|
|
@@ -15330,8 +15317,6 @@ var files = /* @__PURE__ */ new Set([
|
|
|
15330
15317
|
"pnpm-workspace.yaml",
|
|
15331
15318
|
"npmrc",
|
|
15332
15319
|
"gitignore",
|
|
15333
|
-
"dockerignore",
|
|
15334
|
-
"Dockerfile",
|
|
15335
15320
|
".editorconfig",
|
|
15336
15321
|
".prettierrc.json",
|
|
15337
15322
|
".prettierignore",
|
|
@@ -15341,12 +15326,17 @@ var files = /* @__PURE__ */ new Set([
|
|
|
15341
15326
|
"spatius.config.json",
|
|
15342
15327
|
"index.html",
|
|
15343
15328
|
"tsconfig.json",
|
|
15329
|
+
"tsconfig.web.json",
|
|
15330
|
+
"tsconfig.worker.json",
|
|
15331
|
+
"tsconfig.node.json",
|
|
15344
15332
|
"vite.config.ts",
|
|
15345
15333
|
"vitest.config.ts",
|
|
15346
15334
|
"eslint.config.mjs",
|
|
15347
|
-
"
|
|
15348
|
-
"
|
|
15335
|
+
"dev.vars.example",
|
|
15336
|
+
"wrangler.jsonc",
|
|
15337
|
+
"scripts/check-worker.mjs",
|
|
15349
15338
|
"worker/index.ts",
|
|
15339
|
+
"worker/index.test.ts",
|
|
15350
15340
|
"worker/agora.ts",
|
|
15351
15341
|
"worker/agora.test.ts",
|
|
15352
15342
|
"worker/capability.ts",
|
|
@@ -15364,8 +15354,8 @@ var files = /* @__PURE__ */ new Set([
|
|
|
15364
15354
|
"web/components/agents-ui/PROVENANCE.md"
|
|
15365
15355
|
]);
|
|
15366
15356
|
var agoraTemplate = {
|
|
15367
|
-
id: "
|
|
15368
|
-
stack: "
|
|
15357
|
+
id: "cloudflare-agora",
|
|
15358
|
+
stack: "cloudflare-agora",
|
|
15369
15359
|
requiresPython: false,
|
|
15370
15360
|
humanSteps: [
|
|
15371
15361
|
{
|
|
@@ -15376,17 +15366,13 @@ var agoraTemplate = {
|
|
|
15376
15366
|
}
|
|
15377
15367
|
],
|
|
15378
15368
|
directory: "templates/cloudflare-livekit",
|
|
15379
|
-
description: "
|
|
15369
|
+
description: "Cloudflare Workers + Agora Conversational AI",
|
|
15380
15370
|
components: [
|
|
15381
15371
|
"- web/ React + Spatius AvatarKit + Agora",
|
|
15382
|
-
"-
|
|
15372
|
+
"- worker/ Cloudflare API; Agora hosts the conversational agent"
|
|
15383
15373
|
],
|
|
15384
15374
|
layers: [
|
|
15385
15375
|
{ directory: "templates/layers/catalog" },
|
|
15386
|
-
{
|
|
15387
|
-
directory: "templates/layers/node",
|
|
15388
|
-
overrides: ["vite.config.ts", "package-lock.json", "pnpm-lock.yaml"]
|
|
15389
|
-
},
|
|
15390
15376
|
{
|
|
15391
15377
|
directory: "templates/layers/agora",
|
|
15392
15378
|
overrides: [
|
|
@@ -15395,7 +15381,10 @@ var agoraTemplate = {
|
|
|
15395
15381
|
"pnpm-lock.yaml",
|
|
15396
15382
|
"web/App.tsx",
|
|
15397
15383
|
"worker/index.ts",
|
|
15398
|
-
"
|
|
15384
|
+
"worker/index.test.ts",
|
|
15385
|
+
"tsconfig.worker.json",
|
|
15386
|
+
".dev.vars.example",
|
|
15387
|
+
"wrangler.jsonc",
|
|
15399
15388
|
"vitest.config.ts",
|
|
15400
15389
|
"README.md",
|
|
15401
15390
|
"AGENTS.md"
|
|
@@ -15407,7 +15396,7 @@ var agoraTemplate = {
|
|
|
15407
15396
|
return files.has(path2) || [...files].some((file) => file.startsWith(path2 + "/")) || path2 === "web/assets" || path2.startsWith("web/assets/");
|
|
15408
15397
|
},
|
|
15409
15398
|
mapFile(path2) {
|
|
15410
|
-
return
|
|
15399
|
+
return cloudflareLivekitTemplate.mapFile(path2);
|
|
15411
15400
|
},
|
|
15412
15401
|
async configure(directory, configuration) {
|
|
15413
15402
|
const manager = configuration.packageManagers.javascript;
|
|
@@ -15419,8 +15408,20 @@ var agoraTemplate = {
|
|
|
15419
15408
|
);
|
|
15420
15409
|
if (manager.version && manager.version !== "unknown")
|
|
15421
15410
|
pkg.packageManager = `${manager.name}@${manager.version}`;
|
|
15422
|
-
|
|
15423
|
-
|
|
15411
|
+
else delete pkg.packageManager;
|
|
15412
|
+
pkg.scripts.check = [
|
|
15413
|
+
"format:check",
|
|
15414
|
+
"lint",
|
|
15415
|
+
"typecheck",
|
|
15416
|
+
"test",
|
|
15417
|
+
"build",
|
|
15418
|
+
"test:worker"
|
|
15419
|
+
].map(run).join(" && ");
|
|
15420
|
+
pkg.scripts.deploy = `${run("build")} && wrangler deploy`;
|
|
15421
|
+
if (manager.name === "bun")
|
|
15422
|
+
pkg.trustedDependencies = ["core-js", "esbuild", "workerd"];
|
|
15423
|
+
else if (manager.name === "npm" && Number.parseInt(manager.version ?? "", 10) >= 12)
|
|
15424
|
+
pkg.allowScripts = { "core-js": true, esbuild: true, workerd: true };
|
|
15424
15425
|
await writeFile5(path2, JSON.stringify(pkg, null, 2) + "\n");
|
|
15425
15426
|
if (manager.name === "npm") {
|
|
15426
15427
|
const lockPath = join8(directory, "package-lock.json");
|
|
@@ -15431,24 +15432,7 @@ var agoraTemplate = {
|
|
|
15431
15432
|
}
|
|
15432
15433
|
await writeFile5(
|
|
15433
15434
|
join8(directory, "spatius.config.json"),
|
|
15434
|
-
JSON.stringify({ version: 2, stack: "
|
|
15435
|
-
);
|
|
15436
|
-
const serverPath = join8(directory, "server/index.ts");
|
|
15437
|
-
await writeFile5(
|
|
15438
|
-
serverPath,
|
|
15439
|
-
(await readFile6(serverPath, "utf8")).replace(
|
|
15440
|
-
"import { handleRequest }",
|
|
15441
|
-
"import type { AgoraEnvironment } from '../worker/agora.js';\nimport { handleRequest }"
|
|
15442
|
-
).replace(
|
|
15443
|
-
"const env = process.env;",
|
|
15444
|
-
"const env = process.env as unknown as AgoraEnvironment;"
|
|
15445
|
-
)
|
|
15446
|
-
);
|
|
15447
|
-
const dockerPath = join8(directory, "Dockerfile");
|
|
15448
|
-
const install = manager.name === "pnpm" ? "npm install --global pnpm@12.3.4 && pnpm install --frozen-lockfile" : manager.name === "bun" ? "npm install --global bun && bun install" : "npm ci";
|
|
15449
|
-
await writeFile5(
|
|
15450
|
-
dockerPath,
|
|
15451
|
-
(await readFile6(dockerPath, "utf8")).replace("__SPATIUS_CONTAINER_INSTALL__", install).replace("__SPATIUS_CONTAINER_BUILD__", run("build"))
|
|
15435
|
+
JSON.stringify({ version: 2, stack: "cloudflare-agora" }, null, 2) + "\n"
|
|
15452
15436
|
);
|
|
15453
15437
|
for (const name of ["README.md", "AGENTS.md"]) {
|
|
15454
15438
|
const path3 = join8(directory, name);
|
|
@@ -15459,7 +15443,16 @@ var agoraTemplate = {
|
|
|
15459
15443
|
}
|
|
15460
15444
|
await writeFile5(
|
|
15461
15445
|
join8(directory, "DEPLOYMENT.md"),
|
|
15462
|
-
|
|
15446
|
+
`# Cloudflare Workers + Agora Conversational AI
|
|
15447
|
+
|
|
15448
|
+
Run \`${run("wrangler -- login")}\`. Set your non-secret configuration from .dev.vars in the vars section of wrangler.jsonc. Upload AGORA_APP_CERTIFICATE and SPATIUS_API_KEY with \`${run("wrangler -- secret put AGORA_APP_CERTIFICATE")}\` (repeat for SPATIUS_API_KEY). Run \`${run("deploy")}\` to deploy the React frontend and API as one Cloudflare Worker.
|
|
15449
|
+
|
|
15450
|
+
Agora hosts the conversational agent. There is no Python worker, Node server, container, or database to deploy. Enable Agora Conversational AI and RTM, publish an English assistant pipeline, and set AGORA_PIPELINE_ID. The pipeline owns its models and voice. Match AGORA_AVATAR_SAMPLE_RATE to its TTS output.
|
|
15451
|
+
|
|
15452
|
+
Session creation starts a hosted agent. Disconnect and initialization failures stop it through the Worker API. Page-close cleanup is best effort, with a 60-second idle timeout fallback. Sessions and client tokens last 30 minutes; start a fresh conversation afterward.
|
|
15453
|
+
|
|
15454
|
+
Local setup writes .dev.vars only. It never provisions Cloudflare services or uploads secrets.
|
|
15455
|
+
`
|
|
15463
15456
|
);
|
|
15464
15457
|
},
|
|
15465
15458
|
createInstallPlan(directory, managers, platform) {
|
|
@@ -15481,7 +15474,7 @@ var agoraTemplate = {
|
|
|
15481
15474
|
];
|
|
15482
15475
|
},
|
|
15483
15476
|
setup: {
|
|
15484
|
-
recognizes: async (directory) => (await readProjectConfig(directory))?.stack === "
|
|
15477
|
+
recognizes: async (directory) => (await readProjectConfig(directory))?.stack === "cloudflare-agora",
|
|
15485
15478
|
run: runAgoraSetup
|
|
15486
15479
|
},
|
|
15487
15480
|
verification: [
|
|
@@ -15491,105 +15484,33 @@ var agoraTemplate = {
|
|
|
15491
15484
|
};
|
|
15492
15485
|
|
|
15493
15486
|
// src/templates/composed.ts
|
|
15494
|
-
init_catalog();
|
|
15495
|
-
import { readFile as readFile7, writeFile as writeFile6 } from "fs/promises";
|
|
15496
|
-
import { join as join9 } from "path";
|
|
15497
15487
|
init_package_managers();
|
|
15498
15488
|
function composeTemplate(stack) {
|
|
15499
|
-
if (stack === "
|
|
15500
|
-
const selected = stacks[stack];
|
|
15501
|
-
const node = selected.web === "railway";
|
|
15489
|
+
if (stack === "cloudflare-agora") return agoraTemplate;
|
|
15502
15490
|
return {
|
|
15503
15491
|
...cloudflareLivekitTemplate,
|
|
15504
|
-
id: stack,
|
|
15505
15492
|
stack,
|
|
15506
15493
|
requiresPython: true,
|
|
15507
|
-
|
|
15508
|
-
components: [
|
|
15509
|
-
"- web/ React + Spatius AvatarKit",
|
|
15510
|
-
`- ${node ? "server/" : "worker/"} ${selected.web} API`,
|
|
15511
|
-
"- agent/ Python LiveKit agent"
|
|
15512
|
-
],
|
|
15513
|
-
layers: [
|
|
15514
|
-
{ directory: "templates/layers/catalog" },
|
|
15515
|
-
...selected.agent === "railway" ? [{ directory: "templates/layers/railway-agent" }] : [],
|
|
15516
|
-
...node ? [
|
|
15517
|
-
{
|
|
15518
|
-
directory: "templates/layers/node",
|
|
15519
|
-
overrides: [
|
|
15520
|
-
"vite.config.ts",
|
|
15521
|
-
"scripts/dev-web.mjs",
|
|
15522
|
-
"package-lock.json",
|
|
15523
|
-
"pnpm-lock.yaml"
|
|
15524
|
-
]
|
|
15525
|
-
}
|
|
15526
|
-
] : []
|
|
15527
|
-
],
|
|
15528
|
-
includeFile(path2, configuration) {
|
|
15529
|
-
if (node && ["wrangler.jsonc"].includes(path2)) return false;
|
|
15530
|
-
return cloudflareLivekitTemplate.includeFile(path2, configuration);
|
|
15531
|
-
},
|
|
15532
|
-
mapFile(path2) {
|
|
15533
|
-
return node && path2 === "dev.vars.example" ? ".env.local.example" : cloudflareLivekitTemplate.mapFile(path2);
|
|
15534
|
-
},
|
|
15494
|
+
layers: [{ directory: "templates/layers/catalog" }],
|
|
15535
15495
|
async configure(directory, configuration) {
|
|
15536
15496
|
await cloudflareLivekitTemplate.configure(directory, configuration);
|
|
15537
15497
|
await writeFile6(
|
|
15538
15498
|
join9(directory, "spatius.config.json"),
|
|
15539
15499
|
JSON.stringify({ version: 2, stack }, null, 2) + "\n"
|
|
15540
15500
|
);
|
|
15541
|
-
const packagePath = join9(directory, "package.json");
|
|
15542
|
-
const pkg = JSON.parse(await readFile7(packagePath, "utf8"));
|
|
15543
15501
|
const manager = configuration.packageManagers.javascript.name;
|
|
15544
15502
|
const run = (script) => javascriptRunCommand(manager, script);
|
|
15545
|
-
|
|
15503
|
+
await writeFile6(
|
|
15504
|
+
join9(directory, "DEPLOYMENT.md"),
|
|
15505
|
+
`# Cloudflare Workers + LiveKit Cloud
|
|
15546
15506
|
|
|
15547
|
-
|
|
15548
|
-
if (node) {
|
|
15549
|
-
for (const name of ["README.md", "AGENTS.md", "agent/README.md"]) {
|
|
15550
|
-
const path2 = join9(directory, name);
|
|
15551
|
-
await writeFile6(
|
|
15552
|
-
path2,
|
|
15553
|
-
(await readFile7(path2, "utf8")).replaceAll("Cloudflare Workers", "Railway").replaceAll("Cloudflare Worker", "Node API").replaceAll(".dev.vars", ".env.local") + "\nSee DEPLOYMENT.md for this stack\u2019s deployment steps.\n"
|
|
15554
|
-
);
|
|
15555
|
-
}
|
|
15556
|
-
const workerConfigPath = join9(directory, "tsconfig.worker.json");
|
|
15557
|
-
await writeFile6(
|
|
15558
|
-
workerConfigPath,
|
|
15559
|
-
(await readFile7(workerConfigPath, "utf8")).replace(
|
|
15560
|
-
'"types": ["./worker-configuration.d.ts"]',
|
|
15561
|
-
'"types": ["./worker-configuration.d.ts", "node"]'
|
|
15562
|
-
).replace(
|
|
15563
|
-
'"include": ["worker", "worker-configuration.d.ts"]',
|
|
15564
|
-
'"include": ["worker", "worker-configuration.d.ts", "server"]'
|
|
15565
|
-
)
|
|
15566
|
-
);
|
|
15567
|
-
delete pkg.devDependencies["@cloudflare/vite-plugin"];
|
|
15568
|
-
delete pkg.devDependencies.wrangler;
|
|
15569
|
-
pkg.dependencies.tsx = "4.23.13";
|
|
15570
|
-
delete pkg.scripts["cf-typegen"];
|
|
15571
|
-
delete pkg.scripts.wrangler;
|
|
15572
|
-
pkg.scripts.start = "node --env-file-if-exists=.env.local node_modules/tsx/dist/cli.mjs server/index.ts";
|
|
15573
|
-
pkg.scripts.deploy = "railway up";
|
|
15574
|
-
const dockerPath = join9(directory, "Dockerfile");
|
|
15575
|
-
const install = manager === "pnpm" ? "npm install --global pnpm@12.3.4 && pnpm install --frozen-lockfile" : manager === "bun" ? "npm install --global bun && bun install" : "npm ci";
|
|
15576
|
-
await writeFile6(
|
|
15577
|
-
dockerPath,
|
|
15578
|
-
(await readFile7(dockerPath, "utf8")).replace("__SPATIUS_CONTAINER_INSTALL__", install).replace("__SPATIUS_CONTAINER_BUILD__", run("build"))
|
|
15579
|
-
);
|
|
15580
|
-
deployment += "Create a Railway service from this repository with the repository root as its build root and railway.json as its configuration. Add the variables from .env.local.example in Railway. Generate a public domain. The web service serves both the frontend and /api. Deploy with `railway up`; subsequent Git deployments rebuild the service.\n\n";
|
|
15581
|
-
} else {
|
|
15582
|
-
deployment += `Run \`${run("wrangler -- login")}\`, set the non-secret configuration from .dev.vars.example in wrangler.jsonc, and upload LIVEKIT_API_KEY and LIVEKIT_API_SECRET using \`${run("wrangler -- secret put LIVEKIT_API_SECRET")}\` (repeat for the key). Run \`${run("deploy")}\`.
|
|
15507
|
+
Run \`${run("wrangler -- login")}\`, set the non-secret configuration from .dev.vars.example in wrangler.jsonc, and upload LIVEKIT_API_KEY and LIVEKIT_API_SECRET using \`${run("wrangler -- secret put LIVEKIT_API_SECRET")}\` (repeat for the key). Run \`${run("deploy")}\` to deploy the frontend and API together.
|
|
15583
15508
|
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
deployment += "From agent/, run `lk cloud auth` and `lk agent create` for the first deployment. Configure the agent secrets from agent/.env.example. Use `lk agent deploy` for updates.\n";
|
|
15590
|
-
deployment += "\nDeploy the agent before opening conversations. Use separate LiveKit projects for development and production. Local setup never uploads secrets.\n";
|
|
15591
|
-
await writeFile6(join9(directory, "DEPLOYMENT.md"), deployment);
|
|
15592
|
-
await writeFile6(packagePath, JSON.stringify(pkg, null, 2) + "\n");
|
|
15509
|
+
From agent/, run \`lk cloud auth\` and \`lk agent create\` for the first deployment. Configure the agent secrets from agent/.env.example. Use \`lk agent deploy\` for updates.
|
|
15510
|
+
|
|
15511
|
+
Deploy the agent before opening conversations. Use separate LiveKit projects for development and production. Local setup never uploads secrets.
|
|
15512
|
+
`
|
|
15513
|
+
);
|
|
15593
15514
|
}
|
|
15594
15515
|
};
|
|
15595
15516
|
}
|
|
@@ -16643,7 +16564,7 @@ async function readPackageVersion() {
|
|
|
16643
16564
|
const packagePath = fileURLToPath2(
|
|
16644
16565
|
new URL("../package.json", import.meta.url)
|
|
16645
16566
|
);
|
|
16646
|
-
const contents = await
|
|
16567
|
+
const contents = await readFile7(packagePath, "utf8");
|
|
16647
16568
|
const metadata = JSON.parse(contents);
|
|
16648
16569
|
return metadata.version;
|
|
16649
16570
|
}
|
|
@@ -17070,9 +16991,10 @@ async function runSetupCommand(version, projectDirectoryArgument, rawOptions) {
|
|
|
17070
16991
|
}
|
|
17071
16992
|
function addCreateOptions(command) {
|
|
17072
16993
|
return command.addOption(
|
|
17073
|
-
new Option(
|
|
17074
|
-
|
|
17075
|
-
|
|
16994
|
+
new Option(
|
|
16995
|
+
"--stack <stack>",
|
|
16996
|
+
"voice provider on Cloudflare Workers"
|
|
16997
|
+
).choices(Object.keys(stacks))
|
|
17076
16998
|
).option("-y, --yes", "accept safe defaults without prompting").addOption(new Option("--interactive", "force interactive prompts")).addOption(new Option("--no-interactive", "disable interactive prompts")).addOption(
|
|
17077
16999
|
new Option(
|
|
17078
17000
|
"--package-manager <manager>",
|
|
@@ -17083,12 +17005,14 @@ function addCreateOptions(command) {
|
|
|
17083
17005
|
"--python-package-manager <manager>",
|
|
17084
17006
|
"use uv or pip for Python dependencies"
|
|
17085
17007
|
).choices(PYTHON_PACKAGE_MANAGERS)
|
|
17086
|
-
).option("--install", "install
|
|
17008
|
+
).option("--install", "install dependencies for the selected provider").option("--no-install", "skip dependency installation").option("--setup", "configure local provider and Spatius credentials").option("--no-setup", "skip credential setup").option("--debug", "write safe Spatius request diagnostics to stderr").option("--json", "emit one machine-readable JSON result").option("--dry-run", "validate and show planned files without writing");
|
|
17087
17009
|
}
|
|
17088
17010
|
async function main() {
|
|
17089
17011
|
const version = await readPackageVersion();
|
|
17090
17012
|
const program2 = addCreateOptions(
|
|
17091
|
-
new Command().name("create-spatius-app").description(
|
|
17013
|
+
new Command().name("create-spatius-app").description(
|
|
17014
|
+
"Create a Spatius voice-avatar app with LiveKit or Agora Conversational AI on Cloudflare Workers."
|
|
17015
|
+
).version(version).argument(
|
|
17092
17016
|
"[project-directory]",
|
|
17093
17017
|
"directory in which to create the project"
|
|
17094
17018
|
)
|
|
@@ -17098,6 +17022,7 @@ async function main() {
|
|
|
17098
17022
|
`
|
|
17099
17023
|
Examples:
|
|
17100
17024
|
npx create-spatius-app my-app
|
|
17025
|
+
npx create-spatius-app my-app --stack cloudflare-agora
|
|
17101
17026
|
npx create-spatius-app my-app --package-manager pnpm --python-package-manager uv
|
|
17102
17027
|
npx create-spatius-app my-app --no-setup
|
|
17103
17028
|
npx create-spatius-app my-app --setup --interactive
|