@kadj-amoah/showrunner 1.1.1 → 1.1.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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to Showrunner are documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the project tracks loose semver — minor bumps for new capability, patch for fixes.
4
4
 
5
+ ## [1.1.2] — 2026-05-24
6
+
7
+ Hot-fix for a regression discovered while smoke-testing the v1.1.1 install on CachyOS.
8
+
9
+ ### Fixed
10
+
11
+ - **`showrunner --version` crashed on fresh global install with `ERR_MODULE_NOT_FOUND` for `@babel/parser`.** The babel runtime deps (`@babel/parser`, `@babel/traverse`, `@babel/types`) used by the `instrument` stage were in `devDependencies`, so they weren't installed alongside the published package. The CLI bundle loads all subcommand modules eagerly, so the failure surfaced before any subcommand could run. Moved the three packages to `dependencies`. (`@types/babel__traverse` stays in `devDependencies` — type-only.) This bug was almost certainly latent in v1.1.0 as well; nobody hit it because the v1.1.0 install hung on the silent playwright postinstall (the issue v1.1.1 fixed).
12
+
13
+ ### Added
14
+
15
+ - **`showrunner install-browser`** subcommand — wraps the Playwright browser install by shelling out to the bundled `playwright-core`'s CLI directly, which sidesteps the misleading "install your project's dependencies first" warning that plain `npx playwright install` emits when invoked outside a Node project.
16
+ - **Bare-command welcome now detects missing chromium** and prepends a clear "First-time setup: `showrunner install-browser`" hint when the browser cache doesn't have it. Removes the implicit assumption that users will know they need to bootstrap a browser after install.
17
+
5
18
  ## [1.1.1] — 2026-05-24
6
19
 
7
20
  Cross-OS reliability + first-run UX patch. Addresses install-time UX findings from a Linux (CachyOS/Arch) audit of v1.1.0 *and* the post-install "what do I do now?" gaps the author hit when installing on a fresh OS. No behavioural changes to the pipeline itself.
package/dist/cli.js CHANGED
@@ -6114,6 +6114,50 @@ and \`at_word\` actions degrade to \`at\`).
6114
6114
  `;
6115
6115
  }
6116
6116
 
6117
+ // src/commands/installBrowser.ts
6118
+ import { spawn as spawn4 } from "child_process";
6119
+ import { access as access3 } from "fs/promises";
6120
+ import { fileURLToPath } from "url";
6121
+ import { dirname as dirname11, join as join11 } from "path";
6122
+ var DEFAULT_BROWSER = "chromium";
6123
+ async function installBrowserCommand(opts) {
6124
+ const browser = opts.browser ?? DEFAULT_BROWSER;
6125
+ const cli = await resolvePlaywrightCoreCli();
6126
+ logger.info(`installing Playwright ${browser} (via bundled playwright-core, no project required)`);
6127
+ const child = spawn4(process.execPath, [cli, "install", browser], {
6128
+ stdio: "inherit",
6129
+ env: process.env
6130
+ });
6131
+ const code = await new Promise((resolve27, reject) => {
6132
+ child.on("error", reject);
6133
+ child.on("close", (c) => resolve27(c ?? 0));
6134
+ });
6135
+ if (code !== 0) {
6136
+ logger.error(`playwright install exited with code ${code}`);
6137
+ process.exit(code);
6138
+ }
6139
+ logger.info(`${browser} installed. Try \`showrunner doctor -c demo.yaml\` next.`);
6140
+ }
6141
+ async function resolvePlaywrightCoreCli() {
6142
+ const here = fileURLToPath(import.meta.url);
6143
+ let dir = dirname11(here);
6144
+ const root = dir.split(/[\\/]/)[0] + "/";
6145
+ while (dir && dir !== root) {
6146
+ const candidate = join11(dir, "node_modules", "playwright-core", "cli.js");
6147
+ try {
6148
+ await access3(candidate);
6149
+ return candidate;
6150
+ } catch {
6151
+ }
6152
+ const parent = dirname11(dir);
6153
+ if (parent === dir) break;
6154
+ dir = parent;
6155
+ }
6156
+ throw new Error(
6157
+ "Could not locate playwright-core CLI inside Showrunner's node_modules. This is a packaging bug \u2014 please file an issue."
6158
+ );
6159
+ }
6160
+
6117
6161
  // src/commands/validate.ts
6118
6162
  import { stat as stat10 } from "fs/promises";
