@scenar/cli 0.1.20 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/example-bundle/assets/index-BLQ14SEo.js +147 -0
  2. package/example-bundle/assets/logo-f-5LjVqJ.png +0 -0
  3. package/example-bundle/assets/style-Dnzx_jUO.css +1 -0
  4. package/example-bundle/index.html +13 -0
  5. package/example-bundle/pack-manifest.json +36 -0
  6. package/example-bundle/scenario.json +9 -0
  7. package/package.json +17 -5
  8. package/src/__tests__/pack-generate-embed-entry.test.ts +59 -6
  9. package/src/__tests__/publish-command.test.ts +46 -0
  10. package/src/__tests__/publish-flow.test.ts +220 -0
  11. package/src/__tests__/read-viewport.test.ts +45 -0
  12. package/src/__tests__/run-publish.test.ts +92 -0
  13. package/src/__tests__/serve-command.test.ts +44 -0
  14. package/src/__tests__/serve-request-path.test.ts +67 -0
  15. package/src/__tests__/serve-static-server.test.ts +88 -0
  16. package/src/__tests__/try-command.test.ts +21 -0
  17. package/src/api.d.ts +31 -0
  18. package/src/api.d.ts.map +1 -0
  19. package/src/api.js +28 -0
  20. package/src/api.js.map +1 -0
  21. package/src/api.ts +55 -0
  22. package/src/bundle/read-viewport.d.ts +23 -0
  23. package/src/bundle/read-viewport.d.ts.map +1 -0
  24. package/src/bundle/read-viewport.js +25 -0
  25. package/src/bundle/read-viewport.js.map +1 -0
  26. package/src/bundle/read-viewport.ts +36 -0
  27. package/src/commands/deploy.d.ts.map +1 -1
  28. package/src/commands/deploy.js +8 -24
  29. package/src/commands/deploy.js.map +1 -1
  30. package/src/commands/deploy.ts +10 -28
  31. package/src/commands/narrate.d.ts.map +1 -1
  32. package/src/commands/narrate.js +39 -170
  33. package/src/commands/narrate.js.map +1 -1
  34. package/src/commands/narrate.ts +48 -289
  35. package/src/commands/pack.d.ts.map +1 -1
  36. package/src/commands/pack.js +12 -112
  37. package/src/commands/pack.js.map +1 -1
  38. package/src/commands/pack.ts +12 -128
  39. package/src/commands/publish.d.ts +3 -0
  40. package/src/commands/publish.d.ts.map +1 -0
  41. package/src/commands/publish.js +64 -0
  42. package/src/commands/publish.js.map +1 -0
  43. package/src/commands/publish.ts +86 -0
  44. package/src/commands/render.d.ts.map +1 -1
  45. package/src/commands/render.js +12 -194
  46. package/src/commands/render.js.map +1 -1
  47. package/src/commands/render.ts +19 -286
  48. package/src/commands/serve.d.ts +3 -0
  49. package/src/commands/serve.d.ts.map +1 -0
  50. package/src/commands/serve.js +91 -0
  51. package/src/commands/serve.js.map +1 -0
  52. package/src/commands/serve.ts +112 -0
  53. package/src/commands/try.d.ts +3 -0
  54. package/src/commands/try.d.ts.map +1 -0
  55. package/src/commands/try.js +99 -0
  56. package/src/commands/try.js.map +1 -0
  57. package/src/commands/try.ts +122 -0
  58. package/src/index.d.ts.map +1 -1
  59. package/src/index.js +6 -0
  60. package/src/index.js.map +1 -1
  61. package/src/index.ts +6 -0
  62. package/src/narrate/run-narrate.d.ts +43 -0
  63. package/src/narrate/run-narrate.d.ts.map +1 -0
  64. package/src/narrate/run-narrate.js +174 -0
  65. package/src/narrate/run-narrate.js.map +1 -0
  66. package/src/narrate/run-narrate.ts +298 -0
  67. package/src/pack/generate-embed-entry.d.ts +11 -0
  68. package/src/pack/generate-embed-entry.d.ts.map +1 -1
  69. package/src/pack/generate-embed-entry.js +81 -10
  70. package/src/pack/generate-embed-entry.js.map +1 -1
  71. package/src/pack/generate-embed-entry.ts +81 -10
  72. package/src/pack/run-pack.d.ts +34 -0
  73. package/src/pack/run-pack.d.ts.map +1 -0
  74. package/src/pack/run-pack.js +117 -0
  75. package/src/pack/run-pack.js.map +1 -0
  76. package/src/pack/run-pack.ts +163 -0
  77. package/src/publish/github.d.ts +11 -0
  78. package/src/publish/github.d.ts.map +1 -0
  79. package/src/publish/github.js +197 -0
  80. package/src/publish/github.js.map +1 -0
  81. package/src/publish/github.ts +216 -0
  82. package/src/publish/publish-flow.d.ts +110 -0
  83. package/src/publish/publish-flow.d.ts.map +1 -0
  84. package/src/publish/publish-flow.js +117 -0
  85. package/src/publish/publish-flow.js.map +1 -0
  86. package/src/publish/publish-flow.ts +199 -0
  87. package/src/publish/run-publish.d.ts +41 -0
  88. package/src/publish/run-publish.d.ts.map +1 -0
  89. package/src/publish/run-publish.js +77 -0
  90. package/src/publish/run-publish.js.map +1 -0
  91. package/src/publish/run-publish.ts +131 -0
  92. package/src/render/run-render.d.ts +31 -0
  93. package/src/render/run-render.d.ts.map +1 -0
  94. package/src/render/run-render.js +196 -0
  95. package/src/render/run-render.js.map +1 -0
  96. package/src/render/run-render.ts +287 -0
  97. package/src/serve/request-path.d.ts +23 -0
  98. package/src/serve/request-path.d.ts.map +1 -0
  99. package/src/serve/request-path.js +52 -0
  100. package/src/serve/request-path.js.map +1 -0
  101. package/src/serve/request-path.ts +55 -0
  102. package/src/serve/run-serve.d.ts +31 -0
  103. package/src/serve/run-serve.d.ts.map +1 -0
  104. package/src/serve/run-serve.js +35 -0
  105. package/src/serve/run-serve.js.map +1 -0
  106. package/src/serve/run-serve.ts +68 -0
  107. package/src/serve/static-server.d.ts +38 -0
  108. package/src/serve/static-server.d.ts.map +1 -0
  109. package/src/serve/static-server.js +91 -0
  110. package/src/serve/static-server.js.map +1 -0
  111. package/src/serve/static-server.ts +118 -0
  112. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serve.js","sourceRoot":"","sources":["../../../src/commands/serve.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,112 @@
