@kadj-amoah/showrunner 1.1.1 → 1.1.3
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 +22 -0
- package/dist/cli.js +107 -40
- package/dist/cli.js.map +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
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.3] — 2026-05-24
|
|
6
|
+
|
|
7
|
+
UX polish on the post-install discovery path, prompted by author feedback during the CachyOS install smoke-test of v1.1.2 ("the entire welcome wagon needs to be worked on"). No functional change — pure copy + structure.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Bare-command welcome rewritten to one-thing-at-a-time.** Each state (chromium missing, no project here, project here) now shows a single next command and a one-line description, instead of a 15-line wall that duplicated `init`'s footer and crammed multiple ideas per line. The outside-project branch now just points at `showrunner init` and defers all sequencing to `init`'s own output.
|
|
12
|
+
- **`init` next-steps footer tightened.** The previous `cp .env.example .env # then paste in: KEY_A, KEY_B` line conflated three things (file op, key list, no-keys alternative). Now split: step 2 is the `cp`, step 3 lists each required env var on its own line alongside the dashboard URL it's issued from, with the `agent_bridge` alternative as a single parenthetical at the end. Each step is exactly one observable action.
|
|
13
|
+
|
|
14
|
+
## [1.1.2] — 2026-05-24
|
|
15
|
+
|
|
16
|
+
Hot-fix for a regression discovered while smoke-testing the v1.1.1 install on CachyOS.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- **`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).
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **`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.
|
|
25
|
+
- **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.
|
|
26
|
+
|
|
5
27
|
## [1.1.1] — 2026-05-24
|
|
6
28
|
|
|
7
29
|
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
|
@@ -5660,23 +5660,35 @@ function printNextSteps(projectName, resolved) {
|
|
|
5660
5660
|
let step = 1;
|
|
5661
5661
|
lines.push(` ${step++}. cd ${projectName}`);
|
|
5662
5662
|
if (envVars.length > 0) {
|
|
5663
|
-
|
|
5664
|
-
lines.push(` ${step++}.
|
|
5663
|
+
lines.push(` ${step++}. cp .env.example .env`);
|
|
5664
|
+
lines.push(` ${step++}. Edit .env to fill in:`);
|
|
5665
|
+
for (const v of envVars) {
|
|
5666
|
+
const dash = PROVIDER_DASHBOARDS[v];
|
|
5667
|
+
lines.push(` ${v}${dash ? ` (get it at ${dash})` : ""}`);
|
|
5668
|
+
}
|
|
5669
|
+
lines.push(
|
|
5670
|
+
` (No keys? Switch llm.default.provider to \`agent_bridge\` in demo.yaml \u2014 uses your local Claude CLI instead.)`
|
|
5671
|
+
);
|
|
5665
5672
|
} else {
|
|
5666
5673
|
lines.push(
|
|
5667
|
-
` ${step++}. (
|
|
5674
|
+
` ${step++}. (.env not needed \u2014 agent_bridge LLM + ${resolved.tts} TTS don't use API keys.)`
|
|
5668
5675
|
);
|
|
5669
5676
|
}
|
|
5670
|
-
lines.push(` ${step++}.
|
|
5677
|
+
lines.push(` ${step++}. Edit docs/PRD.md (the stub explains what each section is for).`);
|
|
5671
5678
|
lines.push(` ${step++}. showrunner doctor -c demo.yaml`);
|
|
5672
|
-
lines.push(` ${step++}. showrunner run -c demo.yaml
|
|
5679
|
+
lines.push(` ${step++}. showrunner run -c demo.yaml # \u2192 output/demo_final.mp4`);
|
|
5673
5680
|
lines.push("");
|
|
5674
5681
|
lines.push(
|
|
5675
|
-
`
|
|
5682
|
+
`Don't want to write a PRD? Run \`showrunner understand -c demo.yaml --interactive\` instead \u2014 it asks 5 questions and builds the product model from your answers.`
|
|
5676
5683
|
);
|
|
5677
5684
|
lines.push("");
|
|
5678
5685
|
process.stdout.write(lines.join("\n"));
|
|
5679
5686
|
}
|
|
5687
|
+
var PROVIDER_DASHBOARDS = {
|
|
5688
|
+
ANTHROPIC_API_KEY: "https://console.anthropic.com/settings/keys",
|
|
5689
|
+
OPENAI_API_KEY: "https://platform.openai.com/api-keys",
|
|
5690
|
+
ELEVENLABS_API_KEY: "https://elevenlabs.io/app/settings/api-keys"
|
|
5691
|
+
};
|
|
5680
5692
|
function requiredEnvVars(resolved) {
|
|
5681
5693
|
const vars = /* @__PURE__ */ new Set();
|
|
5682
5694
|
if (resolved.llm === "anthropic") vars.add("ANTHROPIC_API_KEY");
|
|
@@ -6114,6 +6126,50 @@ and \`at_word\` actions degrade to \`at\`).
|
|
|
6114
6126
|
`;
|
|
6115
6127
|
}
|
|
6116
6128
|
|
|
6129
|
+
// src/commands/installBrowser.ts
|
|
6130
|
+
import { spawn as spawn4 } from "child_process";
|
|
6131
|
+
import { access as access3 } from "fs/promises";
|
|
6132
|
+
import { fileURLToPath } from "url";
|
|
6133
|
+
import { dirname as dirname11, join as join11 } from "path";
|
|
6134
|
+
var DEFAULT_BROWSER = "chromium";
|
|
6135
|
+
async function installBrowserCommand(opts) {
|
|
6136
|
+
const browser = opts.browser ?? DEFAULT_BROWSER;
|
|
6137
|
+
const cli = await resolvePlaywrightCoreCli();
|
|
6138
|
+
logger.info(`installing Playwright ${browser} (via bundled playwright-core, no project required)`);
|
|
6139
|
+
const child = spawn4(process.execPath, [cli, "install", browser], {
|
|
6140
|
+
stdio: "inherit",
|
|
6141
|
+
env: process.env
|
|
6142
|
+
});
|
|
6143
|
+
const code = await new Promise((resolve27, reject) => {
|
|
6144
|
+
child.on("error", reject);
|
|
6145
|
+
child.on("close", (c) => resolve27(c ?? 0));
|
|
6146
|
+
});
|
|
6147
|
+
if (code !== 0) {
|
|
6148
|
+
logger.error(`playwright install exited with code ${code}`);
|
|
6149
|
+
process.exit(code);
|
|
6150
|
+
}
|
|
6151
|
+
logger.info(`${browser} installed. Try \`showrunner doctor -c demo.yaml\` next.`);
|
|
6152
|
+
}
|
|
6153
|
+
async function resolvePlaywrightCoreCli() {
|
|
6154
|
+
const here = fileURLToPath(import.meta.url);
|
|
6155
|
+
let dir = dirname11(here);
|
|
6156
|
+
const root = dir.split(/[\\/]/)[0] + "/";
|
|
6157
|
+
while (dir && dir !== root) {
|
|
6158
|
+
const candidate = join11(dir, "node_modules", "playwright-core", "cli.js");
|
|
6159
|
+
try {
|
|
6160
|
+
await access3(candidate);
|
|
6161
|
+
return candidate;
|
|
6162
|
+
} catch {
|
|
6163
|
+
}
|
|
6164
|
+
const parent = dirname11(dir);
|
|
6165
|
+
if (parent === dir) break;
|
|
6166
|
+
dir = parent;
|
|
6167
|
+
}
|
|
6168
|
+
throw new Error(
|
|
6169
|
+
"Could not locate playwright-core CLI inside Showrunner's node_modules. This is a packaging bug \u2014 please file an issue."
|
|
6170
|
+
);
|
|
6171
|
+
}
|
|
6172
|
+
|
|
6117
6173
|
// src/commands/validate.ts
|
|
6118
6174
|
import { stat as stat10 } from "fs/promises";
|
|
6119
6175
|
import { isAbsolute as isAbsolute9, resolve as resolve16 } from "path";
|
|
@@ -6266,7 +6322,7 @@ async function pathExists2(p) {
|
|
|
6266
6322
|
|
|
6267
6323
|
// src/commands/rerunSegment.ts
|
|
6268
6324
|
import { mkdir as mkdir12, rename as rename3, writeFile as writeFile13 } from "fs/promises";
|
|
6269
|
-
import { join as
|
|
6325
|
+
import { join as join12, resolve as resolve19 } from "path";
|
|
6270
6326
|
import { chromium as chromium4, firefox as firefox4, webkit as webkit4 } from "playwright-core";
|
|
6271
6327
|
var RERUN_BUFFER_MS = 500;
|
|
6272
6328
|
var browserMap4 = { chromium: chromium4, firefox: firefox4, webkit: webkit4 };
|
|
@@ -6385,7 +6441,7 @@ async function rerunSegmentCommand(opts) {
|
|
|
6385
6441
|
await page.waitForTimeout(config.recording.segment_buffer_ms);
|
|
6386
6442
|
const traceDir = resolve19(configDir, config.recording.trace_dir);
|
|
6387
6443
|
await mkdir12(traceDir, { recursive: true });
|
|
6388
|
-
await ctx.tracing.stopChunk({ path:
|
|
6444
|
+
await ctx.tracing.stopChunk({ path: join12(traceDir, `${segment.id}.zip`) });
|
|
6389
6445
|
const videoHandle = page.video();
|
|
6390
6446
|
await ctx.close();
|
|
6391
6447
|
if (!videoHandle) {
|
|
@@ -6393,9 +6449,9 @@ async function rerunSegmentCommand(opts) {
|
|
|
6393
6449
|
process.exit(1);
|
|
6394
6450
|
}
|
|
6395
6451
|
const original = await videoHandle.path();
|
|
6396
|
-
const dest =
|
|
6452
|
+
const dest = join12(videoDir, `${segment.id}.webm`);
|
|
6397
6453
|
await rename3(original, dest);
|
|
6398
|
-
const metadataPath =
|
|
6454
|
+
const metadataPath = join12(videoDir, `${segment.id}.rerun.json`);
|
|
6399
6455
|
await writeFile13(
|
|
6400
6456
|
metadataPath,
|
|
6401
6457
|
JSON.stringify(
|
|
@@ -6424,7 +6480,7 @@ async function rerunSegmentCommand(opts) {
|
|
|
6424
6480
|
|
|
6425
6481
|
// src/commands/captureAuth.ts
|
|
6426
6482
|
import { mkdir as mkdir14 } from "fs/promises";
|
|
6427
|
-
import { dirname as
|
|
6483
|
+
import { dirname as dirname12, isAbsolute as isAbsolute10, resolve as resolve21 } from "path";
|
|
6428
6484
|
import { createInterface } from "readline/promises";
|
|
6429
6485
|
|
|
6430
6486
|
// src/recording/headed.ts
|
|
@@ -6511,7 +6567,7 @@ async function captureAuthCommand(opts) {
|
|
|
6511
6567
|
}
|
|
6512
6568
|
const cookiesRel = opts.outputCookies ?? "./auth/session.json";
|
|
6513
6569
|
const cookiesPath = isAbsolute10(cookiesRel) ? cookiesRel : resolve21(loaded.configDir, cookiesRel);
|
|
6514
|
-
await mkdir14(
|
|
6570
|
+
await mkdir14(dirname12(cookiesPath), { recursive: true });
|
|
6515
6571
|
logger.info("Launching headed browser for auth capture", {
|
|
6516
6572
|
target: loaded.config.recording.target_url,
|
|
6517
6573
|
output: cookiesPath
|
|
@@ -6552,7 +6608,7 @@ Then re-run \`showrunner run --config <demo.yaml>\`.
|
|
|
6552
6608
|
|
|
6553
6609
|
// src/commands/trace.ts
|
|
6554
6610
|
import { readdir as readdir2, stat as stat12 } from "fs/promises";
|
|
6555
|
-
import { isAbsolute as isAbsolute11, join as
|
|
6611
|
+
import { isAbsolute as isAbsolute11, join as join13, resolve as resolve22 } from "path";
|
|
6556
6612
|
async function traceCommand(opts) {
|
|
6557
6613
|
let loaded;
|
|
6558
6614
|
try {
|
|
@@ -6566,7 +6622,7 @@ async function traceCommand(opts) {
|
|
|
6566
6622
|
}
|
|
6567
6623
|
const traceDir = resolve22(loaded.configDir, loaded.config.recording.trace_dir);
|
|
6568
6624
|
const videoDir = resolve22(loaded.configDir, loaded.config.recording.output_dir);
|
|
6569
|
-
const slicePlanPath =
|
|
6625
|
+
const slicePlanPath = join13(videoDir, "slice_plan.json");
|
|
6570
6626
|
if (opts.all) {
|
|
6571
6627
|
let plan;
|
|
6572
6628
|
try {
|
|
@@ -6577,14 +6633,14 @@ async function traceCommand(opts) {
|
|
|
6577
6633
|
process.exit(1);
|
|
6578
6634
|
}
|
|
6579
6635
|
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) :
|
|
6636
|
+
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
6637
|
logger.info(`Opening trace for ${seg.id}`, { path: tracePath });
|
|
6582
6638
|
await openTrace(tracePath);
|
|
6583
6639
|
}
|
|
6584
6640
|
return;
|
|
6585
6641
|
}
|
|
6586
6642
|
if (opts.segment) {
|
|
6587
|
-
const tracePath =
|
|
6643
|
+
const tracePath = join13(traceDir, `${opts.segment}.zip`);
|
|
6588
6644
|
if (!await fileExists8(tracePath)) {
|
|
6589
6645
|
logger.error(`No trace found at ${tracePath}`);
|
|
6590
6646
|
process.exit(1);
|
|
@@ -6689,7 +6745,7 @@ async function fileExists9(path) {
|
|
|
6689
6745
|
|
|
6690
6746
|
// src/commands/understand.ts
|
|
6691
6747
|
import { mkdir as mkdir15, readFile as readFile10, writeFile as writeFile14 } from "fs/promises";
|
|
6692
|
-
import { dirname as
|
|
6748
|
+
import { dirname as dirname13, isAbsolute as isAbsolute12, resolve as resolve24 } from "path";
|
|
6693
6749
|
|
|
6694
6750
|
// src/productModel/prompts.ts
|
|
6695
6751
|
var PRODUCT_MODEL_SYSTEM_PROMPT = `You build product_model.json for Showrunner, an automated product demo recorder.
|
|
@@ -6894,7 +6950,7 @@ async function understandCommand(opts) {
|
|
|
6894
6950
|
}
|
|
6895
6951
|
}
|
|
6896
6952
|
const outputPath = isAbsolute12(outputRel) ? outputRel : resolve24(configDir, outputRel);
|
|
6897
|
-
await mkdir15(
|
|
6953
|
+
await mkdir15(dirname13(outputPath), { recursive: true });
|
|
6898
6954
|
let productModel;
|
|
6899
6955
|
const provider = resolveDefaultLLMProvider({ configDir, llm: llmConfig });
|
|
6900
6956
|
try {
|
|
@@ -6942,7 +6998,7 @@ async function understandCommand(opts) {
|
|
|
6942
6998
|
|
|
6943
6999
|
// src/commands/instrument.ts
|
|
6944
7000
|
import { mkdir as mkdir16, readdir as readdir3, writeFile as writeFile15 } from "fs/promises";
|
|
6945
|
-
import { dirname as
|
|
7001
|
+
import { dirname as dirname14, isAbsolute as isAbsolute13, join as join14, resolve as resolve25, relative as relative2 } from "path";
|
|
6946
7002
|
|
|
6947
7003
|
// src/instrument/scan.ts
|
|
6948
7004
|
import { readFile as readFile11 } from "fs/promises";
|
|
@@ -7167,7 +7223,7 @@ async function instrumentCommand(opts) {
|
|
|
7167
7223
|
async function walk(root, accept) {
|
|
7168
7224
|
const entries = await readdir3(root, { withFileTypes: true });
|
|
7169
7225
|
for (const entry of entries) {
|
|
7170
|
-
const abs =
|
|
7226
|
+
const abs = join14(root, entry.name);
|
|
7171
7227
|
if (entry.isDirectory()) {
|
|
7172
7228
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
7173
7229
|
await walk(abs, accept);
|
|
@@ -7224,7 +7280,7 @@ async function instrumentCommand(opts) {
|
|
|
7224
7280
|
logger.warn(`Skipped suggestion at ${s.file}:${s.line} \u2014 ${s.reason}`);
|
|
7225
7281
|
}
|
|
7226
7282
|
const outputPath = isAbsolute13(opts.output) ? opts.output : resolve25(loaded.configDir, opts.output);
|
|
7227
|
-
await mkdir16(
|
|
7283
|
+
await mkdir16(dirname14(outputPath), { recursive: true });
|
|
7228
7284
|
await writeFile15(outputPath, patch, "utf8");
|
|
7229
7285
|
logger.info("Wrote instrumentation patch", { path: outputPath, suggestions: suggestions.length });
|
|
7230
7286
|
process.stdout.write(`
|
|
@@ -7559,7 +7615,7 @@ async function fileExists10(path) {
|
|
|
7559
7615
|
|
|
7560
7616
|
// src/cli.ts
|
|
7561
7617
|
var program = new Command();
|
|
7562
|
-
program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.
|
|
7618
|
+
program.name("showrunner").description("Automated product demo recording & production tool").version("1.1.3").option("--json", "emit structured JSON logs to stdout").option("--log-level <level>", "log level (debug|info|warn|error)").hook("preAction", (thisCmd) => {
|
|
7563
7619
|
const opts = thisCmd.opts();
|
|
7564
7620
|
if (opts.json) logger.setJson(true);
|
|
7565
7621
|
if (opts.logLevel) logger.setLevel(opts.logLevel);
|
|
@@ -7592,6 +7648,7 @@ program.command("init").description("Scaffold a new Showrunner project").option(
|
|
|
7592
7648
|
"scaffold resolution: low (854x480) | standard (720p) | high (1080p) | extreme (4K)",
|
|
7593
7649
|
"standard"
|
|
7594
7650
|
).action(initCommand);
|
|
7651
|
+
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
7652
|
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
7653
|
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
7654
|
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,41 +7666,51 @@ program.parseAsync(process.argv).catch((err) => {
|
|
|
7609
7666
|
process.exit(1);
|
|
7610
7667
|
});
|
|
7611
7668
|
async function printWelcome() {
|
|
7612
|
-
const { access:
|
|
7669
|
+
const { access: access4 } = await import("fs/promises");
|
|
7613
7670
|
const { resolve: resolve27 } = await import("path");
|
|
7614
7671
|
const demoYaml = resolve27(process.cwd(), "demo.yaml");
|
|
7615
7672
|
let inProject = false;
|
|
7616
7673
|
try {
|
|
7617
|
-
await
|
|
7674
|
+
await access4(demoYaml);
|
|
7618
7675
|
inProject = true;
|
|
7619
7676
|
} catch {
|
|
7620
7677
|
}
|
|
7621
|
-
const
|
|
7622
|
-
|
|
7623
|
-
|
|
7678
|
+
const browserMissing = await isChromiumMissing();
|
|
7679
|
+
const lines = ["", `Showrunner v1.1.3`, ""];
|
|
7680
|
+
if (browserMissing) {
|
|
7681
|
+
lines.push(`Showrunner records using Chromium. You haven't installed it yet.`);
|
|
7624
7682
|
lines.push(``);
|
|
7625
|
-
lines.push(` showrunner
|
|
7626
|
-
lines.push(` showrunner run -c demo.yaml # run the full pipeline`);
|
|
7683
|
+
lines.push(` showrunner install-browser`);
|
|
7627
7684
|
lines.push(``);
|
|
7628
|
-
lines.push(`
|
|
7629
|
-
} else {
|
|
7630
|
-
lines.push(`
|
|
7685
|
+
lines.push(`(~150 MB, one-off. Re-run \`showrunner\` after it finishes for the next step.)`);
|
|
7686
|
+
} else if (inProject) {
|
|
7687
|
+
lines.push(`This is a Showrunner project (found demo.yaml).`);
|
|
7631
7688
|
lines.push(``);
|
|
7632
|
-
lines.push(` showrunner
|
|
7689
|
+
lines.push(` showrunner doctor -c demo.yaml # check everything is wired correctly`);
|
|
7690
|
+
lines.push(` showrunner run -c demo.yaml # then run the full pipeline`);
|
|
7633
7691
|
lines.push(``);
|
|
7634
|
-
lines.push(`
|
|
7692
|
+
lines.push(`Full command list: \`showrunner --help\``);
|
|
7693
|
+
} else {
|
|
7694
|
+
lines.push(`No Showrunner project in this directory. To create one:`);
|
|
7635
7695
|
lines.push(``);
|
|
7636
|
-
lines.push(`
|
|
7637
|
-
lines.push(` cp .env.example .env # paste provider keys, or use agent_bridge (no keys)`);
|
|
7638
|
-
lines.push(` $EDITOR docs/PRD.md # write your product brief`);
|
|
7639
|
-
lines.push(` showrunner doctor -c demo.yaml # preflight`);
|
|
7640
|
-
lines.push(` showrunner run -c demo.yaml # full pipeline \u2192 output/demo_final.mp4`);
|
|
7696
|
+
lines.push(` showrunner init`);
|
|
7641
7697
|
lines.push(``);
|
|
7642
|
-
lines.push(
|
|
7698
|
+
lines.push(`\`init\` scaffolds the project and prints the next 4 commands tailored to your provider choice.`);
|
|
7643
7699
|
}
|
|
7644
7700
|
lines.push("");
|
|
7645
7701
|
process.stdout.write(lines.join("\n"));
|
|
7646
7702
|
}
|
|
7703
|
+
async function isChromiumMissing() {
|
|
7704
|
+
try {
|
|
7705
|
+
const { chromium: chromium6 } = await import("playwright-core");
|
|
7706
|
+
const exec = chromium6.executablePath();
|
|
7707
|
+
const { stat: stat15 } = await import("fs/promises");
|
|
7708
|
+
await stat15(exec);
|
|
7709
|
+
return false;
|
|
7710
|
+
} catch {
|
|
7711
|
+
return true;
|
|
7712
|
+
}
|
|
7713
|
+
}
|
|
7647
7714
|
function parseStages(value) {
|
|
7648
7715
|
const requested = value.split(",").map((s) => s.trim());
|
|
7649
7716
|
const invalid = requested.filter((s) => !stageChoices.includes(s));
|