6119
6163
  import { isAbsolute as isAbsolute9, resolve as resolve16 } from "path";
@@ -6266,7 +6310,7 @@ async function pathExists2(p) {
6266
6310
 
6267
6311
  // src/commands/rerunSegment.ts
6268
6312
  import { mkdir as mkdir12, rename as rename3, writeFile as writeFile13 } from "fs/promises";
6269
- import { join as join11, resolve as resolve19 } from "path";
6313
+ import { join as join12, resolve as resolve19 } from "path";
6270
6314
  import { chromium as chromium4, firefox as firefox4, webkit as webkit4 } from "playwright-core";
6271
6315
  var RERUN_BUFFER_MS = 500;
6272
6316
  var browserMap4 = { chromium: chromium4, firefox: firefox4, webkit: webkit4 };
@@ -6385,7 +6429,7 @@ async function rerunSegmentCommand(opts) {
6385
6429
  await page.waitForTimeout(config.recording.segment_buffer_ms);
6386
6430
  const traceDir = resolve19(configDir, config.recording.trace_dir);
6387
6431
  await mkdir12(traceDir, { recursive: true });
6388
- await ctx.tracing.stopChunk({ path: join11(traceDir, `${segment.id}.zip`) });
6432
+ await ctx.tracing.stopChunk({ path: join12(traceDir, `${segment.id}.zip`) });
6389
6433
  const videoHandle = page.video();
6390
6434
  await ctx.close();
6391
6435
  if (!videoHandle) {
@@ -6393,9 +6437,9 @@ async function rerunSegmentCommand(opts) {
6393
6437
  process.exit(1);
6394
6438
  }
6395
6439
  const original = await videoHandle.path();
6396
- const dest = join11(videoDir, `${segment.id}.webm`);
6440
+ const dest = join12(videoDir, `${segment.id}.webm`);
6397
6441
  await rename3(original, dest);
6398
- const metadataPath = join11(videoDir, `${segment.id}.rerun.json`);
6442
+ const metadataPath = join12(videoDir, `${segment.id}.rerun.json`);
6399
6443
  await writeFile13(
6400
6444
  metadataPath,
6401
6445
  JSON.stringify(
@@ -6424,7 +6468,7 @@ async function rerunSegmentCommand(opts) {
6424
6468
 
6425
6469
  // src/commands/captureAuth.ts
6426
6470
  import { mkdir as mkdir14 } from "fs/promises";
6427
- import { dirname as dirname11, isAbsolute as isAbsolute10, resolve as resolve21 } from "path";
6471
+ import { dirname as dirname12, isAbsolute as isAbsolute10, resolve as resolve21 } from "path";
6428
6472
  import { createInterface } from "readline/promises";
6429
6473
 
6430
6474
  // src/recording/headed.ts
@@ -6511,7 +6555,7 @@ async function captureAuthCommand(opts) {
6511
6555
  }
6512
6556
  const cookiesRel = opts.outputCookies ?? "./auth/session.json";
6513
6557
  const cookiesPath = isAbsolute10(cookiesRel) ? cookiesRel : resolve21(loaded.configDir, cookiesRel);
6514
- await mkdir14(dirname11(cookiesPath), { recursive: true });
6558
+ await mkdir14(dirname12(cookiesPath), { recursive: true });
6515
6559
  logger.info("Launching headed browser for auth capture", {
6516
6560
  target: loaded.config.recording.target_url,
6517
6561
  output: cookiesPath
@@ -6552,7 +6596,7 @@ Then re-run \`showrunner run --config <demo.yaml>\`.
6552
6596
 
6553
6597
  // src/commands/trace.ts
6554
6598
  import { readdir as readdir2, stat as stat12 } from "fs/promises";
6555
- import { isAbsolute as isAbsolute11, join as join12, resolve as resolve22 } from "path";
6599
+ import { isAbsolute as isAbsolute11, join as join13, resolve as resolve22 } from "path";
6556
6600
  async function traceCommand(opts) {
6557
6601
  let loaded;
6558
6602
  try {
@@ -6566,7 +6610,7 @@ async function traceCommand(opts) {
6566
6610
  }
6567
6611
  const traceDir = resolve22(loaded.configDir, loaded.config.recording.trace_dir);
6568
6612
  const videoDir = resolve22(loaded.configDir, loaded.config.recording.output_dir);
6569
- const slicePlanPath = join12(videoDir, "slice_plan.json");
6613
+ const slicePlanPath = join13(videoDir, "slice_plan.json");
6570
6614
  if (opts.all) {
6571
6615
  let plan;
6572
6616
  try {
@@ -6577,14 +6621,14 @@ async function traceCommand(opts) {
6577
6621
  process.exit(1);
6578
6622
  }
6579
6623
  for (const seg of plan.segments) {
6580
- const tracePath = seg.trace_path && isAbsolute11(seg.trace_path) ? seg.trace_path : seg.trace_path ? resolve22(loaded.configDir, seg.trace_path) : join12(traceDir, `${seg.id}.zip`);
6624
+ const tracePath = seg.trace_path && isAbsolute11(seg.trace_path) ? seg.trace_path : seg.trace_path ? resolve22(loaded.configDir, seg.trace_path) : join13(traceDir, `${seg.id}.zip`);
6581
6625
  logger.info(`Opening trace for ${seg.id}`, { path: tracePath });
6582
6626
  await openTrace(tracePath);
6583
6627
  }
6584
6628
  return;
6585
6629
  }
6586
6630
  if (opts.segment) {
6587
- const tracePath = join12(traceDir, `${opts.segment}.zip`);
6631
+ const tracePath = join13(traceDir, `${opts.segment}.zip`);
6588
6632
  if (!await fileExists8(tracePath)) {
6589
6633
  logger.error(`No trace found at ${tracePath}`);
6590
6634
  process.exit(1);
@@ -6689,7 +6733,7 @@ async function fileExists9(path) {
6689
6733
 
6690
6734
  // src/commands/understand.ts
6691
6735
  import { mkdir as mkdir15, readFile as readFile10, writeFile as writeFile14 } from "fs/promises";
6692
- import { dirname as dirname12, isAbsolute as isAbsolute12, resolve as resolve24 } from "path";
6736
+ import { dirname as dirname13, isAbsolute as isAbsolute12, resolve as resolve24 } from "path";
6693
6737
 
6694
6738
  // src/productModel/prompts.ts
6695
6739
  var PRODUCT_MODEL_SYSTEM_PROMPT = `You build product_model.json for Showrunner, an automated product demo recorder.
@@ -6894,7 +6938,7 @@ async function understandCommand(opts) {
6894
6938
  }
6895
6939
  }
6896
6940
  const outputPath = isAbsolute12(outputRel) ? outputRel : resolve24(configDir, outputRel);
6897
- await mkdir15(dirname12(outputPath), { recursive: true });
6941
+ await mkdir15(dirname13(outputPath), { recursive: true });
6898
6942
  let productModel;
6899
6943
  const provider = resolveDefaultLLMProvider({ configDir, llm: llmConfig });
6900
6944
  try {
@@ -6942,7 +6986,7 @@ async function understandCommand(opts) {
6942
6986
 
6943
6987
  // src/commands/instrument.ts
6944
6988
  import { mkdir as mkdir16, readdir as readdir3, writeFile as writeFile15 } from "fs/promises";
6945
- import { dirname as dirname13, isAbsolute as isAbsolute13, join as join13, resolve as resolve25, relative as relative2 } from "path";
6989
+ import { dirname as dirname14, isAbsolute as isAbsolute13, join as join14, resolve as resolve25, relative as relative2 } from "path";
6946
6990
 
6947
6991
  // src/instrument/scan.ts
6948
6992
  import { readFile as readFile11 } from "fs/promises";
@@ -7167,7 +7211,7 @@ async function instrumentCommand(opts) {
7167
7211
  async function walk(root, accept) {
7168
7212
  const entries = await readdir3(root, { withFileTypes: true });
7169
7213
  for (const entry of entries) {
7170
- const abs = join13(root, entry.name);
7214
+ const abs = join14(root, entry.name);
7171
7215
  if (entry.isDirectory()) {
7172
7216
  if (IGNORED_DIRS.has(entry.name)) continue;
7173
7217
  await walk(abs, accept);
@@ -7224,7 +7268,7 @@ async function instrumentCommand(opts) {
7224
7268
  logger.warn(`Skipped suggestion at ${s.file}:${s.line} \u2014 ${s.reason}`);
7225
7269
  }
7226
7270
  const outputPath = isAbsolute13(opts.output) ? opts.output : resolve25(loaded.configDir, opts.output);
7227
- await mkdir16(dirname13(outputPath), { recursive: true });
7271
+ await mkdir16(dirname14(outputPath), { recursive: true });
7228
7272
  await writeFile15(outputPath, patch, "utf8");
7229
7273
  logger.info("Wrote instrumentation patch", { path: outputPath, suggestions: suggestions.length });
7230
7274
  process.stdout.write(`
@@ -7559,7 +7603,7 @@ async function fileExists10(path) {
7559
7603
 
7560
7604
  // src/cli.ts
7561
7605
  var program = new Command();
7562
- program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.1").option("--json", "emit structured JSON logs to stdout").option("--log-level <level>", "log level (debug|info|warn|error)").hook("preAction", (thisCmd) => {
7606
+ program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.2").option("--json", "emit structured JSON logs to stdout").option("--log-level <level>", "log level (debug|info|warn|error)").hook("preAction", (thisCmd) => {
7563
7607
  const opts = thisCmd.opts();
7564
7608
  if (opts.json) logger.setJson(true);
7565
7609
  if (opts.logLevel) logger.setLevel(opts.logLevel);
@@ -7592,6 +7636,7 @@ program.command("init").description("Scaffold a new Showrunner project").option(
7592
7636
  "scaffold resolution: low (854x480) | standard (720p) | high (1080p) | extreme (4K)",
7593
7637
  "standard"
7594
7638
  ).action(initCommand);
7639
+ program.command("install-browser").description("Install the Playwright browser binary (chromium by default) \u2014 wraps playwright-core install").option("--browser <name>", "browser to install: chromium | firefox | webkit", "chromium").action(installBrowserCommand);
7595
7640
  program.command("doctor").description("Run preflight checks on the current config + environment").requiredOption("-c, --config <path>", "path to demo.yaml").option("--json", "emit results as JSON instead of human-readable rows").action(doctorCommand);
7596
7641
  program.command("validate").description("Validate a demo.yaml config file").requiredOption("-c, --config <path>", "path to demo.yaml").option("--strict", "exit nonzero on any warning (e.g. missing provider env var)").action(validateCommand);
7597
7642
  program.command("understand").description("Build product_model.json from documents or interactive Q&A").option("-c, --config <path>", "path to demo.yaml").option("--interactive", "use interactive Q&A mode").option("--output <path>", "output path for product_model.json").action(understandCommand);
@@ -7609,16 +7654,23 @@ program.parseAsync(process.argv).catch((err) => {
7609
7654
  process.exit(1);
7610
7655
  });
7611
7656
  async function printWelcome() {
7612
- const { access: access3 } = await import("fs/promises");
7657
+ const { access: access4 } = await import("fs/promises");
7613
7658
  const { resolve: resolve27 } = await import("path");
7614
7659
  const demoYaml = resolve27(process.cwd(), "demo.yaml");
7615
7660
  let inProject = false;
7616
7661
  try {
7617
- await access3(demoYaml);
7662
+ await access4(demoYaml);
7618
7663
  inProject = true;
7619
7664
  } catch {
7620
7665
  }
7621
- const lines = ["", `Showrunner v1.1.1 \u2014 automated product-demo recording & production`, ""];
7666
+ const browserMissing = await isChromiumMissing();
7667
+ const lines = ["", `Showrunner v1.1.2 \u2014 automated product-demo recording & production`, ""];
7668
+ if (browserMissing) {
7669
+ lines.push(`First-time setup: install the recording browser (one-off, ~150 MB):`);
7670
+ lines.push(``);
7671
+ lines.push(` showrunner install-browser # wraps Playwright; no "install dependencies first" warning`);
7672
+ lines.push(``);
7673
+ }
7622
7674
  if (inProject) {
7623
7675
  lines.push(`Detected demo.yaml in this directory. Likely next:`);
7624
7676
  lines.push(``);
@@ -7644,6 +7696,17 @@ async function printWelcome() {
7644
7696
  lines.push("");
7645
7697
  process.stdout.write(lines.join("\n"));
7646
7698
  }
7699
+ async function isChromiumMissing() {
7700
+ try {
7701
+ const { chromium: chromium6 } = await import("playwright-core");
7702
+ const exec = chromium6.executablePath();
7703
+ const { stat: stat15 } = await import("fs/promises");
7704
+ await stat15(exec);
7705
+ return false;
7706
+ } catch {
7707
+ return true;
7708
+ }
7709
+ }
7647
7710
  function parseStages(value) {
7648
7711
  const requested = value.split(",").map((s) => s.trim());
7649
7712
  const invalid = requested.filter((s) => !stageChoices.includes(s));