@agent-scope/cli 1.16.0 → 1.17.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.
package/dist/index.cjs CHANGED
@@ -9,8 +9,10 @@ var commander = require('commander');
9
9
  var esbuild = require('esbuild');
10
10
  var module$1 = require('module');
11
11
  var readline = require('readline');
12
- var playwright$1 = require('playwright');
13
12
  var playwright = require('@agent-scope/playwright');
13
+ var playwright$1 = require('playwright');
14
+ var http = require('http');
15
+ var site = require('@agent-scope/site');
14
16
 
15
17
  function _interopNamespace(e) {
16
18
  if (e && e.__esModule) return e;
@@ -1130,9 +1132,9 @@ function createRL() {
1130
1132
  });
1131
1133
  }
1132
1134
  async function ask(rl, question) {
1133
- return new Promise((resolve16) => {
1135
+ return new Promise((resolve17) => {
1134
1136
  rl.question(question, (answer) => {
1135
- resolve16(answer.trim());
1137
+ resolve17(answer.trim());
1136
1138
  });
1137
1139
  });
1138
1140
  }
@@ -1704,6 +1706,7 @@ async function runHooksProfiling(componentName, filePath, props) {
1704
1706
  try {
1705
1707
  const context = await browser.newContext();
1706
1708
  const page = await context.newPage();
1709
+ await page.addInitScript({ content: playwright.getBrowserEntryScript() });
1707
1710
  const htmlHarness = await buildComponentHarness(filePath, componentName, props, 1280);
1708
1711
  await page.setContent(htmlHarness, { waitUntil: "load" });
1709
1712
  await page.waitForFunction(
@@ -2087,24 +2090,6 @@ Available: ${available}`
2087
2090
  var MANIFEST_PATH4 = ".reactscope/manifest.json";
2088
2091
  var DEFAULT_VIEWPORT_WIDTH = 375;
2089
2092
  var DEFAULT_VIEWPORT_HEIGHT = 812;
2090
- var _pool2 = null;
2091
- async function getPool2() {
2092
- if (_pool2 === null) {
2093
- _pool2 = new render.BrowserPool({
2094
- size: { browsers: 1, pagesPerBrowser: 1 },
2095
- viewportWidth: DEFAULT_VIEWPORT_WIDTH,
2096
- viewportHeight: DEFAULT_VIEWPORT_HEIGHT
2097
- });
2098
- await _pool2.init();
2099
- }
2100
- return _pool2;
2101
- }
2102
- async function shutdownPool2() {
2103
- if (_pool2 !== null) {
2104
- await _pool2.close();
2105
- _pool2 = null;
2106
- }
2107
- }
2108
2093
  function mapNodeType(node) {
2109
2094
  if (node.type === "forward_ref") return "forwardRef";
2110
2095
  if (node.type === "host") return "host";
@@ -2314,10 +2299,12 @@ function formatInstrumentTree(root, showProviderDepth = false) {
2314
2299
  }
2315
2300
  async function runInstrumentTree(options) {
2316
2301
  const { componentName, filePath } = options;
2317
- const pool = await getPool2();
2318
- const slot = await pool.acquire();
2319
- const { page } = slot;
2302
+ const browser = await playwright$1.chromium.launch({ headless: true });
2320
2303
  try {
2304
+ const context = await browser.newContext({
2305
+ viewport: { width: DEFAULT_VIEWPORT_WIDTH, height: DEFAULT_VIEWPORT_HEIGHT }
2306
+ });
2307
+ const page = await context.newPage();
2321
2308
  await page.addInitScript({ content: playwright.getBrowserEntryScript() });
2322
2309
  const htmlHarness = await buildComponentHarness(
2323
2310
  filePath,
@@ -2371,7 +2358,7 @@ async function runInstrumentTree(options) {
2371
2358
  }
2372
2359
  return instrumentRoot;
2373
2360
  } finally {
2374
- pool.release(slot);
2361
+ await browser.close();
2375
2362
  }
2376
2363
  }
2377
2364
  function createInstrumentTreeCommand() {
@@ -2411,7 +2398,6 @@ Available: ${available}`
2411
2398
  providerDepth: opts.providerDepth,
2412
2399
  wastedRenders: opts.wastedRenders
2413
2400
  });
2414
- await shutdownPool2();
2415
2401
  const fmt2 = resolveFormat2(opts.format);
2416
2402
  if (fmt2 === "json") {
2417
2403
  process.stdout.write(`${JSON.stringify(instrumentRoot, null, 2)}
@@ -2422,7 +2408,6 @@ Available: ${available}`
2422
2408
  `);
2423
2409
  }
2424
2410
  } catch (err) {
2425
- await shutdownPool2();
2426
2411
  process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
2427
2412
  `);
2428
2413
  process.exit(1);
@@ -2740,22 +2725,22 @@ async function replayInteraction2(page, steps) {
2740
2725
  }
2741
2726
  }
2742
2727
  }
2743
- var _pool3 = null;
2744
- async function getPool3() {
2745
- if (_pool3 === null) {
2746
- _pool3 = new render.BrowserPool({
2728
+ var _pool2 = null;
2729
+ async function getPool2() {
2730
+ if (_pool2 === null) {
2731
+ _pool2 = new render.BrowserPool({
2747
2732
  size: { browsers: 1, pagesPerBrowser: 2 },
2748
2733
  viewportWidth: 1280,
2749
2734
  viewportHeight: 800
2750
2735
  });
2751
- await _pool3.init();
2736
+ await _pool2.init();
2752
2737
  }
2753
- return _pool3;
2738
+ return _pool2;
2754
2739
  }
2755
- async function shutdownPool3() {
2756
- if (_pool3 !== null) {
2757
- await _pool3.close();
2758
- _pool3 = null;
2740
+ async function shutdownPool2() {
2741
+ if (_pool2 !== null) {
2742
+ await _pool2.close();
2743
+ _pool2 = null;
2759
2744
  }
2760
2745
  }
2761
2746
  async function analyzeRenders(options) {
@@ -2772,7 +2757,7 @@ Available: ${available}`
2772
2757
  const rootDir = process.cwd();
2773
2758
  const filePath = path.resolve(rootDir, descriptor.filePath);
2774
2759
  const htmlHarness = await buildComponentHarness(filePath, options.componentName, {}, 1280);
2775
- const pool = await getPool3();
2760
+ const pool = await getPool2();
2776
2761
  const slot = await pool.acquire();
2777
2762
  const { page } = slot;
2778
2763
  const startMs = performance.now();
@@ -2880,7 +2865,7 @@ function createInstrumentRendersCommand() {
2880
2865
  interaction,
2881
2866
  manifestPath: opts.manifest
2882
2867
  });
2883
- await shutdownPool3();
2868
+ await shutdownPool2();
2884
2869
  if (opts.json || !isTTY()) {
2885
2870
  process.stdout.write(`${JSON.stringify(result, null, 2)}
2886
2871
  `);
@@ -2889,7 +2874,7 @@ function createInstrumentRendersCommand() {
2889
2874
  `);
2890
2875
  }
2891
2876
  } catch (err) {
2892
- await shutdownPool3();
2877
+ await shutdownPool2();
2893
2878
  process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
2894
2879
  `);
2895
2880
  process.exit(1);
@@ -2949,22 +2934,22 @@ function writeReportToFile(report, outputPath, pretty) {
2949
2934
  }
2950
2935
  var MANIFEST_PATH6 = ".reactscope/manifest.json";
2951
2936
  var DEFAULT_OUTPUT_DIR = ".reactscope/renders";
2952
- var _pool4 = null;
2953
- async function getPool4(viewportWidth, viewportHeight) {
2954
- if (_pool4 === null) {
2955
- _pool4 = new render.BrowserPool({
2937
+ var _pool3 = null;
2938
+ async function getPool3(viewportWidth, viewportHeight) {
2939
+ if (_pool3 === null) {
2940
+ _pool3 = new render.BrowserPool({
2956
2941
  size: { browsers: 1, pagesPerBrowser: 4 },
2957
2942
  viewportWidth,
2958
2943
  viewportHeight
2959
2944
  });
2960
- await _pool4.init();
2945
+ await _pool3.init();
2961
2946
  }
2962
- return _pool4;
2947
+ return _pool3;
2963
2948
  }
2964
- async function shutdownPool4() {
2965
- if (_pool4 !== null) {
2966
- await _pool4.close();
2967
- _pool4 = null;
2949
+ async function shutdownPool3() {
2950
+ if (_pool3 !== null) {
2951
+ await _pool3.close();
2952
+ _pool3 = null;
2968
2953
  }
2969
2954
  }
2970
2955
  function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
@@ -2975,7 +2960,7 @@ function buildRenderer(filePath, componentName, viewportWidth, viewportHeight) {
2975
2960
  _satori: satori,
2976
2961
  async renderCell(props, _complexityClass) {
2977
2962
  const startMs = performance.now();
2978
- const pool = await getPool4(viewportWidth, viewportHeight);
2963
+ const pool = await getPool3(viewportWidth, viewportHeight);
2979
2964
  const htmlHarness = await buildComponentHarness(
2980
2965
  filePath,
2981
2966
  componentName,
@@ -3111,7 +3096,7 @@ Available: ${available}`
3111
3096
  }
3112
3097
  }
3113
3098
  );
3114
- await shutdownPool4();
3099
+ await shutdownPool3();
3115
3100
  if (outcome.crashed) {
3116
3101
  process.stderr.write(`\u2717 Render failed: ${outcome.error.message}
3117
3102
  `);
@@ -3159,7 +3144,7 @@ Available: ${available}`
3159
3144
  );
3160
3145
  }
3161
3146
  } catch (err) {
3162
- await shutdownPool4();
3147
+ await shutdownPool3();
3163
3148
  process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
3164
3149
  `);
3165
3150
  process.exit(1);
@@ -3244,7 +3229,7 @@ Available: ${available}`
3244
3229
  concurrency
3245
3230
  });
3246
3231
  const result = await matrix.render();
3247
- await shutdownPool4();
3232
+ await shutdownPool3();
3248
3233
  process.stderr.write(
3249
3234
  `Done. ${result.stats.totalCells} cells, avg ${result.stats.avgRenderTimeMs.toFixed(1)}ms
3250
3235
  `
@@ -3289,7 +3274,7 @@ Available: ${available}`
3289
3274
  process.stdout.write(formatMatrixCsv(componentName, result));
3290
3275
  }
3291
3276
  } catch (err) {
3292
- await shutdownPool4();
3277
+ await shutdownPool3();
3293
3278
  process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
3294
3279
  `);
3295
3280
  process.exit(1);
@@ -3386,13 +3371,13 @@ function registerRenderAll(renderCmd) {
3386
3371
  workers.push(worker());
3387
3372
  }
3388
3373
  await Promise.all(workers);
3389
- await shutdownPool4();
3374
+ await shutdownPool3();
3390
3375
  process.stderr.write("\n");
3391
3376
  const summary = formatSummaryText(results, outputDir);
3392
3377
  process.stderr.write(`${summary}
3393
3378
  `);
3394
3379
  } catch (err) {
3395
- await shutdownPool4();
3380
+ await shutdownPool3();
3396
3381
  process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
3397
3382
  `);
3398
3383
  process.exit(1);
@@ -3434,26 +3419,26 @@ function createRenderCommand() {
3434
3419
  return renderCmd;
3435
3420
  }
3436
3421
  var DEFAULT_BASELINE_DIR = ".reactscope/baseline";
3437
- var _pool5 = null;
3438
- async function getPool5(viewportWidth, viewportHeight) {
3439
- if (_pool5 === null) {
3440
- _pool5 = new render.BrowserPool({
3422
+ var _pool4 = null;
3423
+ async function getPool4(viewportWidth, viewportHeight) {
3424
+ if (_pool4 === null) {
3425
+ _pool4 = new render.BrowserPool({
3441
3426
  size: { browsers: 1, pagesPerBrowser: 4 },
3442
3427
  viewportWidth,
3443
3428
  viewportHeight
3444
3429
  });
3445
- await _pool5.init();
3430
+ await _pool4.init();
3446
3431
  }
3447
- return _pool5;
3432
+ return _pool4;
3448
3433
  }
3449
- async function shutdownPool5() {
3450
- if (_pool5 !== null) {
3451
- await _pool5.close();
3452
- _pool5 = null;
3434
+ async function shutdownPool4() {
3435
+ if (_pool4 !== null) {
3436
+ await _pool4.close();
3437
+ _pool4 = null;
3453
3438
  }
3454
3439
  }
3455
3440
  async function renderComponent2(filePath, componentName, props, viewportWidth, viewportHeight) {
3456
- const pool = await getPool5(viewportWidth, viewportHeight);
3441
+ const pool = await getPool4(viewportWidth, viewportHeight);
3457
3442
  const htmlHarness = await buildComponentHarness(filePath, componentName, props, viewportWidth);
3458
3443
  const slot = await pool.acquire();
3459
3444
  const { page } = slot;
@@ -3703,7 +3688,7 @@ async function runBaseline(options = {}) {
3703
3688
  workers.push(worker());
3704
3689
  }
3705
3690
  await Promise.all(workers);
3706
- await shutdownPool5();
3691
+ await shutdownPool4();
3707
3692
  if (isTTY()) {
3708
3693
  process.stderr.write("\n");
3709
3694
  }
@@ -3764,26 +3749,26 @@ function loadBaselineRenderJson2(baselineDir, componentName) {
3764
3749
  if (!fs.existsSync(jsonPath)) return null;
3765
3750
  return JSON.parse(fs.readFileSync(jsonPath, "utf-8"));
3766
3751
  }
3767
- var _pool6 = null;
3768
- async function getPool6(viewportWidth, viewportHeight) {
3769
- if (_pool6 === null) {
3770
- _pool6 = new render.BrowserPool({
3752
+ var _pool5 = null;
3753
+ async function getPool5(viewportWidth, viewportHeight) {
3754
+ if (_pool5 === null) {
3755
+ _pool5 = new render.BrowserPool({
3771
3756
  size: { browsers: 1, pagesPerBrowser: 4 },
3772
3757
  viewportWidth,
3773
3758
  viewportHeight
3774
3759
  });
3775
- await _pool6.init();
3760
+ await _pool5.init();
3776
3761
  }
3777
- return _pool6;
3762
+ return _pool5;
3778
3763
  }
3779
- async function shutdownPool6() {
3780
- if (_pool6 !== null) {
3781
- await _pool6.close();
3782
- _pool6 = null;
3764
+ async function shutdownPool5() {
3765
+ if (_pool5 !== null) {
3766
+ await _pool5.close();
3767
+ _pool5 = null;
3783
3768
  }
3784
3769
  }
3785
3770
  async function renderComponent3(filePath, componentName, props, viewportWidth, viewportHeight) {
3786
- const pool = await getPool6(viewportWidth, viewportHeight);
3771
+ const pool = await getPool5(viewportWidth, viewportHeight);
3787
3772
  const htmlHarness = await buildComponentHarness(filePath, componentName, props, viewportWidth);
3788
3773
  const slot = await pool.acquire();
3789
3774
  const { page } = slot;
@@ -4032,7 +4017,7 @@ async function runDiff(options = {}) {
4032
4017
  }
4033
4018
  await Promise.all(workers);
4034
4019
  }
4035
- await shutdownPool6();
4020
+ await shutdownPool5();
4036
4021
  if (isTTY() && total > 0) {
4037
4022
  process.stderr.write("\n");
4038
4023
  }
@@ -4632,6 +4617,130 @@ function buildStructuredReport(report) {
4632
4617
  route: report.route?.pattern ?? null
4633
4618
  };
4634
4619
  }
4620
+ var MIME_TYPES = {
4621
+ ".html": "text/html; charset=utf-8",
4622
+ ".css": "text/css; charset=utf-8",
4623
+ ".js": "application/javascript; charset=utf-8",
4624
+ ".json": "application/json; charset=utf-8",
4625
+ ".png": "image/png",
4626
+ ".jpg": "image/jpeg",
4627
+ ".jpeg": "image/jpeg",
4628
+ ".svg": "image/svg+xml",
4629
+ ".ico": "image/x-icon"
4630
+ };
4631
+ function registerBuild(siteCmd) {
4632
+ siteCmd.command("build").description("Build a static HTML gallery from .reactscope/ output").option("-i, --input <path>", "Path to .reactscope input directory", ".reactscope").option("-o, --output <path>", "Output directory for generated site", ".reactscope/site").option("--base-path <path>", "Base URL path prefix for subdirectory deployment", "/").option("--compliance <path>", "Path to compliance batch report JSON").option("--title <text>", "Site title", "Scope \u2014 Component Gallery").action(
4633
+ async (opts) => {
4634
+ try {
4635
+ const inputDir = path.resolve(process.cwd(), opts.input);
4636
+ const outputDir = path.resolve(process.cwd(), opts.output);
4637
+ if (!fs.existsSync(inputDir)) {
4638
+ throw new Error(
4639
+ `Input directory not found: ${inputDir}
4640
+ Run \`scope manifest generate\` and \`scope render\` first.`
4641
+ );
4642
+ }
4643
+ const manifestPath = path.join(inputDir, "manifest.json");
4644
+ if (!fs.existsSync(manifestPath)) {
4645
+ throw new Error(
4646
+ `Manifest not found at ${manifestPath}
4647
+ Run \`scope manifest generate\` first.`
4648
+ );
4649
+ }
4650
+ process.stderr.write(`Building site from ${inputDir}\u2026
4651
+ `);
4652
+ await site.buildSite({
4653
+ inputDir,
4654
+ outputDir,
4655
+ basePath: opts.basePath,
4656
+ ...opts.compliance !== void 0 && {
4657
+ compliancePath: path.resolve(process.cwd(), opts.compliance)
4658
+ },
4659
+ title: opts.title
4660
+ });
4661
+ process.stderr.write(`Site written to ${outputDir}
4662
+ `);
4663
+ process.stdout.write(`${outputDir}
4664
+ `);
4665
+ } catch (err) {
4666
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
4667
+ `);
4668
+ process.exit(1);
4669
+ }
4670
+ }
4671
+ );
4672
+ }
4673
+ function registerServe(siteCmd) {
4674
+ siteCmd.command("serve").description("Serve the built static site locally").option("-p, --port <number>", "Port to listen on", "3000").option("-d, --dir <path>", "Directory to serve", ".reactscope/site").action((opts) => {
4675
+ try {
4676
+ const port = Number.parseInt(opts.port, 10);
4677
+ if (Number.isNaN(port) || port < 1 || port > 65535) {
4678
+ throw new Error(`Invalid port: ${opts.port}`);
4679
+ }
4680
+ const serveDir = path.resolve(process.cwd(), opts.dir);
4681
+ if (!fs.existsSync(serveDir)) {
4682
+ throw new Error(
4683
+ `Serve directory not found: ${serveDir}
4684
+ Run \`scope site build\` first.`
4685
+ );
4686
+ }
4687
+ const server = http.createServer((req, res) => {
4688
+ const rawUrl = req.url ?? "/";
4689
+ const urlPath = decodeURIComponent(rawUrl.split("?")[0] ?? "/");
4690
+ const filePath = path.join(serveDir, urlPath.endsWith("/") ? `${urlPath}index.html` : urlPath);
4691
+ if (!filePath.startsWith(serveDir)) {
4692
+ res.writeHead(403, { "Content-Type": "text/plain" });
4693
+ res.end("Forbidden");
4694
+ return;
4695
+ }
4696
+ if (fs.existsSync(filePath) && fs.statSync(filePath).isFile()) {
4697
+ const ext = path.extname(filePath).toLowerCase();
4698
+ const contentType = MIME_TYPES[ext] ?? "application/octet-stream";
4699
+ res.writeHead(200, { "Content-Type": contentType });
4700
+ fs.createReadStream(filePath).pipe(res);
4701
+ return;
4702
+ }
4703
+ const htmlPath = `${filePath}.html`;
4704
+ if (fs.existsSync(htmlPath) && fs.statSync(htmlPath).isFile()) {
4705
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
4706
+ fs.createReadStream(htmlPath).pipe(res);
4707
+ return;
4708
+ }
4709
+ res.writeHead(404, { "Content-Type": "text/plain" });
4710
+ res.end(`Not found: ${urlPath}`);
4711
+ });
4712
+ server.listen(port, () => {
4713
+ process.stderr.write(`Scope site running at http://localhost:${port}
4714
+ `);
4715
+ process.stderr.write(`Serving ${serveDir}
4716
+ `);
4717
+ process.stderr.write("Press Ctrl+C to stop.\n");
4718
+ });
4719
+ server.on("error", (err) => {
4720
+ if (err.code === "EADDRINUSE") {
4721
+ process.stderr.write(`Error: Port ${port} is already in use.
4722
+ `);
4723
+ } else {
4724
+ process.stderr.write(`Server error: ${err.message}
4725
+ `);
4726
+ }
4727
+ process.exit(1);
4728
+ });
4729
+ } catch (err) {
4730
+ process.stderr.write(`Error: ${err instanceof Error ? err.message : String(err)}
4731
+ `);
4732
+ process.exit(1);
4733
+ }
4734
+ });
4735
+ }
4736
+ function createSiteCommand() {
4737
+ const siteCmd = new commander.Command("site").description(
4738
+ "Build and serve the static component gallery site"
4739
+ );
4740
+ registerBuild(siteCmd);
4741
+ registerServe(siteCmd);
4742
+ return siteCmd;
4743
+ }
4635
4744
  var DEFAULT_STYLES_PATH = ".reactscope/compliance-styles.json";
4636
4745
  function loadStylesFile(stylesPath) {
4637
4746
  const absPath = path.resolve(process.cwd(), stylesPath);
@@ -5651,6 +5760,7 @@ function createProgram(options = {}) {
5651
5760
  registerDiffSubCommand(existingReportCmd);
5652
5761
  registerPrCommentSubCommand(existingReportCmd);
5653
5762
  }
5763
+ program.addCommand(createSiteCommand());
5654
5764
  return program;
5655
5765
  }
5656
5766