@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
@@ -1,13 +1,5 @@
1
- import { resolve, join, basename } from "node:path";
2
- import { stat, mkdir, writeFile, rm, readdir, copyFile, access } from "node:fs/promises";
3
- import { detectRenderExport } from "../render/detect-render-export.js";
4
- import { resolveProvidersPath } from "../render/resolve-providers.js";
5
- import { generateEmbedEntry, generateEmbedHtml } from "../pack/generate-embed-entry.js";
6
- import { runViteBuild } from "../pack/build.js";
7
- import { buildPackManifest, writePackManifest, writeScenarioJson, verifyManifestFilesExist, } from "../pack/pack-manifest.js";
8
1
  import { DEFAULT_VIEWPORT } from "../pack/viewport.js";
9
- /** Generator version stamped into scenario.json (kept in sync with the CLI). */
10
- const PACK_GENERATOR_VERSION = "0.0.1";
2
+ import { runPack } from "../pack/run-pack.js";
11
3
  /** Default canonical viewport width (matches DemoViewport's default). */
12
4
  const DEFAULT_WIDTH = DEFAULT_VIEWPORT.width;
13
5
  /** Default shell height for the embed. */
@@ -20,7 +12,7 @@ export function registerPackCommand(program) {
20
12
  "(steps.ts + an index.tsx that exports renderStep, plus an\n" +
21
13
  "optional .scenar/providers.tsx and narration/). Produces a static\n" +
22
14
  "bundle that boots ScenarioPlayer in the browser, ready for\n" +
23
- "`scenar deploy`.\n\n" +
15
+ "`scenar serve`, `scenar publish`, or `scenar deploy`.\n\n" +
24
16
  "The bundle contains index.html, hashed JS/CSS, a scenario.json\n" +
25
17
  "descriptor, and a pack-manifest.json listing every file with its\n" +
26
18
  "lowercase-hex sha256 and content type. Output stays within the\n" +
@@ -34,118 +26,26 @@ export function registerPackCommand(program) {
34
26
  .option("--shell-height <number>", `shell height in px (default: ${DEFAULT_SHELL_HEIGHT})`, String(DEFAULT_SHELL_HEIGHT))
35
27
  .option("--keep-temp", "keep the generated entry directory for debugging")
36
28
  .action(async (dir, options) => {
37
- const scenarioDir = resolve(dir);
38
- let info;
39
29
  try {
40
- info = await stat(scenarioDir);
41
- }
42
- catch {
43
- process.stderr.write(`\x1b[31mError:\x1b[0m ${dir} does not exist.\n`);
44
- process.exitCode = 1;
45
- return;
46
- }
47
- if (!info.isDirectory()) {
48
- process.stderr.write(`\x1b[31mError:\x1b[0m ${dir} is not a directory.\n` +
49
- "The pack command requires a scenario directory (with steps.ts).\n");
50
- process.exitCode = 1;
51
- return;
52
- }
53
- const scenarioId = basename(scenarioDir);
54
- const outDir = options.out ? resolve(options.out) : resolve(`./${scenarioId}-bundle`);
55
- const width = Number(options.width) || DEFAULT_WIDTH;
56
- const shellHeight = Number(options.shellHeight) || DEFAULT_SHELL_HEIGHT;
57
- // The entry must live inside the scenario directory so the bundler's
58
- // node_modules resolution walks up into the consumer project.
59
- const tempDir = join(scenarioDir, ".scenar-pack");
60
- try {
61
- const renderFilePath = await detectRenderExport(scenarioDir);
62
- const providersPath = await resolveProvidersPath(scenarioDir);
63
- const hasNarration = await fileExists(join(scenarioDir, "narration", "manifest.json"));
64
- process.stderr.write(`Scenario: ${scenarioId}\n`);
65
- process.stderr.write(`Render: ${renderFilePath}\n`);
66
- process.stderr.write(`Providers: ${providersPath ?? "none"}\n`);
67
- process.stderr.write(`Narration: ${hasNarration ? "yes (manifest found)" : "none"}\n`);
68
- process.stderr.write(`Output: ${outDir}\n\n`);
69
- // 1. Generate the browser entry + index.html into the temp dir.
70
- const entrySource = generateEmbedEntry({
71
- scenarioDir,
72
- renderFilePath,
73
- scenarioId,
74
- hasNarration,
75
- providersPath,
76
- canonicalWidth: width,
77
- shellHeight,
30
+ const result = await runPack({
31
+ scenarioDir: dir,
32
+ outDir: options.out,
33
+ width: Number(options.width) || DEFAULT_WIDTH,
34
+ shellHeight: Number(options.shellHeight) || DEFAULT_SHELL_HEIGHT,
35
+ keepTemp: options.keepTemp,
36
+ onLog: (message) => process.stderr.write(`${message}\n`),
78
37
  });
79
- await mkdir(tempDir, { recursive: true });
80
- const entryFileName = "entry.tsx";
81
- const entryHtmlPath = join(tempDir, "index.html");
82
- await writeFile(join(tempDir, entryFileName), entrySource, "utf-8");
83
- await writeFile(entryHtmlPath, generateEmbedHtml(scenarioId, entryFileName), "utf-8");
84
- // 2. Build the static bundle with Vite.
85
- process.stderr.write("Bundling embed with Vite...\n");
86
- await runViteBuild({ root: tempDir, outDir, entryHtmlPath });
87
- // 3. Copy the narration manifest + audio. The embed fetches
88
- // ./narration/manifest.json at runtime and resolves each clip's
89
- // relative src against it, so both must ship in the bundle.
90
- if (hasNarration) {
91
- await copyNarration(scenarioDir, outDir);
92
- }
93
- // 4. Write the required scenario.json descriptor at the bundle root,
94
- // recording the canonical viewport baked into the bundle so `deploy`
95
- // can derive a correctly-proportioned embed snippet (DD-004).
96
- await writeScenarioJson(outDir, scenarioId, PACK_GENERATOR_VERSION, {
97
- width,
98
- height: shellHeight,
99
- });
100
- // 5. Compute + write the deploy-facing pack manifest (validates the
101
- // bundle against the backend allowlist; throws on any violation).
102
- const manifest = await buildPackManifest(outDir, scenarioId);
103
- await verifyManifestFilesExist(outDir, manifest);
104
- await writePackManifest(outDir, manifest);
105
- const totalBytes = manifest.files.reduce((sum, f) => sum + f.sizeBytes, 0);
106
- process.stderr.write(`\n\x1b[32m✓\x1b[0m Packed ${manifest.files.length} files (${formatBytes(totalBytes)}) to ${outDir}\n`);
107
- process.stderr.write(` Next: scenar deploy ${outDir}\n`);
38
+ process.stderr.write(`\n\x1b[32m✓\x1b[0m Packed ${result.manifest.files.length} files ` +
39
+ `(${formatBytes(result.totalBytes)}) to ${result.outDir}\n`);
40
+ process.stderr.write(` Next: scenar serve ${result.outDir}\n`);
108
41
  }
109
42
  catch (error) {
110
43
  const msg = error instanceof Error ? error.message : String(error);
111
44
  process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
112
45
  process.exitCode = 1;
113
46
  }
114
- finally {
115
- if (!options.keepTemp) {
116
- await rm(tempDir, { recursive: true, force: true }).catch(() => { });
117
- }
118
- }
119
47
  });
120
48
  }
121
- /**
122
- * Copy the narration manifest and every *.mp3 from <scenarioDir>/narration
123
- * into <outDir>/narration. The manifest is the runtime index the embed fetches;
124
- * the mp3s are the clips it references by relative src.
125
- */
126
- async function copyNarration(scenarioDir, outDir) {
127
- const srcDir = join(scenarioDir, "narration");
128
- const destDir = join(outDir, "narration");
129
- await mkdir(destDir, { recursive: true });
130
- const entries = await readdir(srcDir, { withFileTypes: true });
131
- for (const entry of entries) {
132
- if (!entry.isFile())
133
- continue;
134
- const name = entry.name.toLowerCase();
135
- if (name === "manifest.json" || name.endsWith(".mp3")) {
136
- await copyFile(join(srcDir, entry.name), join(destDir, entry.name));
137
- }
138
- }
139
- }
140
- async function fileExists(path) {
141
- try {
142
- await access(path);
143
- return true;
144
- }
145
- catch {
146
- return false;
147
- }
148
- }
149
49
  function formatBytes(bytes) {
150
50
  if (bytes < 1024)
151
51
  return `${bytes} B`;
@@ -1 +1 @@
1
- {"version":3,"file":"pack.js","sourceRoot":"","sources":["../../../src/commands/pack.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pack.js","sourceRoot":"","sources":["../../../src/commands/pack.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAE9C,yEAAyE;AACzE,MAAM,aAAa,GAAG,gBAAgB,CAAC,KAAK,CAAC;AAE7C,0CAA0C;AAC1C,MAAM,oBAAoB,GAAG,gBAAgB,CAAC,MAAM,CAAC;AASrD,MAAM,UAAU,mBAAmB,CAAC,OAAgB;IAClD,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CACV,4DAA4D;QAC1D,6DAA6D;QAC7D,6DAA6D;QAC7D,qEAAqE;QACrE,8DAA8D;QAC9D,2DAA2D;QAC3D,kEAAkE;QAClE,oEAAoE;QACpE,kEAAkE;QAClE,oEAAoE;QACpE,oEAAoE;QACpE,yDAAyD;QACzD,mEAAmE,CACtE;SACA,QAAQ,CAAC,OAAO,EAAE,sDAAsD,CAAC;SACzE,MAAM,CAAC,cAAc,EAAE,iCAAiC,CAAC;SACzD,MAAM,CAAC,kBAAkB,EAAE,4CAA4C,aAAa,GAAG,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;SAC/G,MAAM,CACL,yBAAyB,EACzB,gCAAgC,oBAAoB,GAAG,EACvD,MAAM,CAAC,oBAAoB,CAAC,CAC7B;SACA,MAAM,CAAC,aAAa,EAAE,kDAAkD,CAAC;SACzE,MAAM,CAAC,KAAK,EAAE,GAAW,EAAE,OAAoB,EAAE,EAAE;QAClD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC;gBAC3B,WAAW,EAAE,GAAG;gBAChB,MAAM,EAAE,OAAO,CAAC,GAAG;gBACnB,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,aAAa;gBAC7C,WAAW,EAAE,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,oBAAoB;gBAChE,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,KAAK,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC;aACzD,CAAC,CAAC;YAEH,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,6BAA6B,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,SAAS;gBAChE,IAAI,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,MAAM,CAAC,MAAM,IAAI,CAC9D,CAAC;YACF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,wBAAwB,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;QAClE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,GAAG,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAAC;YACvD,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACvB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,WAAW,CAAC,KAAa;IAChC,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,KAAK,IAAI,CAAC;IACtC,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IACnE,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AACrD,CAAC"}
@@ -1,20 +1,6 @@
1
- import { resolve, join, basename } from "node:path";
2
- import { stat, mkdir, writeFile, rm, readdir, copyFile, access } from "node:fs/promises";
3
1
  import { Command } from "commander";
4
- import { detectRenderExport } from "../render/detect-render-export.js";
5
- import { resolveProvidersPath } from "../render/resolve-providers.js";
6
- import { generateEmbedEntry, generateEmbedHtml } from "../pack/generate-embed-entry.js";
7
- import { runViteBuild } from "../pack/build.js";
8
- import {
9
- buildPackManifest,
10
- writePackManifest,
11
- writeScenarioJson,
12
- verifyManifestFilesExist,
13
- } from "../pack/pack-manifest.js";
14
2
  import { DEFAULT_VIEWPORT } from "../pack/viewport.js";
15
-
16
- /** Generator version stamped into scenario.json (kept in sync with the CLI). */
17
- const PACK_GENERATOR_VERSION = "0.0.1";
3
+ import { runPack } from "../pack/run-pack.js";
18
4
 
19
5
  /** Default canonical viewport width (matches DemoViewport's default). */
20
6
  const DEFAULT_WIDTH = DEFAULT_VIEWPORT.width;
@@ -38,7 +24,7 @@ export function registerPackCommand(program: Command): void {
38
24
  "(steps.ts + an index.tsx that exports renderStep, plus an\n" +
39
25
  "optional .scenar/providers.tsx and narration/). Produces a static\n" +
40
26
  "bundle that boots ScenarioPlayer in the browser, ready for\n" +
41
- "`scenar deploy`.\n\n" +
27
+ "`scenar serve`, `scenar publish`, or `scenar deploy`.\n\n" +
42
28
  "The bundle contains index.html, hashed JS/CSS, a scenario.json\n" +
43
29
  "descriptor, and a pack-manifest.json listing every file with its\n" +
44
30
  "lowercase-hex sha256 and content type. Output stays within the\n" +
@@ -57,131 +43,29 @@ export function registerPackCommand(program: Command): void {
57
43
  )
58
44
  .option("--keep-temp", "keep the generated entry directory for debugging")
59
45
  .action(async (dir: string, options: PackOptions) => {
60
- const scenarioDir = resolve(dir);
61
-
62
- let info;
63
- try {
64
- info = await stat(scenarioDir);
65
- } catch {
66
- process.stderr.write(`\x1b[31mError:\x1b[0m ${dir} does not exist.\n`);
67
- process.exitCode = 1;
68
- return;
69
- }
70
- if (!info.isDirectory()) {
71
- process.stderr.write(
72
- `\x1b[31mError:\x1b[0m ${dir} is not a directory.\n` +
73
- "The pack command requires a scenario directory (with steps.ts).\n",
74
- );
75
- process.exitCode = 1;
76
- return;
77
- }
78
-
79
- const scenarioId = basename(scenarioDir);
80
- const outDir = options.out ? resolve(options.out) : resolve(`./${scenarioId}-bundle`);
81
- const width = Number(options.width) || DEFAULT_WIDTH;
82
- const shellHeight = Number(options.shellHeight) || DEFAULT_SHELL_HEIGHT;
83
-
84
- // The entry must live inside the scenario directory so the bundler's
85
- // node_modules resolution walks up into the consumer project.
86
- const tempDir = join(scenarioDir, ".scenar-pack");
87
-
88
46
  try {
89
- const renderFilePath = await detectRenderExport(scenarioDir);
90
- const providersPath = await resolveProvidersPath(scenarioDir);
91
- const hasNarration = await fileExists(join(scenarioDir, "narration", "manifest.json"));
92
-
93
- process.stderr.write(`Scenario: ${scenarioId}\n`);
94
- process.stderr.write(`Render: ${renderFilePath}\n`);
95
- process.stderr.write(`Providers: ${providersPath ?? "none"}\n`);
96
- process.stderr.write(`Narration: ${hasNarration ? "yes (manifest found)" : "none"}\n`);
97
- process.stderr.write(`Output: ${outDir}\n\n`);
98
-
99
- // 1. Generate the browser entry + index.html into the temp dir.
100
- const entrySource = generateEmbedEntry({
101
- scenarioDir,
102
- renderFilePath,
103
- scenarioId,
104
- hasNarration,
105
- providersPath,
106
- canonicalWidth: width,
107
- shellHeight,
108
- });
109
- await mkdir(tempDir, { recursive: true });
110
- const entryFileName = "entry.tsx";
111
- const entryHtmlPath = join(tempDir, "index.html");
112
- await writeFile(join(tempDir, entryFileName), entrySource, "utf-8");
113
- await writeFile(entryHtmlPath, generateEmbedHtml(scenarioId, entryFileName), "utf-8");
114
-
115
- // 2. Build the static bundle with Vite.
116
- process.stderr.write("Bundling embed with Vite...\n");
117
- await runViteBuild({ root: tempDir, outDir, entryHtmlPath });
118
-
119
- // 3. Copy the narration manifest + audio. The embed fetches
120
- // ./narration/manifest.json at runtime and resolves each clip's
121
- // relative src against it, so both must ship in the bundle.
122
- if (hasNarration) {
123
- await copyNarration(scenarioDir, outDir);
124
- }
125
-
126
- // 4. Write the required scenario.json descriptor at the bundle root,
127
- // recording the canonical viewport baked into the bundle so `deploy`
128
- // can derive a correctly-proportioned embed snippet (DD-004).
129
- await writeScenarioJson(outDir, scenarioId, PACK_GENERATOR_VERSION, {
130
- width,
131
- height: shellHeight,
47
+ const result = await runPack({
48
+ scenarioDir: dir,
49
+ outDir: options.out,
50
+ width: Number(options.width) || DEFAULT_WIDTH,
51
+ shellHeight: Number(options.shellHeight) || DEFAULT_SHELL_HEIGHT,
52
+ keepTemp: options.keepTemp,
53
+ onLog: (message) => process.stderr.write(`${message}\n`),
132
54
  });
133
55
 
134
- // 5. Compute + write the deploy-facing pack manifest (validates the
135
- // bundle against the backend allowlist; throws on any violation).
136
- const manifest = await buildPackManifest(outDir, scenarioId);
137
- await verifyManifestFilesExist(outDir, manifest);
138
- await writePackManifest(outDir, manifest);
139
-
140
- const totalBytes = manifest.files.reduce((sum, f) => sum + f.sizeBytes, 0);
141
56
  process.stderr.write(
142
- `\n\x1b[32m✓\x1b[0m Packed ${manifest.files.length} files (${formatBytes(totalBytes)}) to ${outDir}\n`,
57
+ `\n\x1b[32m✓\x1b[0m Packed ${result.manifest.files.length} files ` +
58
+ `(${formatBytes(result.totalBytes)}) to ${result.outDir}\n`,
143
59
  );
144
- process.stderr.write(` Next: scenar deploy ${outDir}\n`);
60
+ process.stderr.write(` Next: scenar serve ${result.outDir}\n`);
145
61
  } catch (error) {
146
62
  const msg = error instanceof Error ? error.message : String(error);
147
63
  process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
148
64
  process.exitCode = 1;
149
- } finally {
150
- if (!options.keepTemp) {
151
- await rm(tempDir, { recursive: true, force: true }).catch(() => {});
152
- }
153
65
  }
154
66
  });
155
67
  }
156
68
 
157
- /**
158
- * Copy the narration manifest and every *.mp3 from <scenarioDir>/narration
159
- * into <outDir>/narration. The manifest is the runtime index the embed fetches;
160
- * the mp3s are the clips it references by relative src.
161
- */
162
- async function copyNarration(scenarioDir: string, outDir: string): Promise<void> {
163
- const srcDir = join(scenarioDir, "narration");
164
- const destDir = join(outDir, "narration");
165
- await mkdir(destDir, { recursive: true });
166
- const entries = await readdir(srcDir, { withFileTypes: true });
167
- for (const entry of entries) {
168
- if (!entry.isFile()) continue;
169
- const name = entry.name.toLowerCase();
170
- if (name === "manifest.json" || name.endsWith(".mp3")) {
171
- await copyFile(join(srcDir, entry.name), join(destDir, entry.name));
172
- }
173
- }
174
- }
175
-
176
- async function fileExists(path: string): Promise<boolean> {
177
- try {
178
- await access(path);
179
- return true;
180
- } catch {
181
- return false;
182
- }
183
- }
184
-
185
69
  function formatBytes(bytes: number): string {
186
70
  if (bytes < 1024) return `${bytes} B`;
187
71
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KiB`;
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function registerPublishCommand(program: Command): void;
3
+ //# sourceMappingURL=publish.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.d.ts","sourceRoot":"","sources":["../../../src/commands/publish.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAWpC,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqE7D"}
@@ -0,0 +1,64 @@
1
+ import { runPublish } from "../publish/run-publish.js";
2
+ export function registerPublishCommand(program) {
3
+ program
4
+ .command("publish")
5
+ .description("Publish a packed bundle to GitHub Pages and print its public embed URL.\n\n" +
6
+ "Takes a bundle directory produced by `scenar pack`. Creates (or reuses)\n" +
7
+ "a GitHub repo, publishes the bundle to its gh-pages branch, enables\n" +
8
+ "GitHub Pages, and prints the public URL plus a ready-to-paste <iframe>\n" +
9
+ "snippet.\n\n" +
10
+ "By default many tours share one repo: --repo defaults to `scenar-embeds`\n" +
11
+ "and --path to the packed scenario slug, so the embed serves at\n" +
12
+ "https://<you>.github.io/scenar-embeds/<slug>/ and re-publishing one tour\n" +
13
+ "preserves the others. Pass --path / to publish at the repo root\n" +
14
+ "(a dedicated single-tour repo). Target an org with --org.\n\n" +
15
+ "Requires the GitHub CLI (gh) installed and authenticated (`gh auth\n" +
16
+ "login`). Repos are public by default (GitHub Pages needs Pro for private\n" +
17
+ "repos); pass --private if your plan supports it.\n\n" +
18
+ "For an ephemeral local preview instead, use `scenar serve`.")
19
+ .argument("<bundleDir>", "path to a packed bundle directory (from scenar pack)")
20
+ .option("--repo <name>", "target repository name (default: scenar-embeds)")
21
+ .option("--path <subpath>", "subdirectory within the repo (default: the scenario slug; use / for the root)")
22
+ .option("--org <org>", "publish under a GitHub organization instead of your user account")
23
+ .option("--private", "create the repo private (GitHub Pages requires a paid plan for this)")
24
+ .option("--message <msg>", "commit message for the published snapshot", "Publish Scenar embed")
25
+ .action(async (bundleDir, options) => {
26
+ try {
27
+ process.stderr.write(`Bundle: ${bundleDir}\n`);
28
+ process.stderr.write(`Target: ${options.org ? `${options.org}/` : ""}${options.repo ?? "scenar-embeds"}` +
29
+ `/${options.path ?? "(scenario slug)"} (GitHub Pages)\n\n`);
30
+ const { result, viewport, recordedViewport, snippet } = await runPublish({
31
+ bundleDir,
32
+ repo: options.repo,
33
+ path: options.path,
34
+ org: options.org,
35
+ private: Boolean(options.private),
36
+ message: options.message,
37
+ onLog: (message) => process.stderr.write(`${message}\n`),
38
+ });
39
+ const location = result.path ? `${result.owner}/${result.repo}/${result.path}` : `${result.owner}/${result.repo}`;
40
+ process.stderr.write(`\n\x1b[32m✓\x1b[0m ${result.created ? "Created" : "Updated"} ${location}\n`);
41
+ process.stderr.write(` Repo: ${result.repoUrl}\n`);
42
+ // Bare public URL to stdout (pipeable); everything else to stderr.
43
+ process.stdout.write(`${result.pagesUrl}\n`);
44
+ process.stderr.write("\n GitHub Pages builds asynchronously; the URL goes live in ~1 minute on the first publish.\n");
45
+ if (!recordedViewport) {
46
+ process.stderr.write(" Note: no recorded viewport; snippet uses the default " +
47
+ `${viewport.width}x${viewport.height}. Re-pack with the current CLI ` +
48
+ "to embed at the exact aspect ratio.\n");
49
+ }
50
+ process.stderr.write("\n Embed snippet (paste into any page):\n\n");
51
+ process.stderr.write(`${indent(snippet)}\n`);
52
+ }
53
+ catch (error) {
54
+ const msg = error instanceof Error ? error.message : String(error);
55
+ process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
56
+ process.exitCode = 1;
57
+ }
58
+ });
59
+ }
60
+ /** Indent every line by two spaces (for nesting a block under a heading). */
61
+ function indent(block) {
62
+ return block.replace(/^/gm, " ");
63
+ }
64
+ //# sourceMappingURL=publish.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish.js","sourceRoot":"","sources":["../../../src/commands/publish.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { Command } from "commander";
2
+ import { runPublish } from "../publish/run-publish.js";
3
+
4
+ interface PublishCommandOptions {
5
+ repo?: string;
6
+ path?: string;
7
+ org?: string;
8
+ private?: boolean;
9
+ message?: string;
10
+ }
11
+
12
+ export function registerPublishCommand(program: Command): void {
13
+ program
14
+ .command("publish")
15
+ .description(
16
+ "Publish a packed bundle to GitHub Pages and print its public embed URL.\n\n" +
17
+ "Takes a bundle directory produced by `scenar pack`. Creates (or reuses)\n" +
18
+ "a GitHub repo, publishes the bundle to its gh-pages branch, enables\n" +
19
+ "GitHub Pages, and prints the public URL plus a ready-to-paste <iframe>\n" +
20
+ "snippet.\n\n" +
21
+ "By default many tours share one repo: --repo defaults to `scenar-embeds`\n" +
22
+ "and --path to the packed scenario slug, so the embed serves at\n" +
23
+ "https://<you>.github.io/scenar-embeds/<slug>/ and re-publishing one tour\n" +
24
+ "preserves the others. Pass --path / to publish at the repo root\n" +
25
+ "(a dedicated single-tour repo). Target an org with --org.\n\n" +
26
+ "Requires the GitHub CLI (gh) installed and authenticated (`gh auth\n" +
27
+ "login`). Repos are public by default (GitHub Pages needs Pro for private\n" +
28
+ "repos); pass --private if your plan supports it.\n\n" +
29
+ "For an ephemeral local preview instead, use `scenar serve`.",
30
+ )
31
+ .argument("<bundleDir>", "path to a packed bundle directory (from scenar pack)")
32
+ .option("--repo <name>", "target repository name (default: scenar-embeds)")
33
+ .option("--path <subpath>", "subdirectory within the repo (default: the scenario slug; use / for the root)")
34
+ .option("--org <org>", "publish under a GitHub organization instead of your user account")
35
+ .option("--private", "create the repo private (GitHub Pages requires a paid plan for this)")
36
+ .option("--message <msg>", "commit message for the published snapshot", "Publish Scenar embed")
37
+ .action(async (bundleDir: string, options: PublishCommandOptions) => {
38
+ try {
39
+ process.stderr.write(`Bundle: ${bundleDir}\n`);
40
+ process.stderr.write(
41
+ `Target: ${options.org ? `${options.org}/` : ""}${options.repo ?? "scenar-embeds"}` +
42
+ `/${options.path ?? "(scenario slug)"} (GitHub Pages)\n\n`,
43
+ );
44
+
45
+ const { result, viewport, recordedViewport, snippet } = await runPublish({
46
+ bundleDir,
47
+ repo: options.repo,
48
+ path: options.path,
49
+ org: options.org,
50
+ private: Boolean(options.private),
51
+ message: options.message,
52
+ onLog: (message) => process.stderr.write(`${message}\n`),
53
+ });
54
+
55
+ const location = result.path ? `${result.owner}/${result.repo}/${result.path}` : `${result.owner}/${result.repo}`;
56
+ process.stderr.write(
57
+ `\n\x1b[32m✓\x1b[0m ${result.created ? "Created" : "Updated"} ${location}\n`,
58
+ );
59
+ process.stderr.write(` Repo: ${result.repoUrl}\n`);
60
+ // Bare public URL to stdout (pipeable); everything else to stderr.
61
+ process.stdout.write(`${result.pagesUrl}\n`);
62
+ process.stderr.write(
63
+ "\n GitHub Pages builds asynchronously; the URL goes live in ~1 minute on the first publish.\n",
64
+ );
65
+
66
+ if (!recordedViewport) {
67
+ process.stderr.write(
68
+ " Note: no recorded viewport; snippet uses the default " +
69
+ `${viewport.width}x${viewport.height}. Re-pack with the current CLI ` +
70
+ "to embed at the exact aspect ratio.\n",
71
+ );
72
+ }
73
+ process.stderr.write("\n Embed snippet (paste into any page):\n\n");
74
+ process.stderr.write(`${indent(snippet)}\n`);
75
+ } catch (error) {
76
+ const msg = error instanceof Error ? error.message : String(error);
77
+ process.stderr.write(`\x1b[31mError:\x1b[0m ${msg}\n`);
78
+ process.exitCode = 1;
79
+ }
80
+ });
81
+ }
82
+
83
+ /** Indent every line by two spaces (for nesting a block under a heading). */
84
+ function indent(block: string): string {
85
+ return block.replace(/^/gm, " ");
86
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/commands/render.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA8G5D"}
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/commands/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAapC,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA6C5D"}