1
+ import { spawn } from "node:child_process";
2
+ import { platform } from "node:process";
3
+ import { Command } from "commander";
4
+ import { runServe } from "../serve/run-serve.js";
5
+ import type { BundleServerHandle } from "../serve/static-server.js";
6
+
7
+ /** Vite's preview port — the conventional "serve a built site" port. */
8
+ const DEFAULT_PORT = 4173;
9
+
10
+ /** Bind to loopback by default; a preview is a single-developer tool. */
11
+ const DEFAULT_HOST = "localhost";
12
+
13
+ interface ServeOptions {
14
+ port?: string;
15
+ host?: string;
16
+ open?: boolean;
17
+ }
18
+
19
+ export function registerServeCommand(program: Command): void {
20
+ program
21
+ .command("serve")
22
+ .description(
23
+ "Serve a packed bundle locally and print its embed URL.\n\n" +
24
+ "Takes a bundle directory produced by `scenar pack` and serves it as a\n" +
25
+ "static site on http://localhost:4173 (override with --port/--host).\n" +
26
+ "Prints the URL plus a ready-to-paste responsive <iframe> snippet, then\n" +
27
+ "stays running until you press Ctrl+C.\n\n" +
28
+ "This is the zero-infrastructure way to view an embed: no backend, no\n" +
29
+ "account. For a permanent public URL, use `scenar publish` (GitHub\n" +
30
+ "Pages) or `scenar deploy` (Scenar Cloud).",
31
+ )
32
+ .argument("<bundleDir>", "path to a packed bundle directory (from scenar pack)")
33
+ .option("--port <number>", `port to listen on (default: ${DEFAULT_PORT})`, String(DEFAULT_PORT))
34
+ .option("--host <host>", `host/interface to bind (default: ${DEFAULT_HOST})`, DEFAULT_HOST)
35
+ .option("--open", "open the embed in your default browser")
36
+ .action(async (bundleDir: string, options: ServeOptions) => {
37
+ try {
38
+ const port = Number(options.port);
39
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
40
+ throw new Error(`invalid --port "${options.port}"; expected an integer between 0 and 65535.`);
41
+ }
42
+ const host = options.host ?? DEFAULT_HOST;
43
+
44
+ let result;
45
+ try {
46
+ result = await runServe({ bundleDir, port, host });
47
+ } catch (error) {
48
+ if (isAddrInUse(error)) {
49
+ throw new Error(
50
+ `port ${port} is already in use. Stop the other process or pass --port <number>.`,
51
+ );
52
+ }
53
+ throw error;
54
+ }
55
+
56
+ process.stderr.write(`\n\x1b[32m✓\x1b[0m Serving at ${result.url}\n`);
57
+ if (!result.recordedViewport) {
58
+ process.stderr.write(
59
+ " Note: no recorded viewport; snippet uses the default " +
60
+ `${result.viewport.width}x${result.viewport.height}. Re-pack with the current CLI ` +
61
+ "to embed at the exact aspect ratio.\n",
62
+ );
63
+ }
64
+ process.stderr.write("\n Embed snippet (paste into any page):\n\n");
65
+ process.stderr.write(`${indent(result.snippet)}\n`);
66
+ process.stderr.write("\n Press Ctrl+C to stop.\n");
67
+
68
+ if (options.open) {
69
+ openInBrowser(result.url);
70
+ }
71
+
72
+ await runUntilInterrupted(result.handle);
73
+ } catch (error) {
74
+ const msg = error instanceof Error ? error.message : String(error);
75
+ process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
76
+ process.exitCode = 1;
77
+ }
78
+ });
79
+ }
80
+
81
+ /** Block until SIGINT/SIGTERM, then close the server cleanly. */
82
+ function runUntilInterrupted(handle: BundleServerHandle): Promise<void> {
83
+ return new Promise<void>((resolvePromise) => {
84
+ const shutdown = () => {
85
+ process.stderr.write("\nStopping...\n");
86
+ void handle.close().finally(() => resolvePromise());
87
+ };
88
+ process.once("SIGINT", shutdown);
89
+ process.once("SIGTERM", shutdown);
90
+ });
91
+ }
92
+
93
+ /** Best-effort: launch the OS default browser. Failures are silent (non-fatal). */
94
+ function openInBrowser(url: string): void {
95
+ const command = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
96
+ try {
97
+ const child = spawn(command, [url], { stdio: "ignore", detached: true, shell: platform === "win32" });
98
+ child.on("error", () => {});
99
+ child.unref();
100
+ } catch {
101
+ // Opening a browser is a convenience; never fail the command over it.
102
+ }
103
+ }
104
+
105
+ function isAddrInUse(error: unknown): boolean {
106
+ return typeof error === "object" && error !== null && (error as { code?: string }).code === "EADDRINUSE";
107
+ }
108
+
109
+ /** Indent every line by two spaces (for nesting a block under a heading). */
110
+ function indent(block: string): string {
111
+ return block.replace(/^/gm, " ");
112
+ }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerTryCommand(program: Command): void;
3
+ //# sourceMappingURL=try.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"try.d.ts","sourceRoot":"","sources":["../../../src/commands/try.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAwBpC,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA2DzD"}
@@ -0,0 +1,99 @@
1
+ import { spawn } from "node:child_process";
2
+ import { platform } from "node:process";
3
+ import { fileURLToPath } from "node:url";
4
+ import { dirname, resolve } from "node:path";
5
+ import { stat } from "node:fs/promises";
6
+ import { runServe } from "../serve/run-serve.js";
7
+ /** Vite's preview port — the conventional "serve a built site" port. */
8
+ const DEFAULT_PORT = 4173;
9
+ /** Bind to loopback by default; a preview is a single-developer tool. */
10
+ const DEFAULT_HOST = "localhost";
11
+ /**
12
+ * The example bundle that ships with the CLI. It's generated at build time
13
+ * (`pack examples/welcome-tour --out dist/example-bundle`, see package.json) and
14
+ * published as part of dist/, so it's present for npx consumers. This module
15
+ * compiles to dist/src/commands/try.js, so the bundle sits two levels up.
16
+ */
17
+ const EXAMPLE_BUNDLE_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "../../example-bundle");
18
+ export function registerTryCommand(program) {
19
+ program
20
+ .command("try")
21
+ .description("Serve the bundled welcome-tour example locally — no app required.\n\n" +
22
+ "Boots the example embed that ships with the CLI on\n" +
23
+ "http://localhost:4173 (override with --port/--host) and prints a\n" +
24
+ "ready-to-paste <iframe> snippet, then stays running until Ctrl+C.\n\n" +
25
+ "This is the fastest way to see what a Scenar embed looks like before\n" +
26
+ "wiring up your own app. To build a tour from your components, see\n" +
27
+ "`scenar preview`; to host one, see `scenar publish`.")
28
+ .option("--port <number>", `port to listen on (default: ${DEFAULT_PORT})`, String(DEFAULT_PORT))
29
+ .option("--host <host>", `host/interface to bind (default: ${DEFAULT_HOST})`, DEFAULT_HOST)
30
+ .option("--open", "open the embed in your default browser")
31
+ .action(async (options) => {
32
+ try {
33
+ const port = Number(options.port);
34
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
35
+ throw new Error(`invalid --port "${options.port}"; expected an integer between 0 and 65535.`);
36
+ }
37
+ const host = options.host ?? DEFAULT_HOST;
38
+ const bundle = await stat(EXAMPLE_BUNDLE_DIR).catch(() => null);
39
+ if (!bundle || !bundle.isDirectory()) {
40
+ throw new Error("the bundled example is missing. If you installed @scenar/cli from npm, " +
41
+ "reinstall it; if you're running from source, run `pnpm build` first.");
42
+ }
43
+ let result;
44
+ try {
45
+ result = await runServe({ bundleDir: EXAMPLE_BUNDLE_DIR, port, host });
46
+ }
47
+ catch (error) {
48
+ if (isAddrInUse(error)) {
49
+ throw new Error(`port ${port} is already in use. Stop the other process or pass --port <number>.`);
50
+ }
51
+ throw error;
52
+ }
53
+ process.stderr.write(`\n\x1b[32m✓\x1b[0m Serving the welcome-tour example at ${result.url}\n`);
54
+ process.stderr.write("\n Embed snippet (paste into any page):\n\n");
55
+ process.stderr.write(`${indent(result.snippet)}\n`);
56
+ process.stderr.write("\n Press Ctrl+C to stop.\n");
57
+ if (options.open) {
58
+ openInBrowser(result.url);
59
+ }
60
+ await runUntilInterrupted(result.handle);
61
+ }
62
+ catch (error) {
63
+ const msg = error instanceof Error ? error.message : String(error);
64
+ process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
65
+ process.exitCode = 1;
66
+ }
67
+ });
68
+ }
69
+ /** Block until SIGINT/SIGTERM, then close the server cleanly. */
70
+ function runUntilInterrupted(handle) {
71
+ return new Promise((resolvePromise) => {
72
+ const shutdown = () => {
73
+ process.stderr.write("\nStopping...\n");
74
+ void handle.close().finally(() => resolvePromise());
75
+ };
76
+ process.once("SIGINT", shutdown);
77
+ process.once("SIGTERM", shutdown);
78
+ });
79
+ }
80
+ /** Best-effort: launch the OS default browser. Failures are silent (non-fatal). */
81
+ function openInBrowser(url) {
82
+ const command = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
83
+ try {
84
+ const child = spawn(command, [url], { stdio: "ignore", detached: true, shell: platform === "win32" });
85
+ child.on("error", () => { });
86
+ child.unref();
87
+ }
88
+ catch {
89
+ // Opening a browser is a convenience; never fail the command over it.
90
+ }
91
+ }
92
+ function isAddrInUse(error) {
93
+ return typeof error === "object" && error !== null && error.code === "EADDRINUSE";
94
+ }
95
+ /** Indent every line by two spaces (for nesting a block under a heading). */
96
+ function indent(block) {
97
+ return block.replace(/^/gm, " ");
98
+ }
99
+ //# sourceMappingURL=try.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"try.js","sourceRoot":"","sources":["../../../src/commands/try.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ import { spawn } from "node:child_process";
2
+ import { platform } from "node:process";
3
+ import { fileURLToPath } from "node:url";
4
+ import { dirname, resolve } from "node:path";
5
+ import { stat } from "node:fs/promises";
6
+ import { Command } from "commander";
7
+ import { runServe } from "../serve/run-serve.js";
8
+ import type { BundleServerHandle } from "../serve/static-server.js";
9
+
10
+ /** Vite's preview port — the conventional "serve a built site" port. */
11
+ const DEFAULT_PORT = 4173;
12
+
13
+ /** Bind to loopback by default; a preview is a single-developer tool. */
14
+ const DEFAULT_HOST = "localhost";
15
+
16
+ /**
17
+ * The example bundle that ships with the CLI. It's generated at build time
18
+ * (`pack examples/welcome-tour --out dist/example-bundle`, see package.json) and
19
+ * published as part of dist/, so it's present for npx consumers. This module
20
+ * compiles to dist/src/commands/try.js, so the bundle sits two levels up.
21
+ */
22
+ const EXAMPLE_BUNDLE_DIR = resolve(dirname(fileURLToPath(import.meta.url)), "../../example-bundle");
23
+
24
+ interface TryOptions {
25
+ port?: string;
26
+ host?: string;
27
+ open?: boolean;
28
+ }
29
+
30
+ export function registerTryCommand(program: Command): void {
31
+ program
32
+ .command("try")
33
+ .description(
34
+ "Serve the bundled welcome-tour example locally — no app required.\n\n" +
35
+ "Boots the example embed that ships with the CLI on\n" +
36
+ "http://localhost:4173 (override with --port/--host) and prints a\n" +
37
+ "ready-to-paste <iframe> snippet, then stays running until Ctrl+C.\n\n" +
38
+ "This is the fastest way to see what a Scenar embed looks like before\n" +
39
+ "wiring up your own app. To build a tour from your components, see\n" +
40
+ "`scenar preview`; to host one, see `scenar publish`.",
41
+ )
42
+ .option("--port <number>", `port to listen on (default: ${DEFAULT_PORT})`, String(DEFAULT_PORT))
43
+ .option("--host <host>", `host/interface to bind (default: ${DEFAULT_HOST})`, DEFAULT_HOST)
44
+ .option("--open", "open the embed in your default browser")
45
+ .action(async (options: TryOptions) => {
46
+ try {
47
+ const port = Number(options.port);
48
+ if (!Number.isInteger(port) || port < 0 || port > 65535) {
49
+ throw new Error(`invalid --port "${options.port}"; expected an integer between 0 and 65535.`);
50
+ }
51
+ const host = options.host ?? DEFAULT_HOST;
52
+
53
+ const bundle = await stat(EXAMPLE_BUNDLE_DIR).catch(() => null);
54
+ if (!bundle || !bundle.isDirectory()) {
55
+ throw new Error(
56
+ "the bundled example is missing. If you installed @scenar/cli from npm, " +
57
+ "reinstall it; if you're running from source, run `pnpm build` first.",
58
+ );
59
+ }
60
+
61
+ let result;
62
+ try {
63
+ result = await runServe({ bundleDir: EXAMPLE_BUNDLE_DIR, port, host });
64
+ } catch (error) {
65
+ if (isAddrInUse(error)) {
66
+ throw new Error(
67
+ `port ${port} is already in use. Stop the other process or pass --port <number>.`,
68
+ );
69
+ }
70
+ throw error;
71
+ }
72
+
73
+ process.stderr.write(`\n\x1b[32m✓\x1b[0m Serving the welcome-tour example at ${result.url}\n`);
74
+ process.stderr.write("\n Embed snippet (paste into any page):\n\n");
75
+ process.stderr.write(`${indent(result.snippet)}\n`);
76
+ process.stderr.write("\n Press Ctrl+C to stop.\n");
77
+
78
+ if (options.open) {
79
+ openInBrowser(result.url);
80
+ }
81
+
82
+ await runUntilInterrupted(result.handle);
83
+ } catch (error) {
84
+ const msg = error instanceof Error ? error.message : String(error);
85
+ process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
86
+ process.exitCode = 1;
87
+ }
88
+ });
89
+ }
90
+
91
+ /** Block until SIGINT/SIGTERM, then close the server cleanly. */
92
+ function runUntilInterrupted(handle: BundleServerHandle): Promise<void> {
93
+ return new Promise<void>((resolvePromise) => {
94
+ const shutdown = () => {
95
+ process.stderr.write("\nStopping...\n");
96
+ void handle.close().finally(() => resolvePromise());
97
+ };
98
+ process.once("SIGINT", shutdown);
99
+ process.once("SIGTERM", shutdown);
100
+ });
101
+ }
102
+
103
+ /** Best-effort: launch the OS default browser. Failures are silent (non-fatal). */
104
+ function openInBrowser(url: string): void {
105
+ const command = platform === "darwin" ? "open" : platform === "win32" ? "start" : "xdg-open";
106
+ try {
107
+ const child = spawn(command, [url], { stdio: "ignore", detached: true, shell: platform === "win32" });
108
+ child.on("error", () => {});
109
+ child.unref();
110
+ } catch {
111
+ // Opening a browser is a convenience; never fail the command over it.
112
+ }
113
+ }
114
+
115
+ function isAddrInUse(error: unknown): boolean {
116
+ return typeof error === "object" && error !== null && (error as { code?: string }).code === "EADDRINUSE";
117
+ }
118
+
119
+ /** Indent every line by two spaces (for nesting a block under a heading). */
120
+ function indent(block: string): string {
121
+ return block.replace(/^/gm, " ");
122
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAQpC,wBAAgB,aAAa,IAAI,OAAO,CAgBvC;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAGxC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,aAAa,IAAI,OAAO,CAmBvC;AAED,wBAAgB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAGxC"}
package/src/index.js CHANGED
@@ -4,6 +4,9 @@ import { registerNarrateCommand } from "./commands/narrate.js";
4
4
  import { registerPreviewCommand } from "./commands/preview.js";
5
5
  import { registerRenderCommand } from "./commands/render.js";
6
6
  import { registerPackCommand } from "./commands/pack.js";
7
+ import { registerServeCommand } from "./commands/serve.js";
8
+ import { registerTryCommand } from "./commands/try.js";
9
+ import { registerPublishCommand } from "./commands/publish.js";
7
10
  import { registerDeployCommand } from "./commands/deploy.js";
8
11
  export function createProgram() {
9
12
  const program = new Command();
@@ -16,6 +19,9 @@ export function createProgram() {
16
19
  registerRenderCommand(program);
17
20
  registerPreviewCommand(program);
18
21
  registerPackCommand(program);
22
+ registerServeCommand(program);
23
+ registerTryCommand(program);
24
+ registerPublishCommand(program);
19
25
  registerDeployCommand(program);
20
26
  return program;
21
27
  }
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,QAAQ,CAAC;SACd,WAAW,CAAC,8FAA8F,CAAC;SAC3G,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjC,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAE/B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAAc;IAChC,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,QAAQ,CAAC;SACd,WAAW,CAAC,8FAA8F,CAAC;SAC3G,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,uBAAuB,CAAC,OAAO,CAAC,CAAC;IACjC,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAC/B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC7B,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAC9B,kBAAkB,CAAC,OAAO,CAAC,CAAC;IAC5B,sBAAsB,CAAC,OAAO,CAAC,CAAC;IAChC,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAE/B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,IAAc;IAChC,MAAM,OAAO,GAAG,aAAa,EAAE,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACtB,CAAC"}
package/src/index.ts CHANGED
@@ -4,6 +4,9 @@ import { registerNarrateCommand } from "./commands/narrate.js";
4
4
  import { registerPreviewCommand } from "./commands/preview.js";
5
5
  import { registerRenderCommand } from "./commands/render.js";
6
6
  import { registerPackCommand } from "./commands/pack.js";
7
+ import { registerServeCommand } from "./commands/serve.js";
8
+ import { registerTryCommand } from "./commands/try.js";
9
+ import { registerPublishCommand } from "./commands/publish.js";
7
10
  import { registerDeployCommand } from "./commands/deploy.js";
8
11
 
9
12
  export function createProgram(): Command {
@@ -19,6 +22,9 @@ export function createProgram(): Command {
19
22
  registerRenderCommand(program);
20
23
  registerPreviewCommand(program);
21
24
  registerPackCommand(program);
25
+ registerServeCommand(program);
26
+ registerTryCommand(program);
27
+ registerPublishCommand(program);
22
28
  registerDeployCommand(program);
23
29
 
24
30
  return program;
@@ -0,0 +1,43 @@
1
+ /** Options for {@link runNarrate}. Paths may be relative; resolved here. */
2
+ export interface RunNarrateOptions {
3
+ /** A scenario YAML file, a .ts steps file, or a directory of scenarios. */
4
+ readonly target: string;
5
+ /** TTS provider name: echogarden, edge-tts, or openai (default echogarden). */
6
+ readonly tts?: string;
7
+ /** Output directory for audio (default: alongside the scenario). */
8
+ readonly out?: string;
9
+ /** Voice name (provider-specific). */
10
+ readonly voice?: string;
11
+ /** URL path prefix for the `src` fields written into the manifest. */
12
+ readonly baseUrl?: string;
13
+ /** Progress sink for per-step/per-scenario messages. */
14
+ readonly onLog?: (message: string) => void;
15
+ }
16
+ /** Per-scenario narration outcome. */
17
+ export interface NarratedScenario {
18
+ readonly id: string;
19
+ readonly generated: number;
20
+ readonly cached: number;
21
+ /** True when the scenario had no narration text (nothing to synthesize). */
22
+ readonly skipped: boolean;
23
+ }
24
+ /** Aggregate result of a narrate run. */
25
+ export interface NarrateResult {
26
+ readonly mode: "file" | "directory";
27
+ readonly scenarios: NarratedScenario[];
28
+ readonly totalGenerated: number;
29
+ readonly totalCached: number;
30
+ readonly totalSkipped: number;
31
+ readonly errors: Array<{
32
+ id: string;
33
+ message: string;
34
+ }>;
35
+ }
36
+ /**
37
+ * Generate narration audio for a scenario file or a directory of scenarios —
38
+ * the pure orchestration behind `scenar narrate`. Resolves the TTS provider,
39
+ * synthesizes per-step audio (incremental via a content-hash cache), writes each
40
+ * scenario's manifest, and returns aggregate stats. No process/exit coupling.
41
+ */
42
+ export declare function runNarrate(options: RunNarrateOptions): Promise<NarrateResult>;
43
+ //# sourceMappingURL=run-narrate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-narrate.d.ts","sourceRoot":"","sources":["../../../src/narrate/run-narrate.ts"],"names":[],"mappings":"AAkBA,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,2EAA2E;IAC3E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,sCAAsC;IACtC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,sEAAsE;IACtE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,wDAAwD;IACxD,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAC5C;AAED,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,4EAA4E;IAC5E,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED,yCAAyC;AACzC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,gBAAgB,EAAE,CAAC;IACvC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACzD;AAkID;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CASnF"}
@@ -0,0 +1,174 @@
1
+ import { mkdir, writeFile, stat } from "node:fs/promises";
2
+ import { join, resolve, extname, basename, dirname } from "node:path";
3
+ import { loadScenarioYaml } from "../util/load-yaml.js";
4
+ import { loadStepsFromTs } from "../util/load-ts.js";
5
+ import { discoverScenarios } from "../util/discover-scenarios.js";
6
+ import { computeHash, loadCache, saveCache, fileExists, isCached, getCachedDuration, buildCacheFile, } from "../util/narration-cache.js";
7
+ import { validateScenario } from "../validate/scenario-validator.js";
8
+ import { resolveProvider } from "../tts/resolve-provider.js";
9
+ function extractNarratedSteps(steps) {
10
+ const result = [];
11
+ for (let i = 0; i < steps.length; i++) {
12
+ const text = steps[i].narration ?? steps[i].narrationText;
13
+ if (typeof text === "string" && text.length > 0) {
14
+ result.push({ index: i, text });
15
+ }
16
+ }
17
+ return result;
18
+ }
19
+ function buildRuntimeManifest(totalSteps, entries) {
20
+ const steps = Array.from({ length: totalSteps }, () => null);
21
+ for (const [idx, entry] of entries) {
22
+ steps[idx] = entry;
23
+ }
24
+ return { steps };
25
+ }
26
+ async function loadStepsFromFile(filePath) {
27
+ const ext = extname(filePath).toLowerCase();
28
+ if (ext === ".ts" || ext === ".tsx") {
29
+ const steps = await loadStepsFromTs(filePath);
30
+ return { steps, totalSteps: steps.length };
31
+ }
32
+ const scenario = await loadScenarioYaml(filePath);
33
+ const validation = validateScenario(scenario);
34
+ if (!validation.valid) {
35
+ throw new Error(`Scenario has ${validation.errors.length} error(s). Run 'scenar validate ${filePath}' for details.`);
36
+ }
37
+ const steps = scenario["steps"];
38
+ if (!steps) {
39
+ throw new Error("No steps found in scenario.");
40
+ }
41
+ return { steps: steps, totalSteps: steps.length };
42
+ }
43
+ async function generateNarration(opts) {
44
+ const { scenarioId, narratedSteps, totalSteps, outDir, voice, baseUrl, provider, onLog } = opts;
45
+ await mkdir(outDir, { recursive: true });
46
+ const existingCache = await loadCache(outDir);
47
+ const stats = { generated: 0, cached: 0 };
48
+ const manifestEntries = new Map();
49
+ const cacheEntries = new Map();
50
+ for (const step of narratedSteps) {
51
+ const hash = computeHash(step.text, voice);
52
+ const fileName = `step-${step.index}.mp3`;
53
+ const mp3Path = join(outDir, fileName);
54
+ const srcPrefix = baseUrl ? `${baseUrl.replace(/\/$/, "")}/${scenarioId}` : `.`;
55
+ const src = `${srcPrefix}/${fileName}`;
56
+ if (isCached(existingCache, step.index, hash, voice) && (await fileExists(mp3Path))) {
57
+ const durationMs = getCachedDuration(existingCache, step.index);
58
+ manifestEntries.set(step.index, { src, durationMs });
59
+ cacheEntries.set(step.index, { hash, durationMs });
60
+ stats.cached++;
61
+ onLog(` step ${step.index}: cached`);
62
+ continue;
63
+ }
64
+ onLog(` step ${step.index}: generating...`);
65
+ const result = await provider.synthesize(step.text, { voice: voice || undefined });
66
+ await writeFile(mp3Path, result.audio);
67
+ manifestEntries.set(step.index, { src, durationMs: result.durationMs });
68
+ cacheEntries.set(step.index, { hash, durationMs: result.durationMs });
69
+ stats.generated++;
70
+ onLog(` step ${step.index}: ${result.durationMs}ms (${result.audio.length} bytes)`);
71
+ }
72
+ const manifest = buildRuntimeManifest(totalSteps, manifestEntries);
73
+ await writeFile(join(outDir, "manifest.json"), JSON.stringify(manifest, null, 2) + "\n");
74
+ const cache = buildCacheFile(voice, totalSteps, cacheEntries);
75
+ await saveCache(outDir, cache);
76
+ onLog(`\x1b[32m+\x1b[0m ${scenarioId}: ${stats.generated} generated, ${stats.cached} cached`);
77
+ return stats;
78
+ }
79
+ /**
80
+ * Generate narration audio for a scenario file or a directory of scenarios —
81
+ * the pure orchestration behind `scenar narrate`. Resolves the TTS provider,
82
+ * synthesizes per-step audio (incremental via a content-hash cache), writes each
83
+ * scenario's manifest, and returns aggregate stats. No process/exit coupling.
84
+ */
85
+ export async function runNarrate(options) {
86
+ const onLog = options.onLog ?? (() => { });
87
+ const provider = await resolveProvider(options.tts ?? "echogarden");
88
+ const resolved = resolve(options.target);
89
+ const info = await stat(resolved);
90
+ return info.isDirectory()
91
+ ? narrateDirectory(resolved, options, provider, onLog)
92
+ : narrateSingleFile(resolved, options, provider, onLog);
93
+ }
94
+ async function narrateSingleFile(filePath, options, provider, onLog) {
95
+ const { steps, totalSteps } = await loadStepsFromFile(filePath);
96
+ const narratedSteps = extractNarratedSteps(steps);
97
+ const id = basename(dirname(filePath));
98
+ if (narratedSteps.length === 0) {
99
+ return {
100
+ mode: "file",
101
+ scenarios: [{ id, generated: 0, cached: 0, skipped: true }],
102
+ totalGenerated: 0,
103
+ totalCached: 0,
104
+ totalSkipped: 1,
105
+ errors: [],
106
+ };
107
+ }
108
+ const outDir = options.out ?? join(dirname(filePath), "narration");
109
+ const stats = await generateNarration({
110
+ scenarioId: id,
111
+ narratedSteps,
112
+ totalSteps,
113
+ outDir,
114
+ voice: options.voice ?? "",
115
+ baseUrl: options.baseUrl,
116
+ provider,
117
+ onLog,
118
+ });
119
+ return {
120
+ mode: "file",
121
+ scenarios: [{ id, generated: stats.generated, cached: stats.cached, skipped: false }],
122
+ totalGenerated: stats.generated,
123
+ totalCached: stats.cached,
124
+ totalSkipped: 0,
125
+ errors: [],
126
+ };
127
+ }
128
+ async function narrateDirectory(dirPath, options, provider, onLog) {
129
+ const scenarios = await discoverScenarios(dirPath);
130
+ const result = [];
131
+ const errors = [];
132
+ let totalGenerated = 0;
133
+ let totalCached = 0;
134
+ let totalSkipped = 0;
135
+ if (scenarios.length > 0) {
136
+ onLog(`Discovered ${scenarios.length} scenario(s)`);
137
+ }
138
+ for (const scenario of scenarios) {
139
+ onLog(` ${scenario.id}`);
140
+ try {
141
+ const steps = await loadStepsFromTs(scenario.stepsPath);
142
+ const narratedSteps = extractNarratedSteps(steps);
143
+ if (narratedSteps.length === 0) {
144
+ onLog(" (no narration)");
145
+ totalSkipped++;
146
+ result.push({ id: scenario.id, generated: 0, cached: 0, skipped: true });
147
+ continue;
148
+ }
149
+ const outDir = options.out
150
+ ? join(options.out, scenario.id)
151
+ : join(dirPath, scenario.id, "narration");
152
+ const stats = await generateNarration({
153
+ scenarioId: scenario.id,
154
+ narratedSteps,
155
+ totalSteps: steps.length,
156
+ outDir,
157
+ voice: options.voice ?? "",
158
+ baseUrl: options.baseUrl,
159
+ provider,
160
+ onLog,
161
+ });
162
+ totalGenerated += stats.generated;
163
+ totalCached += stats.cached;
164
+ result.push({ id: scenario.id, generated: stats.generated, cached: stats.cached, skipped: false });
165
+ }
166
+ catch (error) {
167
+ const message = error instanceof Error ? error.message : String(error);
168
+ onLog(` \x1b[31mfailed\x1b[0m: ${message}`);
169
+ errors.push({ id: scenario.id, message });
170
+ }
171
+ }
172
+ return { mode: "directory", scenarios: result, totalGenerated, totalCached, totalSkipped, errors };
173
+ }
174
+ //# sourceMappingURL=run-narrate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-narrate.js","sourceRoot":"","sources":["../../../src/narrate/run-narrate.ts"],"names":[],"mappings":"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"}