@agent-scope/cli 1.20.2 → 1.20.4
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/cli.js +155 -66
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +152 -63
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +24 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +152 -63
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/cli.js
CHANGED
|
@@ -2333,6 +2333,7 @@ function createManifestCommand() {
|
|
|
2333
2333
|
|
|
2334
2334
|
// src/instrument/hooks.ts
|
|
2335
2335
|
import { resolve as resolve5 } from "path";
|
|
2336
|
+
import { serialize } from "@agent-scope/core";
|
|
2336
2337
|
import { getBrowserEntryScript as getBrowserEntryScript2 } from "@agent-scope/playwright";
|
|
2337
2338
|
import { Command as Cmd } from "commander";
|
|
2338
2339
|
import { chromium as chromium2 } from "playwright";
|
|
@@ -2374,26 +2375,7 @@ function buildHookInstrumentationScript() {
|
|
|
2374
2375
|
// Walk fiber tree and collect hook data per component
|
|
2375
2376
|
var components = [];
|
|
2376
2377
|
|
|
2377
|
-
|
|
2378
|
-
if (v === null) return null;
|
|
2379
|
-
if (v === undefined) return undefined;
|
|
2380
|
-
var t = typeof v;
|
|
2381
|
-
if (t === "string" || t === "number" || t === "boolean") return v;
|
|
2382
|
-
if (t === "function") return "[function " + (v.name || "anonymous") + "]";
|
|
2383
|
-
if (Array.isArray(v)) {
|
|
2384
|
-
try { return v.map(serializeValue); } catch(e) { return "[Array]"; }
|
|
2385
|
-
}
|
|
2386
|
-
if (t === "object") {
|
|
2387
|
-
try {
|
|
2388
|
-
var keys = Object.keys(v).slice(0, 5);
|
|
2389
|
-
var out = {};
|
|
2390
|
-
for (var k of keys) { out[k] = serializeValue(v[k]); }
|
|
2391
|
-
if (Object.keys(v).length > 5) out["..."] = "(truncated)";
|
|
2392
|
-
return out;
|
|
2393
|
-
} catch(e) { return "[Object]"; }
|
|
2394
|
-
}
|
|
2395
|
-
return String(v);
|
|
2396
|
-
}
|
|
2378
|
+
var serializeValue = __SCOPE_SERIALIZE_VALUE__;
|
|
2397
2379
|
|
|
2398
2380
|
// Hook node classifiers (mirrors hooks-extractor.ts logic)
|
|
2399
2381
|
var HookLayout = 0b00100;
|
|
@@ -2614,7 +2596,10 @@ async function runHooksProfiling(componentName, filePath, props) {
|
|
|
2614
2596
|
if (renderError !== null) {
|
|
2615
2597
|
throw new Error(`Component render error: ${renderError}`);
|
|
2616
2598
|
}
|
|
2617
|
-
const instrumentScript = buildHookInstrumentationScript()
|
|
2599
|
+
const instrumentScript = buildHookInstrumentationScript().replace(
|
|
2600
|
+
"__SCOPE_SERIALIZE_VALUE__",
|
|
2601
|
+
serialize.toString()
|
|
2602
|
+
);
|
|
2618
2603
|
const raw = await page.evaluate(instrumentScript);
|
|
2619
2604
|
const result = raw;
|
|
2620
2605
|
if (result.error) {
|
|
@@ -2816,44 +2801,79 @@ function buildProfilingCollectScript() {
|
|
|
2816
2801
|
`;
|
|
2817
2802
|
}
|
|
2818
2803
|
async function replayInteraction(page, steps) {
|
|
2819
|
-
|
|
2804
|
+
let executed = 0;
|
|
2805
|
+
const skippedInteractions = [];
|
|
2806
|
+
const skip = (index, step, reason) => {
|
|
2807
|
+
skippedInteractions.push({ index, action: step.action, target: step.target, reason });
|
|
2808
|
+
};
|
|
2809
|
+
for (const [index, step] of steps.entries()) {
|
|
2820
2810
|
switch (step.action) {
|
|
2821
2811
|
case "click":
|
|
2822
|
-
if (step.target) {
|
|
2823
|
-
|
|
2824
|
-
|
|
2812
|
+
if (!step.target) {
|
|
2813
|
+
skip(index, step, "missing_target");
|
|
2814
|
+
break;
|
|
2815
|
+
}
|
|
2816
|
+
try {
|
|
2817
|
+
await page.click(step.target, { timeout: 5e3 });
|
|
2818
|
+
executed++;
|
|
2819
|
+
} catch {
|
|
2820
|
+
process.stderr.write(` \u26A0 click target "${step.target}" not found, skipping
|
|
2825
2821
|
`);
|
|
2826
|
-
|
|
2822
|
+
skip(index, step, "not_found");
|
|
2827
2823
|
}
|
|
2828
2824
|
break;
|
|
2829
2825
|
case "fill":
|
|
2830
|
-
if (step.target
|
|
2831
|
-
|
|
2832
|
-
|
|
2826
|
+
if (!step.target) {
|
|
2827
|
+
skip(index, step, "missing_target");
|
|
2828
|
+
break;
|
|
2829
|
+
}
|
|
2830
|
+
if (step.value === void 0) {
|
|
2831
|
+
skip(index, step, "missing_value");
|
|
2832
|
+
break;
|
|
2833
|
+
}
|
|
2834
|
+
try {
|
|
2835
|
+
await page.fill(step.target, step.value, { timeout: 5e3 });
|
|
2836
|
+
executed++;
|
|
2837
|
+
} catch {
|
|
2838
|
+
process.stderr.write(` \u26A0 fill target "${step.target}" not found, skipping
|
|
2833
2839
|
`);
|
|
2834
|
-
|
|
2840
|
+
skip(index, step, "not_found");
|
|
2835
2841
|
}
|
|
2836
2842
|
break;
|
|
2837
2843
|
case "hover":
|
|
2838
|
-
if (step.target) {
|
|
2839
|
-
|
|
2840
|
-
|
|
2844
|
+
if (!step.target) {
|
|
2845
|
+
skip(index, step, "missing_target");
|
|
2846
|
+
break;
|
|
2847
|
+
}
|
|
2848
|
+
try {
|
|
2849
|
+
await page.hover(step.target, { timeout: 5e3 });
|
|
2850
|
+
executed++;
|
|
2851
|
+
} catch {
|
|
2852
|
+
process.stderr.write(` \u26A0 hover target "${step.target}" not found, skipping
|
|
2841
2853
|
`);
|
|
2842
|
-
|
|
2854
|
+
skip(index, step, "not_found");
|
|
2843
2855
|
}
|
|
2844
2856
|
break;
|
|
2845
2857
|
case "press":
|
|
2846
|
-
if (step.value) {
|
|
2847
|
-
|
|
2858
|
+
if (!step.value) {
|
|
2859
|
+
skip(index, step, "missing_value");
|
|
2860
|
+
break;
|
|
2848
2861
|
}
|
|
2862
|
+
await page.keyboard.press(step.value);
|
|
2863
|
+
executed++;
|
|
2849
2864
|
break;
|
|
2850
2865
|
case "wait":
|
|
2851
2866
|
await page.waitForTimeout(step.delay ?? 500);
|
|
2867
|
+
executed++;
|
|
2868
|
+
break;
|
|
2869
|
+
default:
|
|
2870
|
+
skip(index, step, "unsupported");
|
|
2852
2871
|
break;
|
|
2853
2872
|
}
|
|
2854
2873
|
}
|
|
2874
|
+
return { executed, skippedInteractions };
|
|
2855
2875
|
}
|
|
2856
|
-
function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts) {
|
|
2876
|
+
function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts, profileValidity) {
|
|
2857
2877
|
const flags = /* @__PURE__ */ new Set();
|
|
2858
2878
|
if (wastedRenders !== null && wastedRenders > 0 && wastedRenders / Math.max(1, totalRenders) > 0.3) {
|
|
2859
2879
|
flags.add("WASTED_RENDER");
|
|
@@ -2864,7 +2884,7 @@ function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts)
|
|
|
2864
2884
|
if (layoutShifts.cumulativeScore > 0.1) {
|
|
2865
2885
|
flags.add("LAYOUT_SHIFT_DETECTED");
|
|
2866
2886
|
}
|
|
2867
|
-
if (timing.js > 100) {
|
|
2887
|
+
if (profileValidity === "interaction" && timing.js > 100) {
|
|
2868
2888
|
flags.add("SLOW_INTERACTION");
|
|
2869
2889
|
}
|
|
2870
2890
|
return [...flags];
|
|
@@ -2903,13 +2923,18 @@ async function runInteractionProfile(componentName, filePath, props, interaction
|
|
|
2903
2923
|
throw new Error(`Profiling setup failed: ${setupData.error}`);
|
|
2904
2924
|
}
|
|
2905
2925
|
const jsStart = Date.now();
|
|
2926
|
+
let executedInteractions = 0;
|
|
2927
|
+
let skippedInteractions = [];
|
|
2906
2928
|
if (interaction.length > 0) {
|
|
2907
2929
|
process.stderr.write(` Replaying ${interaction.length} interaction step(s)\u2026
|
|
2908
2930
|
`);
|
|
2909
|
-
await replayInteraction(page, interaction);
|
|
2931
|
+
const replayResult = await replayInteraction(page, interaction);
|
|
2932
|
+
executedInteractions = replayResult.executed;
|
|
2933
|
+
skippedInteractions = replayResult.skippedInteractions;
|
|
2910
2934
|
await page.waitForTimeout(300);
|
|
2911
2935
|
}
|
|
2912
2936
|
const jsDuration = Date.now() - jsStart;
|
|
2937
|
+
const profileValidity = interaction.length === 0 ? "no_interaction_requested" : executedInteractions === 0 ? "setup_only" : "interaction";
|
|
2913
2938
|
const collected = await page.evaluate(buildProfilingCollectScript());
|
|
2914
2939
|
const profileData = collected;
|
|
2915
2940
|
if (profileData.error) {
|
|
@@ -2927,7 +2952,13 @@ async function runInteractionProfile(componentName, filePath, props, interaction
|
|
|
2927
2952
|
const totalRenders = profileData.commitCount ?? 0;
|
|
2928
2953
|
const uniqueComponents = profileData.uniqueComponents ?? 0;
|
|
2929
2954
|
const wastedRenders = profileData.wastedRenders ?? null;
|
|
2930
|
-
const flags = analyzeProfileFlags(
|
|
2955
|
+
const flags = analyzeProfileFlags(
|
|
2956
|
+
totalRenders,
|
|
2957
|
+
wastedRenders,
|
|
2958
|
+
timing,
|
|
2959
|
+
layoutShifts,
|
|
2960
|
+
profileValidity
|
|
2961
|
+
);
|
|
2931
2962
|
return {
|
|
2932
2963
|
component: componentName,
|
|
2933
2964
|
totalRenders,
|
|
@@ -2941,7 +2972,17 @@ async function runInteractionProfile(componentName, filePath, props, interaction
|
|
|
2941
2972
|
timing,
|
|
2942
2973
|
layoutShifts,
|
|
2943
2974
|
flags,
|
|
2944
|
-
interaction
|
|
2975
|
+
interaction,
|
|
2976
|
+
interactionSteps: {
|
|
2977
|
+
attempted: interaction.length,
|
|
2978
|
+
executed: executedInteractions,
|
|
2979
|
+
skipped: skippedInteractions.length
|
|
2980
|
+
},
|
|
2981
|
+
interactionStepsAttempted: interaction.length,
|
|
2982
|
+
interactionStepsExecuted: executedInteractions,
|
|
2983
|
+
interactionStepsSkipped: skippedInteractions.length,
|
|
2984
|
+
skippedInteractions,
|
|
2985
|
+
profileValidity
|
|
2945
2986
|
};
|
|
2946
2987
|
} finally {
|
|
2947
2988
|
await browser.close();
|
|
@@ -3047,6 +3088,7 @@ function flattenSerializedValue(sv) {
|
|
|
3047
3088
|
case "string":
|
|
3048
3089
|
case "number":
|
|
3049
3090
|
case "boolean":
|
|
3091
|
+
case "date":
|
|
3050
3092
|
return v.value;
|
|
3051
3093
|
case "object": {
|
|
3052
3094
|
if (!Array.isArray(v.entries)) return {};
|
|
@@ -3899,7 +3941,7 @@ Examples:
|
|
|
3899
3941
|
}
|
|
3900
3942
|
|
|
3901
3943
|
// src/render-commands.ts
|
|
3902
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
3944
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync9, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
3903
3945
|
import { resolve as resolve11 } from "path";
|
|
3904
3946
|
import {
|
|
3905
3947
|
ALL_CONTEXT_IDS,
|
|
@@ -4157,6 +4199,39 @@ function formatAggregateRenderFailureJson(componentName, failures, scenarioCount
|
|
|
4157
4199
|
}
|
|
4158
4200
|
var MANIFEST_PATH6 = ".reactscope/manifest.json";
|
|
4159
4201
|
var DEFAULT_OUTPUT_DIR = ".reactscope/renders";
|
|
4202
|
+
function renderArtifactBaseName(componentName, scenarioName) {
|
|
4203
|
+
return scenarioName === void 0 ? componentName : `${componentName}-${scenarioName}`;
|
|
4204
|
+
}
|
|
4205
|
+
function removeStaleRenderError(outputDir, componentName, scenarioName) {
|
|
4206
|
+
const errorPath = resolve11(
|
|
4207
|
+
outputDir,
|
|
4208
|
+
`${renderArtifactBaseName(componentName, scenarioName)}.error.json`
|
|
4209
|
+
);
|
|
4210
|
+
if (existsSync9(errorPath)) {
|
|
4211
|
+
rmSync2(errorPath, { force: true });
|
|
4212
|
+
}
|
|
4213
|
+
}
|
|
4214
|
+
function writeRenderErrorArtifact(outputDir, failure) {
|
|
4215
|
+
const errPath = resolve11(
|
|
4216
|
+
outputDir,
|
|
4217
|
+
`${renderArtifactBaseName(failure.component, failure.scenario)}.error.json`
|
|
4218
|
+
);
|
|
4219
|
+
writeFileSync6(
|
|
4220
|
+
errPath,
|
|
4221
|
+
JSON.stringify(
|
|
4222
|
+
{
|
|
4223
|
+
component: failure.component,
|
|
4224
|
+
scenario: failure.scenario,
|
|
4225
|
+
errorMessage: failure.errorMessage,
|
|
4226
|
+
heuristicFlags: failure.heuristicFlags,
|
|
4227
|
+
propsAtCrash: failure.propsAtCrash
|
|
4228
|
+
},
|
|
4229
|
+
null,
|
|
4230
|
+
2
|
|
4231
|
+
)
|
|
4232
|
+
);
|
|
4233
|
+
return errPath;
|
|
4234
|
+
}
|
|
4160
4235
|
var _pool3 = null;
|
|
4161
4236
|
async function getPool3(viewportWidth, viewportHeight) {
|
|
4162
4237
|
if (_pool3 === null) {
|
|
@@ -4841,20 +4916,12 @@ function registerRenderAll(renderCmd) {
|
|
|
4841
4916
|
success: false,
|
|
4842
4917
|
errorMessage: outcome.error.message
|
|
4843
4918
|
});
|
|
4844
|
-
const errPath =
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
errorMessage: outcome.error.message,
|
|
4851
|
-
heuristicFlags: outcome.error.heuristicFlags,
|
|
4852
|
-
propsAtCrash: outcome.error.propsAtCrash
|
|
4853
|
-
},
|
|
4854
|
-
null,
|
|
4855
|
-
2
|
|
4856
|
-
)
|
|
4857
|
-
);
|
|
4919
|
+
const errPath = writeRenderErrorArtifact(outputDir, {
|
|
4920
|
+
component: name,
|
|
4921
|
+
errorMessage: outcome.error.message,
|
|
4922
|
+
heuristicFlags: outcome.error.heuristicFlags,
|
|
4923
|
+
propsAtCrash: outcome.error.propsAtCrash
|
|
4924
|
+
});
|
|
4858
4925
|
failures.push({
|
|
4859
4926
|
component: name,
|
|
4860
4927
|
stage: "render",
|
|
@@ -4867,13 +4934,14 @@ function registerRenderAll(renderCmd) {
|
|
|
4867
4934
|
}
|
|
4868
4935
|
const result = outcome.result;
|
|
4869
4936
|
results.push({ name, renderTimeMs: result.renderTimeMs, success: true });
|
|
4937
|
+
removeStaleRenderError(outputDir, name);
|
|
4870
4938
|
if (!isIcon) {
|
|
4871
4939
|
const pngPath = resolve11(outputDir, `${name}.png`);
|
|
4872
4940
|
writeFileSync6(pngPath, result.screenshot);
|
|
4873
4941
|
outputPaths.push(pngPath);
|
|
4874
4942
|
}
|
|
4875
4943
|
const jsonPath = resolve11(outputDir, `${name}.json`);
|
|
4876
|
-
const renderJson = formatRenderJson(name,
|
|
4944
|
+
const renderJson = formatRenderJson(name, renderProps, result);
|
|
4877
4945
|
const extResult = result;
|
|
4878
4946
|
if (isIcon && extResult.svgContent) {
|
|
4879
4947
|
renderJson.svgContent = extResult.svgContent;
|
|
@@ -4934,13 +5002,34 @@ function registerRenderAll(renderCmd) {
|
|
|
4934
5002
|
concurrency: 2
|
|
4935
5003
|
});
|
|
4936
5004
|
const matrixResult = await matrix.render();
|
|
4937
|
-
const matrixCells = matrixResult.cells.map((cell) =>
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
4941
|
-
|
|
4942
|
-
|
|
4943
|
-
|
|
5005
|
+
const matrixCells = matrixResult.cells.map((cell) => {
|
|
5006
|
+
const scenarioName = scenarioEntries2[cell.axisIndices[0] ?? 0]?.[0] ?? "";
|
|
5007
|
+
const artifactBaseName = renderArtifactBaseName(name, scenarioName);
|
|
5008
|
+
const scenarioPngPath = resolve11(outputDir, `${artifactBaseName}.png`);
|
|
5009
|
+
const scenarioJsonPath = resolve11(outputDir, `${artifactBaseName}.json`);
|
|
5010
|
+
if (!isIcon) {
|
|
5011
|
+
writeFileSync6(scenarioPngPath, cell.result.screenshot);
|
|
5012
|
+
outputPaths.push(scenarioPngPath);
|
|
5013
|
+
}
|
|
5014
|
+
const scenarioJson = formatRenderJson(
|
|
5015
|
+
`${name}:${scenarioName}`,
|
|
5016
|
+
scenarioPropsMap[scenarioName] ?? {},
|
|
5017
|
+
cell.result
|
|
5018
|
+
);
|
|
5019
|
+
writeFileSync6(scenarioJsonPath, JSON.stringify(scenarioJson, null, 2));
|
|
5020
|
+
outputPaths.push(scenarioJsonPath);
|
|
5021
|
+
removeStaleRenderError(outputDir, name, scenarioName);
|
|
5022
|
+
return {
|
|
5023
|
+
axisValues: [scenarioName],
|
|
5024
|
+
scenario: scenarioName,
|
|
5025
|
+
outputPath: scenarioJsonPath,
|
|
5026
|
+
screenshotPath: isIcon ? void 0 : scenarioPngPath,
|
|
5027
|
+
screenshot: cell.result.screenshot.toString("base64"),
|
|
5028
|
+
width: cell.result.width,
|
|
5029
|
+
height: cell.result.height,
|
|
5030
|
+
renderTimeMs: cell.result.renderTimeMs
|
|
5031
|
+
};
|
|
5032
|
+
});
|
|
4944
5033
|
const existingJson = JSON.parse(readFileSync9(jsonPath, "utf-8"));
|
|
4945
5034
|
existingJson.cells = matrixCells;
|
|
4946
5035
|
existingJson.axisLabels = [scenarioAxis.values];
|
|
@@ -5047,7 +5136,7 @@ function createRenderCommand() {
|
|
|
5047
5136
|
}
|
|
5048
5137
|
|
|
5049
5138
|
// src/report/baseline.ts
|
|
5050
|
-
import { existsSync as existsSync10, mkdirSync as mkdirSync6, rmSync as
|
|
5139
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync6, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "fs";
|
|
5051
5140
|
import { resolve as resolve12 } from "path";
|
|
5052
5141
|
import { generateManifest as generateManifest4 } from "@agent-scope/manifest";
|
|
5053
5142
|
import { BrowserPool as BrowserPool4, safeRender as safeRender3 } from "@agent-scope/render";
|
|
@@ -5208,7 +5297,7 @@ async function runBaseline(options = {}) {
|
|
|
5208
5297
|
const baselineDir = resolve12(rootDir, outputDir);
|
|
5209
5298
|
const rendersDir = resolve12(baselineDir, "renders");
|
|
5210
5299
|
if (existsSync10(baselineDir)) {
|
|
5211
|
-
|
|
5300
|
+
rmSync3(baselineDir, { recursive: true, force: true });
|
|
5212
5301
|
}
|
|
5213
5302
|
mkdirSync6(rendersDir, { recursive: true });
|
|
5214
5303
|
let manifest;
|