create-spatius-app 0.1.2 → 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 +18 -6
- package/dist/cli.js +647 -144
- package/dist/templates.js +529 -66
- package/package.json +4 -4
- package/schemas/result-v3.schema.json +6 -2
- package/schemas/result-v4.schema.json +170 -0
- package/templates/cloudflare-livekit/AGENTS.md +2 -0
- package/templates/cloudflare-livekit/agent/README.md +13 -0
- package/templates/cloudflare-livekit/agent/env.example +3 -0
- package/templates/cloudflare-livekit/agent/pyproject.toml +1 -1
- package/templates/cloudflare-livekit/agent/src/agent.py +7 -4
- 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 +247 -158
- package/templates/cloudflare-livekit/package.json +9 -8
- package/templates/cloudflare-livekit/pnpm-lock.yaml +200 -177
- package/templates/cloudflare-livekit/tests/dev-events.test.mjs +23 -0
- package/templates/cloudflare-livekit/tests/dev-fixtures.mjs +14 -5
- package/templates/cloudflare-livekit/tests/dev-supervision.test.mjs +17 -14
- package/templates/cloudflare-livekit/tsconfig.web.json +1 -0
- package/templates/cloudflare-livekit/tsconfig.worker.json +1 -0
- package/templates/cloudflare-livekit/web/api.ts +12 -0
- package/templates/cloudflare-livekit/web/components/agents-ui/agent-chat-transcript.tsx +6 -90
- package/templates/cloudflare-livekit/web/components/transcript-view.tsx +90 -0
- package/templates/cloudflare-livekit/web/session-attempt.ts +23 -2
- package/templates/cloudflare-livekit/worker/capability.test.ts +31 -0
- package/templates/cloudflare-livekit/worker/capability.ts +72 -0
- package/templates/cloudflare-livekit/worker/index.test.ts +2 -2
- package/templates/cloudflare-livekit/worker/index.ts +15 -3
- package/templates/cloudflare-livekit/worker/session.ts +34 -0
- package/templates/layers/agora/AGENTS.md +13 -0
- package/templates/layers/agora/README.md +25 -0
- package/templates/layers/agora/dev.vars.example +9 -0
- package/templates/layers/agora/package-lock.json +6988 -0
- package/templates/layers/agora/package.json +60 -0
- package/templates/layers/agora/pnpm-lock.yaml +4648 -0
- package/templates/layers/agora/scripts/check-worker.mjs +98 -0
- package/templates/layers/agora/tsconfig.worker.json +23 -0
- package/templates/layers/agora/vitest.config.ts +4 -0
- package/templates/layers/agora/web/App.tsx +174 -0
- package/templates/layers/agora/web/agora-session.test.ts +70 -0
- package/templates/layers/agora/web/agora-session.ts +267 -0
- package/templates/layers/agora/worker/agora.test.ts +62 -0
- package/templates/layers/agora/worker/agora.ts +156 -0
- package/templates/layers/agora/worker/index.test.ts +66 -0
- package/templates/layers/agora/worker/index.ts +54 -0
- package/templates/layers/agora/wrangler.jsonc +24 -0
- package/templates/layers/catalog/DEPLOYMENT.md +3 -0
- package/templates/layers/catalog/spatius.config.json +1 -0
- package/dist/cli.js.map +0 -1
- package/dist/templates.js.map +0 -1
- package/templates/cloudflare-livekit/worker-configuration.d.ts +0 -17184
package/dist/cli.js
CHANGED
|
@@ -40,6 +40,103 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
40
40
|
mod
|
|
41
41
|
));
|
|
42
42
|
|
|
43
|
+
// src/errors.ts
|
|
44
|
+
function normalizeError(error) {
|
|
45
|
+
if (error instanceof CliError) {
|
|
46
|
+
return error;
|
|
47
|
+
}
|
|
48
|
+
if (error instanceof Error && error.message.trim() !== "") {
|
|
49
|
+
return new CliError("UNKNOWN_ERROR", error.message);
|
|
50
|
+
}
|
|
51
|
+
return new CliError(
|
|
52
|
+
"UNKNOWN_ERROR",
|
|
53
|
+
"An unexpected error occurred while creating the project."
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
var EXIT_CODES, CliError, PromptCancelledError;
|
|
57
|
+
var init_errors = __esm({
|
|
58
|
+
"src/errors.ts"() {
|
|
59
|
+
"use strict";
|
|
60
|
+
EXIT_CODES = {
|
|
61
|
+
cancelled: 130,
|
|
62
|
+
filesystem: 4,
|
|
63
|
+
general: 1,
|
|
64
|
+
invalidArgument: 2,
|
|
65
|
+
success: 0,
|
|
66
|
+
targetConflict: 3
|
|
67
|
+
};
|
|
68
|
+
CliError = class extends Error {
|
|
69
|
+
code;
|
|
70
|
+
exitCode;
|
|
71
|
+
path;
|
|
72
|
+
recovery;
|
|
73
|
+
constructor(code, message, { exitCode = EXIT_CODES.general, path: path2, recovery } = {}) {
|
|
74
|
+
super(message);
|
|
75
|
+
this.name = "CliError";
|
|
76
|
+
this.code = code;
|
|
77
|
+
this.exitCode = exitCode;
|
|
78
|
+
this.path = path2;
|
|
79
|
+
this.recovery = recovery;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
PromptCancelledError = class extends CliError {
|
|
83
|
+
constructor(message = "Project creation was cancelled.") {
|
|
84
|
+
super("CANCELLED", message, {
|
|
85
|
+
exitCode: EXIT_CODES.cancelled,
|
|
86
|
+
recovery: "Run the command again when you are ready."
|
|
87
|
+
});
|
|
88
|
+
this.name = "PromptCancelledError";
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// src/catalog.ts
|
|
95
|
+
function validateSelection(stack) {
|
|
96
|
+
if (!Object.hasOwn(stacks, stack)) {
|
|
97
|
+
throw new CliError("INVALID_ARGUMENT", `Unsupported stack: ${stack}`, {
|
|
98
|
+
exitCode: EXIT_CODES.invalidArgument
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
return { stack };
|
|
102
|
+
}
|
|
103
|
+
async function selectCatalog(options) {
|
|
104
|
+
if (options.stack !== void 0) return validateSelection(options.stack);
|
|
105
|
+
const choices = Object.entries(stacks).map(([value, stack]) => ({
|
|
106
|
+
value,
|
|
107
|
+
label: stack.label
|
|
108
|
+
}));
|
|
109
|
+
return validateSelection(
|
|
110
|
+
options.interactive ? await options.prompts.choose(
|
|
111
|
+
"Which voice AI provider?",
|
|
112
|
+
choices,
|
|
113
|
+
DEFAULT_STACK
|
|
114
|
+
) : DEFAULT_STACK
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
var stacks, DEFAULT_STACK;
|
|
118
|
+
var init_catalog = __esm({
|
|
119
|
+
"src/catalog.ts"() {
|
|
120
|
+
"use strict";
|
|
121
|
+
init_errors();
|
|
122
|
+
stacks = {
|
|
123
|
+
"cloudflare-livekit": {
|
|
124
|
+
label: "LiveKit",
|
|
125
|
+
web: "cloudflare",
|
|
126
|
+
agent: "livekit",
|
|
127
|
+
provider: "livekit"
|
|
128
|
+
},
|
|
129
|
+
"cloudflare-agora": {
|
|
130
|
+
label: "Agora Conversational AI",
|
|
131
|
+
web: "cloudflare",
|
|
132
|
+
agent: "agora",
|
|
133
|
+
provider: "agora"
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
DEFAULT_STACK = "cloudflare-livekit";
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
|
|
43
140
|
// node_modules/.pnpm/sisteransi@1.0.5/node_modules/sisteransi/src/index.js
|
|
44
141
|
var require_src = __commonJS({
|
|
45
142
|
"node_modules/.pnpm/sisteransi@1.0.5/node_modules/sisteransi/src/index.js"(exports, module) {
|
|
@@ -123,7 +220,7 @@ var require_dist = __commonJS({
|
|
|
123
220
|
determineAgent: () => determineAgent2
|
|
124
221
|
});
|
|
125
222
|
module.exports = __toCommonJS(src_exports);
|
|
126
|
-
var
|
|
223
|
+
var import_promises13 = __require("fs/promises");
|
|
127
224
|
var import_node_fs4 = __require("fs");
|
|
128
225
|
var DEVIN_LOCAL_PATH = "/opt/.devin";
|
|
129
226
|
var CURSOR = "cursor";
|
|
@@ -211,7 +308,7 @@ var require_dist = __commonJS({
|
|
|
211
308
|
return { isAgent: true, agent: { name: GITHUB_COPILOT } };
|
|
212
309
|
}
|
|
213
310
|
try {
|
|
214
|
-
await (0,
|
|
311
|
+
await (0, import_promises13.access)(DEVIN_LOCAL_PATH, import_node_fs4.constants.F_OK);
|
|
215
312
|
return { isAgent: true, agent: { name: DEVIN } };
|
|
216
313
|
} catch (_error) {
|
|
217
314
|
}
|
|
@@ -220,57 +317,6 @@ var require_dist = __commonJS({
|
|
|
220
317
|
}
|
|
221
318
|
});
|
|
222
319
|
|
|
223
|
-
// src/errors.ts
|
|
224
|
-
function normalizeError(error) {
|
|
225
|
-
if (error instanceof CliError) {
|
|
226
|
-
return error;
|
|
227
|
-
}
|
|
228
|
-
if (error instanceof Error && error.message.trim() !== "") {
|
|
229
|
-
return new CliError("UNKNOWN_ERROR", error.message);
|
|
230
|
-
}
|
|
231
|
-
return new CliError(
|
|
232
|
-
"UNKNOWN_ERROR",
|
|
233
|
-
"An unexpected error occurred while creating the project."
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
var EXIT_CODES, CliError, PromptCancelledError;
|
|
237
|
-
var init_errors = __esm({
|
|
238
|
-
"src/errors.ts"() {
|
|
239
|
-
"use strict";
|
|
240
|
-
EXIT_CODES = {
|
|
241
|
-
cancelled: 130,
|
|
242
|
-
filesystem: 4,
|
|
243
|
-
general: 1,
|
|
244
|
-
invalidArgument: 2,
|
|
245
|
-
success: 0,
|
|
246
|
-
targetConflict: 3
|
|
247
|
-
};
|
|
248
|
-
CliError = class extends Error {
|
|
249
|
-
code;
|
|
250
|
-
exitCode;
|
|
251
|
-
path;
|
|
252
|
-
recovery;
|
|
253
|
-
constructor(code, message, { exitCode = EXIT_CODES.general, path: path2, recovery } = {}) {
|
|
254
|
-
super(message);
|
|
255
|
-
this.name = "CliError";
|
|
256
|
-
this.code = code;
|
|
257
|
-
this.exitCode = exitCode;
|
|
258
|
-
this.path = path2;
|
|
259
|
-
this.recovery = recovery;
|
|
260
|
-
}
|
|
261
|
-
};
|
|
262
|
-
PromptCancelledError = class extends CliError {
|
|
263
|
-
constructor(message = "Project creation was cancelled.") {
|
|
264
|
-
super("CANCELLED", message, {
|
|
265
|
-
exitCode: EXIT_CODES.cancelled,
|
|
266
|
-
recovery: "Run the command again when you are ready."
|
|
267
|
-
});
|
|
268
|
-
this.name = "PromptCancelledError";
|
|
269
|
-
}
|
|
270
|
-
};
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
|
|
274
320
|
// src/package-managers.ts
|
|
275
321
|
import { execFile, spawn } from "child_process";
|
|
276
322
|
function requiresWindowsCommandShell(command, platform = process.platform) {
|
|
@@ -531,7 +577,7 @@ function selectPythonPackageManager(inventory, explicit, platform = process.plat
|
|
|
531
577
|
}
|
|
532
578
|
function assertPackageManagersAvailable(packageManagers) {
|
|
533
579
|
const unavailable = [packageManagers.javascript, packageManagers.python].find(
|
|
534
|
-
(manager) => !manager.available
|
|
580
|
+
(manager) => manager !== void 0 && !manager.available
|
|
535
581
|
);
|
|
536
582
|
if (unavailable !== void 0) {
|
|
537
583
|
throw new CliError(
|
|
@@ -571,24 +617,74 @@ var init_package_managers = __esm({
|
|
|
571
617
|
}
|
|
572
618
|
});
|
|
573
619
|
|
|
574
|
-
// src/
|
|
575
|
-
import { lstat } from "fs/promises";
|
|
620
|
+
// src/project-config.ts
|
|
621
|
+
import { lstat, readFile } from "fs/promises";
|
|
576
622
|
import { join as join2 } from "path";
|
|
623
|
+
async function readProjectConfig(directory) {
|
|
624
|
+
const path2 = join2(directory, "spatius.config.json");
|
|
625
|
+
try {
|
|
626
|
+
const stats = await lstat(path2);
|
|
627
|
+
if (!stats.isFile() || stats.isSymbolicLink())
|
|
628
|
+
throw new Error("Expected a regular file");
|
|
629
|
+
const value = JSON.parse(await readFile(path2, "utf8"));
|
|
630
|
+
if (value && typeof value === "object" && "version" in value && value.version !== 2)
|
|
631
|
+
throw new CliError(
|
|
632
|
+
"INVALID_ARGUMENT",
|
|
633
|
+
"Unsupported spatius.config.json version. Expected version 2.",
|
|
634
|
+
{ path: path2, exitCode: EXIT_CODES.invalidArgument }
|
|
635
|
+
);
|
|
636
|
+
if (!value || typeof value !== "object" || !("version" in value) || value.version !== 2 || !("stack" in value) || typeof value.stack !== "string")
|
|
637
|
+
throw new Error("Unsupported project configuration");
|
|
638
|
+
return { version: 2, ...validateSelection(value.stack) };
|
|
639
|
+
} catch (error) {
|
|
640
|
+
if (error instanceof CliError) throw error;
|
|
641
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT")
|
|
642
|
+
return void 0;
|
|
643
|
+
throw new CliError("INVALID_ARGUMENT", "Invalid spatius.config.json.", {
|
|
644
|
+
path: path2,
|
|
645
|
+
exitCode: EXIT_CODES.invalidArgument
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
async function webEnvironmentPath(directory) {
|
|
650
|
+
await readProjectConfig(directory);
|
|
651
|
+
return ".dev.vars";
|
|
652
|
+
}
|
|
653
|
+
var init_project_config = __esm({
|
|
654
|
+
"src/project-config.ts"() {
|
|
655
|
+
"use strict";
|
|
656
|
+
init_catalog();
|
|
657
|
+
init_errors();
|
|
658
|
+
}
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
// src/setup/project.ts
|
|
662
|
+
import { lstat as lstat2 } from "fs/promises";
|
|
663
|
+
import { join as join4 } from "path";
|
|
577
664
|
async function assertSpatiusProject(targetDirectory) {
|
|
578
665
|
try {
|
|
579
|
-
const target = await
|
|
666
|
+
const target = await lstat2(targetDirectory);
|
|
580
667
|
if (!target.isDirectory() || target.isSymbolicLink()) {
|
|
581
668
|
throw new Error("not a regular directory");
|
|
582
669
|
}
|
|
670
|
+
const config = await readProjectConfig(targetDirectory);
|
|
671
|
+
const paths = config ? [
|
|
672
|
+
"package.json",
|
|
673
|
+
"spatius.config.json",
|
|
674
|
+
await webEnvironmentPath(targetDirectory) + ".example",
|
|
675
|
+
"worker/index.ts",
|
|
676
|
+
"agent/src/agent.py",
|
|
677
|
+
"agent/.env.example"
|
|
678
|
+
] : requiredProjectPaths;
|
|
583
679
|
await Promise.all([
|
|
584
680
|
...requiredProjectDirectories.map(async (relativePath) => {
|
|
585
|
-
const stats = await
|
|
681
|
+
const stats = await lstat2(join4(targetDirectory, relativePath));
|
|
586
682
|
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
587
683
|
throw new Error(`${relativePath} is not a regular directory`);
|
|
588
684
|
}
|
|
589
685
|
}),
|
|
590
|
-
...
|
|
591
|
-
const stats = await
|
|
686
|
+
...paths.map(async (relativePath) => {
|
|
687
|
+
const stats = await lstat2(join4(targetDirectory, relativePath));
|
|
592
688
|
if (!stats.isFile() || stats.isSymbolicLink()) {
|
|
593
689
|
throw new Error(`${relativePath} is not a regular file`);
|
|
594
690
|
}
|
|
@@ -610,6 +706,7 @@ var requiredProjectPaths, requiredProjectDirectories;
|
|
|
610
706
|
var init_project = __esm({
|
|
611
707
|
"src/setup/project.ts"() {
|
|
612
708
|
"use strict";
|
|
709
|
+
init_project_config();
|
|
613
710
|
init_errors();
|
|
614
711
|
requiredProjectPaths = [
|
|
615
712
|
"package.json",
|
|
@@ -648,14 +745,14 @@ var init_voices = __esm({
|
|
|
648
745
|
// src/setup/environment.ts
|
|
649
746
|
import {
|
|
650
747
|
chmod,
|
|
651
|
-
lstat as
|
|
652
|
-
readFile as
|
|
748
|
+
lstat as lstat3,
|
|
749
|
+
readFile as readFile3,
|
|
653
750
|
rename,
|
|
654
751
|
rm,
|
|
655
752
|
writeFile as writeFile2
|
|
656
753
|
} from "fs/promises";
|
|
657
754
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
658
|
-
import { join as
|
|
755
|
+
import { join as join5 } from "path";
|
|
659
756
|
function parseQuotedValue(rawValue) {
|
|
660
757
|
const value = rawValue.trim();
|
|
661
758
|
if (value.startsWith('"')) {
|
|
@@ -810,7 +907,7 @@ function isMissingFile(error) {
|
|
|
810
907
|
}
|
|
811
908
|
async function readOptionalCredentialFile(path2) {
|
|
812
909
|
try {
|
|
813
|
-
const stats = await
|
|
910
|
+
const stats = await lstat3(path2);
|
|
814
911
|
if (stats.isSymbolicLink() || !stats.isFile()) {
|
|
815
912
|
throw new CliError(
|
|
816
913
|
"FILESYSTEM_ERROR",
|
|
@@ -822,7 +919,7 @@ async function readOptionalCredentialFile(path2) {
|
|
|
822
919
|
}
|
|
823
920
|
);
|
|
824
921
|
}
|
|
825
|
-
return await
|
|
922
|
+
return await readFile3(path2, "utf8");
|
|
826
923
|
} catch (error) {
|
|
827
924
|
if (isMissingFile(error)) {
|
|
828
925
|
return void 0;
|
|
@@ -831,10 +928,11 @@ async function readOptionalCredentialFile(path2) {
|
|
|
831
928
|
}
|
|
832
929
|
}
|
|
833
930
|
async function readCredentialFileState(targetDirectory) {
|
|
931
|
+
const webPath = await webEnvironmentPath(targetDirectory);
|
|
834
932
|
const [worker, agent, workerExample] = await Promise.all([
|
|
835
|
-
readOptionalCredentialFile(
|
|
836
|
-
readOptionalCredentialFile(
|
|
837
|
-
readOptionalCredentialFile(
|
|
933
|
+
readOptionalCredentialFile(join5(targetDirectory, webPath)),
|
|
934
|
+
readOptionalCredentialFile(join5(targetDirectory, "agent", ".env.local")),
|
|
935
|
+
readOptionalCredentialFile(join5(targetDirectory, webPath + ".example"))
|
|
838
936
|
]);
|
|
839
937
|
return {
|
|
840
938
|
agent,
|
|
@@ -906,23 +1004,24 @@ async function pathExists(path2, fileSystem) {
|
|
|
906
1004
|
}
|
|
907
1005
|
}
|
|
908
1006
|
async function writeCredentialFilesAtomically(targetDirectory, contents, fileSystem = defaultAtomicFileSystem) {
|
|
1007
|
+
const webPath = await webEnvironmentPath(targetDirectory);
|
|
909
1008
|
const nonce = randomUUID2();
|
|
910
1009
|
const targets = [
|
|
911
1010
|
{
|
|
912
1011
|
backupMoved: false,
|
|
913
|
-
backupPath:
|
|
914
|
-
destinationPath:
|
|
1012
|
+
backupPath: join5(targetDirectory, `.dev.vars.${nonce}.backup`),
|
|
1013
|
+
destinationPath: join5(targetDirectory, webPath),
|
|
915
1014
|
hadOriginal: false,
|
|
916
1015
|
installed: false,
|
|
917
|
-
stagedPath:
|
|
1016
|
+
stagedPath: join5(targetDirectory, `.dev.vars.${nonce}.tmp`)
|
|
918
1017
|
},
|
|
919
1018
|
{
|
|
920
1019
|
backupMoved: false,
|
|
921
|
-
backupPath:
|
|
922
|
-
destinationPath:
|
|
1020
|
+
backupPath: join5(targetDirectory, "agent", `.env.local.${nonce}.backup`),
|
|
1021
|
+
destinationPath: join5(targetDirectory, "agent", ".env.local"),
|
|
923
1022
|
hadOriginal: false,
|
|
924
1023
|
installed: false,
|
|
925
|
-
stagedPath:
|
|
1024
|
+
stagedPath: join5(targetDirectory, "agent", `.env.local.${nonce}.tmp`)
|
|
926
1025
|
}
|
|
927
1026
|
];
|
|
928
1027
|
const payloads = [contents.worker, contents.agent];
|
|
@@ -983,9 +1082,10 @@ async function writeCredentialFilesAtomically(targetDirectory, contents, fileSys
|
|
|
983
1082
|
}
|
|
984
1083
|
}
|
|
985
1084
|
async function readCredentialExamples(targetDirectory) {
|
|
1085
|
+
const webPath = await webEnvironmentPath(targetDirectory);
|
|
986
1086
|
const [worker, agent] = await Promise.all([
|
|
987
|
-
|
|
988
|
-
|
|
1087
|
+
readFile3(join5(targetDirectory, webPath + ".example"), "utf8"),
|
|
1088
|
+
readFile3(join5(targetDirectory, "agent", ".env.example"), "utf8")
|
|
989
1089
|
]);
|
|
990
1090
|
return { agent, worker };
|
|
991
1091
|
}
|
|
@@ -993,6 +1093,7 @@ var WORKER_MANAGED_KEYS, AGENT_MANAGED_KEYS, assignmentPattern, defaultAtomicFil
|
|
|
993
1093
|
var init_environment = __esm({
|
|
994
1094
|
"src/setup/environment.ts"() {
|
|
995
1095
|
"use strict";
|
|
1096
|
+
init_project_config();
|
|
996
1097
|
init_errors();
|
|
997
1098
|
WORKER_MANAGED_KEYS = [
|
|
998
1099
|
"LIVEKIT_URL",
|
|
@@ -1012,8 +1113,8 @@ var init_environment = __esm({
|
|
|
1012
1113
|
assignmentPattern = /^\s*(?:export\s+)?([A-Za-z_][A-Za-z\d_]*)\s*=\s*(.*)$/u;
|
|
1013
1114
|
defaultAtomicFileSystem = {
|
|
1014
1115
|
chmod,
|
|
1015
|
-
lstat:
|
|
1016
|
-
readFile: (path2) =>
|
|
1116
|
+
lstat: lstat3,
|
|
1117
|
+
readFile: (path2) => readFile3(path2),
|
|
1017
1118
|
rename,
|
|
1018
1119
|
rm: (path2) => rm(path2, { force: true }),
|
|
1019
1120
|
writeFile: writeFile2
|
|
@@ -8351,12 +8452,12 @@ var require_dist2 = __commonJS({
|
|
|
8351
8452
|
// src/setup/livekit.ts
|
|
8352
8453
|
import { createHmac } from "crypto";
|
|
8353
8454
|
import { spawn as spawn2 } from "child_process";
|
|
8354
|
-
import { chmod as chmod2, mkdtemp, readFile as
|
|
8455
|
+
import { chmod as chmod2, mkdtemp, readFile as readFile4, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
8355
8456
|
import { homedir, tmpdir } from "os";
|
|
8356
|
-
import { join as
|
|
8357
|
-
async function readLiveKitProjects(configPath =
|
|
8457
|
+
import { join as join6 } from "path";
|
|
8458
|
+
async function readLiveKitProjects(configPath = join6(homedir(), ".livekit", "cli-config.yaml")) {
|
|
8358
8459
|
try {
|
|
8359
|
-
const config = (0, import_yaml.parse)(await
|
|
8460
|
+
const config = (0, import_yaml.parse)(await readFile4(configPath, "utf8"));
|
|
8360
8461
|
if (config == null) return [];
|
|
8361
8462
|
if (typeof config !== "object" || !("projects" in config) || !Array.isArray(config.projects))
|
|
8362
8463
|
throw new Error();
|
|
@@ -8519,12 +8620,12 @@ async function loadLiveKitCredentialsWithCli({
|
|
|
8519
8620
|
projectName
|
|
8520
8621
|
} = {}) {
|
|
8521
8622
|
const directory = await fileSystem.mkdtemp(
|
|
8522
|
-
|
|
8623
|
+
join6(temporaryRoot, "create-spatius-app-livekit-")
|
|
8523
8624
|
);
|
|
8524
8625
|
try {
|
|
8525
8626
|
await fileSystem.chmod(directory, 448);
|
|
8526
8627
|
await fileSystem.writeFile(
|
|
8527
|
-
|
|
8628
|
+
join6(directory, ".env.example"),
|
|
8528
8629
|
"LIVEKIT_URL=\nLIVEKIT_API_KEY=\nLIVEKIT_API_SECRET=\n",
|
|
8529
8630
|
384
|
|
8530
8631
|
);
|
|
@@ -8543,7 +8644,7 @@ async function loadLiveKitCredentialsWithCli({
|
|
|
8543
8644
|
}
|
|
8544
8645
|
);
|
|
8545
8646
|
const values = parseDotenv(
|
|
8546
|
-
await fileSystem.readFile(
|
|
8647
|
+
await fileSystem.readFile(join6(directory, ".env.local"))
|
|
8547
8648
|
);
|
|
8548
8649
|
const validated = validateCredentialBundle({
|
|
8549
8650
|
liveKit: {
|
|
@@ -8643,7 +8744,7 @@ var init_livekit = __esm({
|
|
|
8643
8744
|
defaultTemporaryFileSystem = {
|
|
8644
8745
|
chmod: chmod2,
|
|
8645
8746
|
mkdtemp,
|
|
8646
|
-
readFile: (path2) =>
|
|
8747
|
+
readFile: (path2) => readFile4(path2, "utf8"),
|
|
8647
8748
|
remove: (path2) => rm2(path2, {
|
|
8648
8749
|
force: true,
|
|
8649
8750
|
maxRetries: 3,
|
|
@@ -10094,7 +10195,7 @@ async function runCredentialSetup({
|
|
|
10094
10195
|
const examples = await readExamples(targetDirectory);
|
|
10095
10196
|
const rendered = buildCredentialFileContents(state, examples, credentials);
|
|
10096
10197
|
await writeFiles(targetDirectory, rendered);
|
|
10097
|
-
onStatus("Saved
|
|
10198
|
+
onStatus("Saved local web and agent credentials for local development only.");
|
|
10098
10199
|
return "configured";
|
|
10099
10200
|
}
|
|
10100
10201
|
var init_wizard = __esm({
|
|
@@ -10113,6 +10214,9 @@ var init_wizard = __esm({
|
|
|
10113
10214
|
}
|
|
10114
10215
|
});
|
|
10115
10216
|
|
|
10217
|
+
// src/cli.ts
|
|
10218
|
+
init_catalog();
|
|
10219
|
+
|
|
10116
10220
|
// node_modules/.pnpm/@clack+core@1.4.3/node_modules/@clack/core/dist/index.mjs
|
|
10117
10221
|
import { styleText } from "util";
|
|
10118
10222
|
import { stdout, stdin } from "process";
|
|
@@ -14699,7 +14803,7 @@ var program = new Command();
|
|
|
14699
14803
|
|
|
14700
14804
|
// src/cli.ts
|
|
14701
14805
|
init_errors();
|
|
14702
|
-
import { readFile as
|
|
14806
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
14703
14807
|
import process5 from "process";
|
|
14704
14808
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
14705
14809
|
|
|
@@ -14744,18 +14848,22 @@ init_package_managers();
|
|
|
14744
14848
|
import { spawn as spawn3 } from "child_process";
|
|
14745
14849
|
|
|
14746
14850
|
// src/templates.ts
|
|
14747
|
-
|
|
14748
|
-
|
|
14851
|
+
init_project_config();
|
|
14852
|
+
init_catalog();
|
|
14749
14853
|
|
|
14750
|
-
// src/templates/
|
|
14751
|
-
|
|
14854
|
+
// src/templates/composed.ts
|
|
14855
|
+
import { writeFile as writeFile6 } from "fs/promises";
|
|
14856
|
+
import { join as join9 } from "path";
|
|
14857
|
+
|
|
14858
|
+
// src/templates/agora.ts
|
|
14752
14859
|
init_package_managers();
|
|
14753
|
-
|
|
14860
|
+
import { readFile as readFile6, writeFile as writeFile5 } from "fs/promises";
|
|
14861
|
+
import { basename as basename3, join as join8 } from "path";
|
|
14754
14862
|
|
|
14755
14863
|
// src/templates/cloudflare-livekit/configure.ts
|
|
14756
14864
|
init_package_managers();
|
|
14757
14865
|
import { randomUUID } from "crypto";
|
|
14758
|
-
import { readFile, writeFile } from "fs/promises";
|
|
14866
|
+
import { access, readFile as readFile2, writeFile } from "fs/promises";
|
|
14759
14867
|
import { basename, join as join3 } from "path";
|
|
14760
14868
|
function toValidPackageName(value) {
|
|
14761
14869
|
const normalized = value.trim().toLowerCase().replace(/\s+/gu, "-").replace(/^[._-]+/u, "").replace(/[^a-z\d~._-]+/gu, "-").replace(/-+/gu, "-").slice(0, 214).replace(/-$/u, "");
|
|
@@ -14771,6 +14879,7 @@ function npmRequiresScriptAllowlist(version) {
|
|
|
14771
14879
|
function createReplacements(packageManagers, platform) {
|
|
14772
14880
|
const javascript = packageManagers.javascript;
|
|
14773
14881
|
const python = packageManagers.python;
|
|
14882
|
+
if (!python) throw new Error("LiveKit templates require Python tooling.");
|
|
14774
14883
|
return /* @__PURE__ */ new Map([
|
|
14775
14884
|
[
|
|
14776
14885
|
"{{SPATIUS_JAVASCRIPT_PACKAGE_MANAGER}}",
|
|
@@ -14842,7 +14951,7 @@ function createReplacements(packageManagers, platform) {
|
|
|
14842
14951
|
]);
|
|
14843
14952
|
}
|
|
14844
14953
|
async function renderFile(path2, replacements) {
|
|
14845
|
-
let contents = await
|
|
14954
|
+
let contents = await readFile2(path2, "utf8");
|
|
14846
14955
|
for (const [token, replacement] of replacements) {
|
|
14847
14956
|
contents = contents.replaceAll(token, replacement);
|
|
14848
14957
|
}
|
|
@@ -14854,7 +14963,7 @@ async function configureGeneratedTemplate(targetDirectory, {
|
|
|
14854
14963
|
projectName = basename(targetDirectory)
|
|
14855
14964
|
}) {
|
|
14856
14965
|
const packagePath = join3(targetDirectory, "package.json");
|
|
14857
|
-
const packageContents = await
|
|
14966
|
+
const packageContents = await readFile2(packagePath, "utf8");
|
|
14858
14967
|
const metadata = JSON.parse(packageContents);
|
|
14859
14968
|
const javascript = packageManagers.javascript;
|
|
14860
14969
|
const run = (script) => javascriptRunCommand(javascript.name, script);
|
|
@@ -14893,7 +15002,7 @@ async function configureGeneratedTemplate(targetDirectory, {
|
|
|
14893
15002
|
if (javascript.name === "npm") {
|
|
14894
15003
|
const packageLockPath = join3(targetDirectory, "package-lock.json");
|
|
14895
15004
|
const packageLock = JSON.parse(
|
|
14896
|
-
await
|
|
15005
|
+
await readFile2(packageLockPath, "utf8")
|
|
14897
15006
|
);
|
|
14898
15007
|
packageLock.name = metadata.name;
|
|
14899
15008
|
if (packageLock.packages?.[""] !== void 0) {
|
|
@@ -14912,12 +15021,18 @@ async function configureGeneratedTemplate(targetDirectory, {
|
|
|
14912
15021
|
await Promise.all(
|
|
14913
15022
|
[
|
|
14914
15023
|
".dev.vars.example",
|
|
15024
|
+
".env.local.example",
|
|
14915
15025
|
"wrangler.jsonc",
|
|
14916
15026
|
"agent/src/agent.py",
|
|
14917
15027
|
"agent/README.md",
|
|
14918
15028
|
"worker/index.test.ts"
|
|
14919
15029
|
].map(
|
|
14920
|
-
async (relativePath) =>
|
|
15030
|
+
async (relativePath) => access(join3(targetDirectory, relativePath)).then(
|
|
15031
|
+
() => renderFile(join3(targetDirectory, relativePath), agentReplacements)
|
|
15032
|
+
).catch((error) => {
|
|
15033
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
|
|
15034
|
+
throw error;
|
|
15035
|
+
})
|
|
14921
15036
|
)
|
|
14922
15037
|
);
|
|
14923
15038
|
await Promise.all(
|
|
@@ -14952,6 +15067,7 @@ function createInstallPlan(targetDirectory, packageManagers, platform = process.
|
|
|
14952
15067
|
`Installing JavaScript dependencies with ${javascript.name}`
|
|
14953
15068
|
)
|
|
14954
15069
|
];
|
|
15070
|
+
if (!python) return steps;
|
|
14955
15071
|
if (python.name === "uv") {
|
|
14956
15072
|
steps.push(
|
|
14957
15073
|
commandStep(
|
|
@@ -14990,6 +15106,9 @@ function createInstallPlan(targetDirectory, packageManagers, platform = process.
|
|
|
14990
15106
|
}
|
|
14991
15107
|
|
|
14992
15108
|
// src/templates/cloudflare-livekit/index.ts
|
|
15109
|
+
init_errors();
|
|
15110
|
+
init_package_managers();
|
|
15111
|
+
init_project();
|
|
14993
15112
|
var credentialSetupStep = {
|
|
14994
15113
|
command: "npx create-spatius-app setup . --interactive",
|
|
14995
15114
|
reason: "Connect LiveKit and Spatius accounts and select an avatar and voice in a secure interactive terminal. Never paste secrets into chat.",
|
|
@@ -15017,8 +15136,9 @@ var cloudflareLivekitTemplate = {
|
|
|
15017
15136
|
"- agent/ Python LiveKit agent"
|
|
15018
15137
|
],
|
|
15019
15138
|
includeFile(path2, configuration) {
|
|
15139
|
+
if (path2 === "worker-configuration.d.ts") return false;
|
|
15020
15140
|
const javascript = configuration?.packageManagers.javascript.name ?? "pnpm";
|
|
15021
|
-
const python = configuration?.packageManagers.python
|
|
15141
|
+
const python = configuration?.packageManagers.python?.name ?? "uv";
|
|
15022
15142
|
if (path2 === "package-lock.json") return javascript === "npm";
|
|
15023
15143
|
if (["npmrc", "pnpm-lock.yaml", "pnpm-workspace.yaml"].includes(path2)) {
|
|
15024
15144
|
return javascript === "pnpm";
|
|
@@ -15042,6 +15162,8 @@ var cloudflareLivekitTemplate = {
|
|
|
15042
15162
|
platform,
|
|
15043
15163
|
pythonPackageManager
|
|
15044
15164
|
}) {
|
|
15165
|
+
if (!pythonPackageManager)
|
|
15166
|
+
throw new Error("LiveKit templates require Python tooling.");
|
|
15045
15167
|
return [
|
|
15046
15168
|
...dependenciesInstalled ? [] : [
|
|
15047
15169
|
javascriptInstallCommand(javascriptPackageManager),
|
|
@@ -15074,16 +15196,339 @@ var cloudflareLivekitTemplate = {
|
|
|
15074
15196
|
]
|
|
15075
15197
|
};
|
|
15076
15198
|
|
|
15199
|
+
// src/setup/agora.ts
|
|
15200
|
+
init_environment();
|
|
15201
|
+
init_errors();
|
|
15202
|
+
init_project_config();
|
|
15203
|
+
import { lstat as lstat4, readFile as readFile5, rename as rename2, rm as rm3, writeFile as writeFile4 } from "fs/promises";
|
|
15204
|
+
import { join as join7 } from "path";
|
|
15205
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
15206
|
+
var runAgoraSetup = async ({
|
|
15207
|
+
targetDirectory,
|
|
15208
|
+
prompts,
|
|
15209
|
+
onStatus = () => void 0
|
|
15210
|
+
}) => {
|
|
15211
|
+
const target = await lstat4(targetDirectory);
|
|
15212
|
+
if (!target.isDirectory() || target.isSymbolicLink())
|
|
15213
|
+
throw new CliError(
|
|
15214
|
+
"INVALID_ARGUMENT",
|
|
15215
|
+
"Expected a regular project directory.",
|
|
15216
|
+
{ exitCode: EXIT_CODES.invalidArgument }
|
|
15217
|
+
);
|
|
15218
|
+
const config = await readProjectConfig(targetDirectory);
|
|
15219
|
+
if (config?.stack !== "cloudflare-agora")
|
|
15220
|
+
throw new CliError(
|
|
15221
|
+
"INVALID_ARGUMENT",
|
|
15222
|
+
"Expected a Cloudflare + Agora project.",
|
|
15223
|
+
{ exitCode: EXIT_CODES.invalidArgument }
|
|
15224
|
+
);
|
|
15225
|
+
for (const name of ["package.json", ".dev.vars.example", "worker/agora.ts"]) {
|
|
15226
|
+
const stats = await lstat4(join7(targetDirectory, name));
|
|
15227
|
+
if (!stats.isFile() || stats.isSymbolicLink())
|
|
15228
|
+
throw new CliError(
|
|
15229
|
+
"INVALID_ARGUMENT",
|
|
15230
|
+
"Expected regular project files.",
|
|
15231
|
+
{ exitCode: EXIT_CODES.invalidArgument }
|
|
15232
|
+
);
|
|
15233
|
+
}
|
|
15234
|
+
const path2 = join7(targetDirectory, ".dev.vars");
|
|
15235
|
+
let existing;
|
|
15236
|
+
try {
|
|
15237
|
+
const stats = await lstat4(path2);
|
|
15238
|
+
if (!stats.isFile() || stats.isSymbolicLink())
|
|
15239
|
+
throw new Error("Unsafe credential path");
|
|
15240
|
+
existing = await readFile5(path2, "utf8");
|
|
15241
|
+
} catch (error) {
|
|
15242
|
+
if (!(error instanceof Error && "code" in error && error.code === "ENOENT"))
|
|
15243
|
+
throw new CliError("FILESYSTEM_ERROR", "Cannot safely read .dev.vars.", {
|
|
15244
|
+
exitCode: EXIT_CODES.filesystem,
|
|
15245
|
+
path: path2
|
|
15246
|
+
});
|
|
15247
|
+
}
|
|
15248
|
+
if (existing && !await prompts.confirm(
|
|
15249
|
+
"Replace existing Agora and Spatius credentials? Other variables are preserved.",
|
|
15250
|
+
false
|
|
15251
|
+
))
|
|
15252
|
+
return "unchanged";
|
|
15253
|
+
const values = {};
|
|
15254
|
+
const previous = parseDotenv(existing ?? "");
|
|
15255
|
+
const secretKeys = ["AGORA_APP_CERTIFICATE", "SPATIUS_API_KEY"];
|
|
15256
|
+
for (const key of [
|
|
15257
|
+
"AGORA_APP_ID",
|
|
15258
|
+
"AGORA_APP_CERTIFICATE",
|
|
15259
|
+
"AGORA_PIPELINE_ID",
|
|
15260
|
+
"SPATIUS_APP_ID",
|
|
15261
|
+
"SPATIUS_API_KEY",
|
|
15262
|
+
"SPATIUS_AVATAR_ID",
|
|
15263
|
+
"SPATIUS_REGION",
|
|
15264
|
+
"AGORA_AVATAR_SAMPLE_RATE"
|
|
15265
|
+
]) {
|
|
15266
|
+
const defaults = {
|
|
15267
|
+
SPATIUS_REGION: "cn-beijing",
|
|
15268
|
+
AGORA_AVATAR_SAMPLE_RATE: "24000"
|
|
15269
|
+
};
|
|
15270
|
+
while (true) {
|
|
15271
|
+
const value = secretKeys.includes(key) ? await prompts.password(key) : await prompts.input(key, {
|
|
15272
|
+
initialValue: previous.get(key) ?? defaults[key]
|
|
15273
|
+
});
|
|
15274
|
+
if (isPlaceholderValue(value) || new RegExp("\\p{Cc}", "u").test(value)) {
|
|
15275
|
+
onStatus("Enter a non-empty value without control characters.");
|
|
15276
|
+
continue;
|
|
15277
|
+
}
|
|
15278
|
+
if (["AGORA_APP_ID", "AGORA_APP_CERTIFICATE"].includes(key) && !/^[a-f\d]{32}$/iu.test(value)) {
|
|
15279
|
+
onStatus(
|
|
15280
|
+
"Agora App ID and certificate must contain 32 hexadecimal characters."
|
|
15281
|
+
);
|
|
15282
|
+
continue;
|
|
15283
|
+
}
|
|
15284
|
+
if (key === "AGORA_AVATAR_SAMPLE_RATE" && ![8e3, 16e3, 22050, 24e3, 32e3, 44100, 48e3].includes(
|
|
15285
|
+
Number(value)
|
|
15286
|
+
)) {
|
|
15287
|
+
onStatus("Choose a supported audio sample rate.");
|
|
15288
|
+
continue;
|
|
15289
|
+
}
|
|
15290
|
+
values[key] = value;
|
|
15291
|
+
break;
|
|
15292
|
+
}
|
|
15293
|
+
}
|
|
15294
|
+
const rendered = mergeDotenv(
|
|
15295
|
+
existing ?? await readFile5(join7(targetDirectory, ".dev.vars.example"), "utf8"),
|
|
15296
|
+
values
|
|
15297
|
+
);
|
|
15298
|
+
const temporary = join7(targetDirectory, `.dev.vars.${randomUUID3()}.tmp`);
|
|
15299
|
+
try {
|
|
15300
|
+
await writeFile4(temporary, rendered, { flag: "wx", mode: 384 });
|
|
15301
|
+
await rename2(temporary, path2);
|
|
15302
|
+
} finally {
|
|
15303
|
+
await rm3(temporary, { force: true });
|
|
15304
|
+
}
|
|
15305
|
+
onStatus(
|
|
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."
|
|
15307
|
+
);
|
|
15308
|
+
return "configured";
|
|
15309
|
+
};
|
|
15310
|
+
|
|
15311
|
+
// src/templates/agora.ts
|
|
15312
|
+
init_project_config();
|
|
15313
|
+
var files = /* @__PURE__ */ new Set([
|
|
15314
|
+
"package.json",
|
|
15315
|
+
"package-lock.json",
|
|
15316
|
+
"pnpm-lock.yaml",
|
|
15317
|
+
"pnpm-workspace.yaml",
|
|
15318
|
+
"npmrc",
|
|
15319
|
+
"gitignore",
|
|
15320
|
+
".editorconfig",
|
|
15321
|
+
".prettierrc.json",
|
|
15322
|
+
".prettierignore",
|
|
15323
|
+
"README.md",
|
|
15324
|
+
"AGENTS.md",
|
|
15325
|
+
"DEPLOYMENT.md",
|
|
15326
|
+
"spatius.config.json",
|
|
15327
|
+
"index.html",
|
|
15328
|
+
"tsconfig.json",
|
|
15329
|
+
"tsconfig.web.json",
|
|
15330
|
+
"tsconfig.worker.json",
|
|
15331
|
+
"tsconfig.node.json",
|
|
15332
|
+
"vite.config.ts",
|
|
15333
|
+
"vitest.config.ts",
|
|
15334
|
+
"eslint.config.mjs",
|
|
15335
|
+
"dev.vars.example",
|
|
15336
|
+
"wrangler.jsonc",
|
|
15337
|
+
"scripts/check-worker.mjs",
|
|
15338
|
+
"worker/index.ts",
|
|
15339
|
+
"worker/index.test.ts",
|
|
15340
|
+
"worker/agora.ts",
|
|
15341
|
+
"worker/agora.test.ts",
|
|
15342
|
+
"worker/capability.ts",
|
|
15343
|
+
"worker/capability.test.ts",
|
|
15344
|
+
"web/App.tsx",
|
|
15345
|
+
"web/main.tsx",
|
|
15346
|
+
"web/styles.css",
|
|
15347
|
+
"web/agora-session.ts",
|
|
15348
|
+
"web/agora-session.test.ts",
|
|
15349
|
+
"web/components/transcript-view.tsx",
|
|
15350
|
+
"web/components/transcript-panel.tsx",
|
|
15351
|
+
"web/components/icons.tsx",
|
|
15352
|
+
"web/components/agents-ui/agent-chat-input.tsx",
|
|
15353
|
+
"web/components/agents-ui/LICENSE",
|
|
15354
|
+
"web/components/agents-ui/PROVENANCE.md"
|
|
15355
|
+
]);
|
|
15356
|
+
var agoraTemplate = {
|
|
15357
|
+
id: "cloudflare-agora",
|
|
15358
|
+
stack: "cloudflare-agora",
|
|
15359
|
+
requiresPython: false,
|
|
15360
|
+
humanSteps: [
|
|
15361
|
+
{
|
|
15362
|
+
command: "npx create-spatius-app setup . --interactive",
|
|
15363
|
+
reason: "Enter Agora and Spatius credentials in a secure interactive terminal and configure a published English Agora pipeline. Never paste secrets into chat.",
|
|
15364
|
+
requiresHuman: true,
|
|
15365
|
+
requiresTty: true
|
|
15366
|
+
}
|
|
15367
|
+
],
|
|
15368
|
+
directory: "templates/cloudflare-livekit",
|
|
15369
|
+
description: "Cloudflare Workers + Agora Conversational AI",
|
|
15370
|
+
components: [
|
|
15371
|
+
"- web/ React + Spatius AvatarKit + Agora",
|
|
15372
|
+
"- worker/ Cloudflare API; Agora hosts the conversational agent"
|
|
15373
|
+
],
|
|
15374
|
+
layers: [
|
|
15375
|
+
{ directory: "templates/layers/catalog" },
|
|
15376
|
+
{
|
|
15377
|
+
directory: "templates/layers/agora",
|
|
15378
|
+
overrides: [
|
|
15379
|
+
"package.json",
|
|
15380
|
+
"package-lock.json",
|
|
15381
|
+
"pnpm-lock.yaml",
|
|
15382
|
+
"web/App.tsx",
|
|
15383
|
+
"worker/index.ts",
|
|
15384
|
+
"worker/index.test.ts",
|
|
15385
|
+
"tsconfig.worker.json",
|
|
15386
|
+
".dev.vars.example",
|
|
15387
|
+
"wrangler.jsonc",
|
|
15388
|
+
"vitest.config.ts",
|
|
15389
|
+
"README.md",
|
|
15390
|
+
"AGENTS.md"
|
|
15391
|
+
]
|
|
15392
|
+
}
|
|
15393
|
+
],
|
|
15394
|
+
includeFile(path2, configuration) {
|
|
15395
|
+
if (!cloudflareLivekitTemplate.includeFile(path2, configuration)) return false;
|
|
15396
|
+
return files.has(path2) || [...files].some((file) => file.startsWith(path2 + "/")) || path2 === "web/assets" || path2.startsWith("web/assets/");
|
|
15397
|
+
},
|
|
15398
|
+
mapFile(path2) {
|
|
15399
|
+
return cloudflareLivekitTemplate.mapFile(path2);
|
|
15400
|
+
},
|
|
15401
|
+
async configure(directory, configuration) {
|
|
15402
|
+
const manager = configuration.packageManagers.javascript;
|
|
15403
|
+
const run = (script) => javascriptRunCommand(manager.name, script);
|
|
15404
|
+
const path2 = join8(directory, "package.json");
|
|
15405
|
+
const pkg = JSON.parse(await readFile6(path2, "utf8"));
|
|
15406
|
+
pkg.name = toValidPackageName(
|
|
15407
|
+
configuration.projectName ?? basename3(directory)
|
|
15408
|
+
);
|
|
15409
|
+
if (manager.version && manager.version !== "unknown")
|
|
15410
|
+
pkg.packageManager = `${manager.name}@${manager.version}`;
|
|
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 };
|
|
15425
|
+
await writeFile5(path2, JSON.stringify(pkg, null, 2) + "\n");
|
|
15426
|
+
if (manager.name === "npm") {
|
|
15427
|
+
const lockPath = join8(directory, "package-lock.json");
|
|
15428
|
+
const lock = JSON.parse(await readFile6(lockPath, "utf8"));
|
|
15429
|
+
lock.name = pkg.name;
|
|
15430
|
+
lock.packages[""].name = pkg.name;
|
|
15431
|
+
await writeFile5(lockPath, JSON.stringify(lock, null, 2) + "\n");
|
|
15432
|
+
}
|
|
15433
|
+
await writeFile5(
|
|
15434
|
+
join8(directory, "spatius.config.json"),
|
|
15435
|
+
JSON.stringify({ version: 2, stack: "cloudflare-agora" }, null, 2) + "\n"
|
|
15436
|
+
);
|
|
15437
|
+
for (const name of ["README.md", "AGENTS.md"]) {
|
|
15438
|
+
const path3 = join8(directory, name);
|
|
15439
|
+
await writeFile5(
|
|
15440
|
+
path3,
|
|
15441
|
+
(await readFile6(path3, "utf8")).replaceAll("__INSTALL__", javascriptInstallCommand(manager.name)).replaceAll("__DEV__", run("dev")).replaceAll("__CHECK__", run("check"))
|
|
15442
|
+
);
|
|
15443
|
+
}
|
|
15444
|
+
await writeFile5(
|
|
15445
|
+
join8(directory, "DEPLOYMENT.md"),
|
|
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
|
+
`
|
|
15456
|
+
);
|
|
15457
|
+
},
|
|
15458
|
+
createInstallPlan(directory, managers, platform) {
|
|
15459
|
+
return createInstallPlan(
|
|
15460
|
+
directory,
|
|
15461
|
+
{ javascript: managers.javascript },
|
|
15462
|
+
platform
|
|
15463
|
+
);
|
|
15464
|
+
},
|
|
15465
|
+
nextSteps({
|
|
15466
|
+
dependenciesInstalled,
|
|
15467
|
+
credentialsConfigured,
|
|
15468
|
+
javascriptPackageManager
|
|
15469
|
+
}) {
|
|
15470
|
+
return [
|
|
15471
|
+
...dependenciesInstalled ? [] : [javascriptInstallCommand(javascriptPackageManager)],
|
|
15472
|
+
...credentialsConfigured ? [] : ["npx create-spatius-app setup . --interactive"],
|
|
15473
|
+
javascriptRunCommand(javascriptPackageManager, "dev")
|
|
15474
|
+
];
|
|
15475
|
+
},
|
|
15476
|
+
setup: {
|
|
15477
|
+
recognizes: async (directory) => (await readProjectConfig(directory))?.stack === "cloudflare-agora",
|
|
15478
|
+
run: runAgoraSetup
|
|
15479
|
+
},
|
|
15480
|
+
verification: [
|
|
15481
|
+
{ javascript: "npm", scripts: ["check"] },
|
|
15482
|
+
{ javascript: "pnpm", scripts: ["check"] }
|
|
15483
|
+
]
|
|
15484
|
+
};
|
|
15485
|
+
|
|
15486
|
+
// src/templates/composed.ts
|
|
15487
|
+
init_package_managers();
|
|
15488
|
+
function composeTemplate(stack) {
|
|
15489
|
+
if (stack === "cloudflare-agora") return agoraTemplate;
|
|
15490
|
+
return {
|
|
15491
|
+
...cloudflareLivekitTemplate,
|
|
15492
|
+
stack,
|
|
15493
|
+
requiresPython: true,
|
|
15494
|
+
layers: [{ directory: "templates/layers/catalog" }],
|
|
15495
|
+
async configure(directory, configuration) {
|
|
15496
|
+
await cloudflareLivekitTemplate.configure(directory, configuration);
|
|
15497
|
+
await writeFile6(
|
|
15498
|
+
join9(directory, "spatius.config.json"),
|
|
15499
|
+
JSON.stringify({ version: 2, stack }, null, 2) + "\n"
|
|
15500
|
+
);
|
|
15501
|
+
const manager = configuration.packageManagers.javascript.name;
|
|
15502
|
+
const run = (script) => javascriptRunCommand(manager, script);
|
|
15503
|
+
await writeFile6(
|
|
15504
|
+
join9(directory, "DEPLOYMENT.md"),
|
|
15505
|
+
`# Cloudflare Workers + LiveKit Cloud
|
|
15506
|
+
|
|
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.
|
|
15508
|
+
|
|
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
|
+
);
|
|
15514
|
+
}
|
|
15515
|
+
};
|
|
15516
|
+
}
|
|
15517
|
+
|
|
15518
|
+
// src/templates.ts
|
|
15519
|
+
init_errors();
|
|
15520
|
+
import { fileURLToPath } from "url";
|
|
15521
|
+
|
|
15077
15522
|
// src/scaffold.ts
|
|
15078
15523
|
init_errors();
|
|
15079
15524
|
import { constants } from "fs";
|
|
15080
|
-
import { copyFile, lstat as
|
|
15081
|
-
import { basename as
|
|
15525
|
+
import { copyFile, lstat as lstat6, mkdir, readdir as readdir2, rm as rm4 } from "fs/promises";
|
|
15526
|
+
import { basename as basename4, dirname as dirname2, isAbsolute, join as join10, win32 as win322 } from "path";
|
|
15082
15527
|
|
|
15083
15528
|
// src/project-directory.ts
|
|
15084
15529
|
init_errors();
|
|
15085
15530
|
import { homedir as homedir2 } from "os";
|
|
15086
|
-
import { lstat as
|
|
15531
|
+
import { lstat as lstat5, readdir } from "fs/promises";
|
|
15087
15532
|
import { parse as parse2, resolve } from "path";
|
|
15088
15533
|
function normalizeTargetDirectory(input, currentWorkingDirectory = process.cwd()) {
|
|
15089
15534
|
return resolve(currentWorkingDirectory, input);
|
|
@@ -15115,7 +15560,7 @@ function assertSafeTargetDirectory(targetDirectory, homeDirectory = homedir2())
|
|
|
15115
15560
|
}
|
|
15116
15561
|
async function inspectTargetDirectory(targetDirectory) {
|
|
15117
15562
|
try {
|
|
15118
|
-
const stats = await
|
|
15563
|
+
const stats = await lstat5(targetDirectory);
|
|
15119
15564
|
if (!stats.isDirectory() || stats.isSymbolicLink()) {
|
|
15120
15565
|
return "not-a-directory";
|
|
15121
15566
|
}
|
|
@@ -15187,10 +15632,10 @@ function assertRelativeDestination(path2) {
|
|
|
15187
15632
|
}
|
|
15188
15633
|
}
|
|
15189
15634
|
async function listTemplateFiles(templateDirectory, template, configuration, relativeDirectory = "") {
|
|
15190
|
-
const entries = await readdir2(
|
|
15635
|
+
const entries = await readdir2(join10(templateDirectory, relativeDirectory), {
|
|
15191
15636
|
withFileTypes: true
|
|
15192
15637
|
});
|
|
15193
|
-
const
|
|
15638
|
+
const files2 = [];
|
|
15194
15639
|
for (const entry of entries) {
|
|
15195
15640
|
if (shouldIgnoreTemplateEntry(entry.name)) continue;
|
|
15196
15641
|
const source = [relativeDirectory, entry.name].filter(Boolean).join("/");
|
|
@@ -15199,7 +15644,7 @@ async function listTemplateFiles(templateDirectory, template, configuration, rel
|
|
|
15199
15644
|
throw new Error("Bundled templates must not contain symbolic links.");
|
|
15200
15645
|
}
|
|
15201
15646
|
if (entry.isDirectory()) {
|
|
15202
|
-
|
|
15647
|
+
files2.push(
|
|
15203
15648
|
...await listTemplateFiles(
|
|
15204
15649
|
templateDirectory,
|
|
15205
15650
|
template,
|
|
@@ -15210,12 +15655,12 @@ async function listTemplateFiles(templateDirectory, template, configuration, rel
|
|
|
15210
15655
|
} else if (entry.isFile()) {
|
|
15211
15656
|
const destination = template.mapFile(source);
|
|
15212
15657
|
assertRelativeDestination(destination);
|
|
15213
|
-
|
|
15658
|
+
files2.push({ source, destination });
|
|
15214
15659
|
} else {
|
|
15215
15660
|
throw new Error("Bundled templates must contain regular files.");
|
|
15216
15661
|
}
|
|
15217
15662
|
}
|
|
15218
|
-
return
|
|
15663
|
+
return files2;
|
|
15219
15664
|
}
|
|
15220
15665
|
async function createScaffoldPlan({
|
|
15221
15666
|
configuration,
|
|
@@ -15227,7 +15672,7 @@ async function createScaffoldPlan({
|
|
|
15227
15672
|
const targetState = await assertTargetDirectoryAvailable(targetDirectory);
|
|
15228
15673
|
let entries;
|
|
15229
15674
|
try {
|
|
15230
|
-
const root = await
|
|
15675
|
+
const root = await lstat6(templateDirectory);
|
|
15231
15676
|
if (!root.isDirectory() || root.isSymbolicLink()) {
|
|
15232
15677
|
throw new Error("The template must be a regular directory.");
|
|
15233
15678
|
}
|
|
@@ -15236,6 +15681,33 @@ async function createScaffoldPlan({
|
|
|
15236
15681
|
template,
|
|
15237
15682
|
configuration
|
|
15238
15683
|
);
|
|
15684
|
+
for (const layer of template.layers ?? []) {
|
|
15685
|
+
const directory = resolveTemplateDirectory({
|
|
15686
|
+
...template,
|
|
15687
|
+
directory: layer.directory
|
|
15688
|
+
});
|
|
15689
|
+
const root2 = await lstat6(directory);
|
|
15690
|
+
if (!root2.isDirectory() || root2.isSymbolicLink())
|
|
15691
|
+
throw new Error("Invalid template layer.");
|
|
15692
|
+
const additions = await listTemplateFiles(
|
|
15693
|
+
directory,
|
|
15694
|
+
template,
|
|
15695
|
+
configuration
|
|
15696
|
+
);
|
|
15697
|
+
for (const addition of additions) {
|
|
15698
|
+
const index = entries.findIndex(
|
|
15699
|
+
(entry) => entry.destination === addition.destination
|
|
15700
|
+
);
|
|
15701
|
+
if (index >= 0) {
|
|
15702
|
+
if (!layer.overrides?.includes(addition.destination))
|
|
15703
|
+
throw new Error(
|
|
15704
|
+
`Undeclared template override: ${addition.destination}`
|
|
15705
|
+
);
|
|
15706
|
+
entries.splice(index, 1);
|
|
15707
|
+
}
|
|
15708
|
+
entries.push({ ...addition, directory });
|
|
15709
|
+
}
|
|
15710
|
+
}
|
|
15239
15711
|
const destinations = /* @__PURE__ */ new Set();
|
|
15240
15712
|
const directories = /* @__PURE__ */ new Map();
|
|
15241
15713
|
for (const { destination } of entries) {
|
|
@@ -15294,11 +15766,11 @@ async function createScaffoldPlan({
|
|
|
15294
15766
|
}
|
|
15295
15767
|
async function rollback(targetDirectory, targetCreated, createdPaths) {
|
|
15296
15768
|
if (targetCreated) {
|
|
15297
|
-
await
|
|
15769
|
+
await rm4(targetDirectory, { force: true, recursive: true });
|
|
15298
15770
|
return;
|
|
15299
15771
|
}
|
|
15300
15772
|
for (const path2 of [...createdPaths].reverse()) {
|
|
15301
|
-
await
|
|
15773
|
+
await rm4(path2, { force: true, recursive: true });
|
|
15302
15774
|
}
|
|
15303
15775
|
}
|
|
15304
15776
|
async function scaffoldProject({
|
|
@@ -15333,9 +15805,9 @@ async function scaffoldProject({
|
|
|
15333
15805
|
targetCreated = true;
|
|
15334
15806
|
}
|
|
15335
15807
|
for (const entry of plan.entries) {
|
|
15336
|
-
const source =
|
|
15337
|
-
const destination =
|
|
15338
|
-
const stats = await
|
|
15808
|
+
const source = join10(entry.directory ?? templateDirectory, entry.source);
|
|
15809
|
+
const destination = join10(targetDirectory, entry.destination);
|
|
15810
|
+
const stats = await lstat6(source);
|
|
15339
15811
|
if (!stats.isFile() || stats.isSymbolicLink()) {
|
|
15340
15812
|
throw new Error("A template source is no longer a regular file.");
|
|
15341
15813
|
}
|
|
@@ -15352,7 +15824,7 @@ async function scaffoldProject({
|
|
|
15352
15824
|
);
|
|
15353
15825
|
throw new CliError(
|
|
15354
15826
|
"FILESYSTEM_ERROR",
|
|
15355
|
-
"Could not create " +
|
|
15827
|
+
"Could not create " + basename4(targetDirectory) + ": " + (error instanceof Error ? error.message : "unknown filesystem error"),
|
|
15356
15828
|
{
|
|
15357
15829
|
exitCode: EXIT_CODES.filesystem,
|
|
15358
15830
|
path: targetDirectory,
|
|
@@ -15366,16 +15838,26 @@ async function scaffoldProject({
|
|
|
15366
15838
|
// src/templates.ts
|
|
15367
15839
|
init_package_managers();
|
|
15368
15840
|
var DEFAULT_TEMPLATE_ID = "cloudflare-livekit";
|
|
15369
|
-
var templateRegistry =
|
|
15370
|
-
[
|
|
15371
|
-
|
|
15841
|
+
var templateRegistry = Object.fromEntries(
|
|
15842
|
+
Object.keys(stacks).map((stack) => [
|
|
15843
|
+
stack,
|
|
15844
|
+
composeTemplate(stack)
|
|
15845
|
+
])
|
|
15846
|
+
);
|
|
15372
15847
|
function getTemplate(id = DEFAULT_TEMPLATE_ID) {
|
|
15373
|
-
|
|
15848
|
+
const template = templateRegistry[id];
|
|
15849
|
+
if (!template)
|
|
15850
|
+
throw new CliError("INVALID_ARGUMENT", `Unknown template: ${id}`, {
|
|
15851
|
+
exitCode: EXIT_CODES.invalidArgument
|
|
15852
|
+
});
|
|
15853
|
+
return template;
|
|
15374
15854
|
}
|
|
15375
15855
|
function resolveTemplateDirectory(template = getTemplate()) {
|
|
15376
15856
|
return fileURLToPath(new URL(`../${template.directory}/`, import.meta.url));
|
|
15377
15857
|
}
|
|
15378
|
-
async function resolveProjectTemplate(targetDirectory, templates =
|
|
15858
|
+
async function resolveProjectTemplate(targetDirectory, templates = [getTemplate()]) {
|
|
15859
|
+
const config = await readProjectConfig(targetDirectory);
|
|
15860
|
+
if (config) return getTemplate(config.stack);
|
|
15379
15861
|
const matches = [];
|
|
15380
15862
|
for (const template of templates) {
|
|
15381
15863
|
if (await template.setup.recognizes(targetDirectory))
|
|
@@ -15568,10 +16050,10 @@ function resolveInteractionMode({
|
|
|
15568
16050
|
init_package_managers();
|
|
15569
16051
|
|
|
15570
16052
|
// src/output.ts
|
|
15571
|
-
var OUTPUT_SCHEMA_VERSION =
|
|
16053
|
+
var OUTPUT_SCHEMA_VERSION = 4;
|
|
15572
16054
|
function createSuccessResult({
|
|
15573
16055
|
dryRun,
|
|
15574
|
-
files,
|
|
16056
|
+
files: files2,
|
|
15575
16057
|
generatorVersion,
|
|
15576
16058
|
dependenciesInstalled,
|
|
15577
16059
|
javascriptPackageManager,
|
|
@@ -15587,7 +16069,7 @@ function createSuccessResult({
|
|
|
15587
16069
|
gitInitialized: false
|
|
15588
16070
|
},
|
|
15589
16071
|
command: "create",
|
|
15590
|
-
created: dryRun ? [] : [...
|
|
16072
|
+
created: dryRun ? [] : [...files2],
|
|
15591
16073
|
dryRun,
|
|
15592
16074
|
generatorVersion,
|
|
15593
16075
|
humanSteps: template.humanSteps.map((step) => ({ ...step })),
|
|
@@ -15600,12 +16082,12 @@ function createSuccessResult({
|
|
|
15600
16082
|
ok: true,
|
|
15601
16083
|
packageManagers: {
|
|
15602
16084
|
javascript: javascriptPackageManager,
|
|
15603
|
-
python: pythonPackageManager.name
|
|
16085
|
+
...pythonPackageManager ? { python: pythonPackageManager.name } : {}
|
|
15604
16086
|
},
|
|
15605
16087
|
projectDirectory,
|
|
15606
16088
|
schemaVersion: OUTPUT_SCHEMA_VERSION,
|
|
15607
|
-
|
|
15608
|
-
wouldCreate: dryRun ? [...
|
|
16089
|
+
stack: template.stack ?? "cloudflare-livekit",
|
|
16090
|
+
wouldCreate: dryRun ? [...files2] : []
|
|
15609
16091
|
};
|
|
15610
16092
|
}
|
|
15611
16093
|
function createFailureResult(error) {
|
|
@@ -16035,7 +16517,10 @@ var PromptSession = class {
|
|
|
16035
16517
|
return this.confirm("Install dependencies now?", true);
|
|
16036
16518
|
}
|
|
16037
16519
|
shouldConfigureCredentials() {
|
|
16038
|
-
return this.confirm(
|
|
16520
|
+
return this.confirm(
|
|
16521
|
+
"Configure provider and Spatius credentials now?",
|
|
16522
|
+
true
|
|
16523
|
+
);
|
|
16039
16524
|
}
|
|
16040
16525
|
};
|
|
16041
16526
|
|
|
@@ -16079,7 +16564,7 @@ async function readPackageVersion() {
|
|
|
16079
16564
|
const packagePath = fileURLToPath2(
|
|
16080
16565
|
new URL("../package.json", import.meta.url)
|
|
16081
16566
|
);
|
|
16082
|
-
const contents = await
|
|
16567
|
+
const contents = await readFile7(packagePath, "utf8");
|
|
16083
16568
|
const metadata = JSON.parse(contents);
|
|
16084
16569
|
return metadata.version;
|
|
16085
16570
|
}
|
|
@@ -16177,7 +16662,7 @@ function managerOptions(managers) {
|
|
|
16177
16662
|
value: manager.name
|
|
16178
16663
|
}));
|
|
16179
16664
|
}
|
|
16180
|
-
async function resolvePackageManagers(options, interactive, prompts) {
|
|
16665
|
+
async function resolvePackageManagers(options, interactive, prompts, requiresPython = true) {
|
|
16181
16666
|
if (!interactive && (options.install !== true || options.dryRun)) {
|
|
16182
16667
|
const inventory2 = { javascript: [], python: [] };
|
|
16183
16668
|
const javascriptDefault2 = options.packageManager ?? inferInvokingPackageManager(process5.env.npm_config_user_agent) ?? "npm";
|
|
@@ -16187,15 +16672,17 @@ async function resolvePackageManagers(options, interactive, prompts) {
|
|
|
16187
16672
|
javascriptDefault2,
|
|
16188
16673
|
process5.env.npm_config_user_agent
|
|
16189
16674
|
),
|
|
16190
|
-
|
|
16191
|
-
|
|
16192
|
-
|
|
16193
|
-
|
|
16675
|
+
...requiresPython ? {
|
|
16676
|
+
python: selectPythonPackageManager(
|
|
16677
|
+
inventory2,
|
|
16678
|
+
options.pythonPackageManager ?? "uv"
|
|
16679
|
+
)
|
|
16680
|
+
} : {}
|
|
16194
16681
|
};
|
|
16195
16682
|
}
|
|
16196
16683
|
const inventory = await detectPackageManagers({
|
|
16197
16684
|
...options.packageManager === void 0 ? {} : { javascript: [options.packageManager] },
|
|
16198
|
-
|
|
16685
|
+
...!requiresPython ? { python: [] } : options.pythonPackageManager === void 0 ? {} : { python: [options.pythonPackageManager] }
|
|
16199
16686
|
});
|
|
16200
16687
|
const javascriptDefault = !interactive && options.packageManager === void 0 && inventory.javascript.length === 0 ? "npm" : options.packageManager;
|
|
16201
16688
|
const pythonDefault = !interactive && options.pythonPackageManager === void 0 && inventory.python.length === 0 ? "uv" : options.pythonPackageManager;
|
|
@@ -16204,7 +16691,7 @@ async function resolvePackageManagers(options, interactive, prompts) {
|
|
|
16204
16691
|
javascriptDefault,
|
|
16205
16692
|
process5.env.npm_config_user_agent
|
|
16206
16693
|
);
|
|
16207
|
-
let python = selectPythonPackageManager(inventory, pythonDefault);
|
|
16694
|
+
let python = requiresPython ? selectPythonPackageManager(inventory, pythonDefault) : void 0;
|
|
16208
16695
|
if (interactive && options.packageManager === void 0) {
|
|
16209
16696
|
const selectedName = await prompts.choose(
|
|
16210
16697
|
"Which JavaScript package manager?",
|
|
@@ -16215,7 +16702,7 @@ async function resolvePackageManagers(options, interactive, prompts) {
|
|
|
16215
16702
|
(manager) => manager.name === selectedName
|
|
16216
16703
|
);
|
|
16217
16704
|
}
|
|
16218
|
-
if (interactive && options.pythonPackageManager === void 0) {
|
|
16705
|
+
if (interactive && python && options.pythonPackageManager === void 0) {
|
|
16219
16706
|
const selectedName = await prompts.choose(
|
|
16220
16707
|
"Which Python package manager?",
|
|
16221
16708
|
managerOptions(inventory.python),
|
|
@@ -16226,7 +16713,7 @@ async function resolvePackageManagers(options, interactive, prompts) {
|
|
|
16226
16713
|
return { javascript, python };
|
|
16227
16714
|
}
|
|
16228
16715
|
function renderManagerSummary(javascript, python) {
|
|
16229
|
-
return `${javascript.name} + ${python.name}
|
|
16716
|
+
return python ? `${javascript.name} + ${python.name}` : javascript.name;
|
|
16230
16717
|
}
|
|
16231
16718
|
function isTestTerminalOverride() {
|
|
16232
16719
|
return process5.env.NODE_ENV === "test" && process5.env.CREATE_SPATIUS_APP_TEST_INTERACTIVE === "1";
|
|
@@ -16256,6 +16743,7 @@ async function detectExecutionAgent() {
|
|
|
16256
16743
|
}
|
|
16257
16744
|
async function runCreateCommand(version, projectDirectoryArgument, rawOptions) {
|
|
16258
16745
|
const options = {
|
|
16746
|
+
stack: rawOptions.stack,
|
|
16259
16747
|
debug: rawOptions.debug === true,
|
|
16260
16748
|
dryRun: rawOptions.dryRun === true,
|
|
16261
16749
|
install: rawOptions.install,
|
|
@@ -16300,6 +16788,14 @@ async function runCreateCommand(version, projectDirectoryArgument, rawOptions) {
|
|
|
16300
16788
|
`${theme.accent("create-spatius-app")} ${theme.highlight(`v${version}`)}`
|
|
16301
16789
|
);
|
|
16302
16790
|
}
|
|
16791
|
+
const selection = await selectCatalog({ ...options, interactive, prompts });
|
|
16792
|
+
const template = getTemplate(selection.stack);
|
|
16793
|
+
if (template.requiresPython === false && options.pythonPackageManager)
|
|
16794
|
+
throw new CliError(
|
|
16795
|
+
"INVALID_ARGUMENT",
|
|
16796
|
+
"This stack does not use Python. Remove --python-package-manager.",
|
|
16797
|
+
{ exitCode: EXIT_CODES.invalidArgument }
|
|
16798
|
+
);
|
|
16303
16799
|
const projectDirectoryInput = await resolveProjectDirectoryInput({
|
|
16304
16800
|
argument: projectDirectoryArgument,
|
|
16305
16801
|
interactive,
|
|
@@ -16309,13 +16805,13 @@ async function runCreateCommand(version, projectDirectoryArgument, rawOptions) {
|
|
|
16309
16805
|
const packageManagers = await resolvePackageManagers(
|
|
16310
16806
|
options,
|
|
16311
16807
|
interactive,
|
|
16312
|
-
prompts
|
|
16808
|
+
prompts,
|
|
16809
|
+
template.requiresPython !== false
|
|
16313
16810
|
);
|
|
16314
16811
|
const shouldInstall = !options.dryRun && (options.install ?? (interactive ? await prompts.shouldInstall() : false));
|
|
16315
16812
|
if (shouldInstall) {
|
|
16316
16813
|
assertPackageManagersAvailable(packageManagers);
|
|
16317
16814
|
}
|
|
16318
|
-
const template = getTemplate();
|
|
16319
16815
|
const configuration = { packageManagers };
|
|
16320
16816
|
const scaffold = options.dryRun ? await createScaffoldPlan({
|
|
16321
16817
|
configuration,
|
|
@@ -16494,7 +16990,12 @@ async function runSetupCommand(version, projectDirectoryArgument, rawOptions) {
|
|
|
16494
16990
|
}
|
|
16495
16991
|
}
|
|
16496
16992
|
function addCreateOptions(command) {
|
|
16497
|
-
return command.
|
|
16993
|
+
return command.addOption(
|
|
16994
|
+
new Option(
|
|
16995
|
+
"--stack <stack>",
|
|
16996
|
+
"voice provider on Cloudflare Workers"
|
|
16997
|
+
).choices(Object.keys(stacks))
|
|
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(
|
|
16498
16999
|
new Option(
|
|
16499
17000
|
"--package-manager <manager>",
|
|
16500
17001
|
"use pnpm, bun, or npm for JavaScript dependencies"
|
|
@@ -16504,12 +17005,14 @@ function addCreateOptions(command) {
|
|
|
16504
17005
|
"--python-package-manager <manager>",
|
|
16505
17006
|
"use uv or pip for Python dependencies"
|
|
16506
17007
|
).choices(PYTHON_PACKAGE_MANAGERS)
|
|
16507
|
-
).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");
|
|
16508
17009
|
}
|
|
16509
17010
|
async function main() {
|
|
16510
17011
|
const version = await readPackageVersion();
|
|
16511
17012
|
const program2 = addCreateOptions(
|
|
16512
|
-
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(
|
|
16513
17016
|
"[project-directory]",
|
|
16514
17017
|
"directory in which to create the project"
|
|
16515
17018
|
)
|
|
@@ -16519,6 +17022,7 @@ async function main() {
|
|
|
16519
17022
|
`
|
|
16520
17023
|
Examples:
|
|
16521
17024
|
npx create-spatius-app my-app
|
|
17025
|
+
npx create-spatius-app my-app --stack cloudflare-agora
|
|
16522
17026
|
npx create-spatius-app my-app --package-manager pnpm --python-package-manager uv
|
|
16523
17027
|
npx create-spatius-app my-app --no-setup
|
|
16524
17028
|
npx create-spatius-app my-app --setup --interactive
|
|
@@ -16529,7 +17033,7 @@ Examples:
|
|
|
16529
17033
|
async (projectDirectoryArgument, rawOptions) => runCreateCommand(version, projectDirectoryArgument, rawOptions)
|
|
16530
17034
|
);
|
|
16531
17035
|
program2.command("setup").description(
|
|
16532
|
-
"securely configure local
|
|
17036
|
+
"securely configure local provider credentials for an existing generated project"
|
|
16533
17037
|
).argument("[project-directory]", "generated project to configure", ".").addOption(new Option("--interactive", "allow browser and secret prompts")).addOption(new Option("--no-interactive", "disable interactive prompts")).option("--debug", "write safe Spatius request diagnostics to stderr").addHelpText(
|
|
16534
17038
|
"after",
|
|
16535
17039
|
`
|
|
@@ -16567,4 +17071,3 @@ try {
|
|
|
16567
17071
|
}
|
|
16568
17072
|
process5.exitCode = normalizedError.exitCode;
|
|
16569
17073
|
}
|
|
16570
|
-
//# sourceMappingURL=cli.js.map
|