@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 +13 -0
- package/dist/cli.js +82 -19
- package/dist/cli.js.map +1 -1
- package/package.json +4 -4
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
|
|
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:
|
|
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 =
|
|
6440
|
+
const dest = join12(videoDir, `${segment.id}.webm`);
|
|
6397
6441
|
await rename3(original, dest);
|
|
6398
|
-
const metadataPath =
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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) :
|
|
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 =
|
|
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
|
|
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(
|
|
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
|
|
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 =
|
|
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(
|
|
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.
|
|
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:
|
|
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
|
|
7662
|
+
await access4(demoYaml);
|
|
7618
7663
|
inProject = true;
|
|
7619
7664
|
} catch {
|
|
7620
7665
|
}
|
|
7621
|
-
const
|
|
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));
|