@agent-scope/cli 1.20.3 → 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 +83 -41
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +83 -41
- 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 +83 -41
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.d.cts
CHANGED
|
@@ -363,6 +363,18 @@ interface LayoutShiftData {
|
|
|
363
363
|
* Heuristic flags from §6.1 catalog applicable to interaction profiling.
|
|
364
364
|
*/
|
|
365
365
|
type ProfileHeuristicFlag = "WASTED_RENDER" | "MEMO_INEFFECTIVE" | "EFFECT_EVERY_RENDER" | "MISSING_CLEANUP" | "HIGH_RENDER_COUNT" | "LAYOUT_SHIFT_DETECTED" | "SLOW_INTERACTION";
|
|
366
|
+
type ProfileValidity = "interaction" | "setup_only" | "no_interaction_requested";
|
|
367
|
+
interface SkippedInteraction {
|
|
368
|
+
index: number;
|
|
369
|
+
action: InteractionStep$1["action"];
|
|
370
|
+
target?: string;
|
|
371
|
+
reason: "not_found" | "missing_target" | "missing_value" | "unsupported";
|
|
372
|
+
}
|
|
373
|
+
interface InteractionExecutionSummary {
|
|
374
|
+
attempted: number;
|
|
375
|
+
executed: number;
|
|
376
|
+
skipped: number;
|
|
377
|
+
}
|
|
366
378
|
/** Full interaction profile result per Spec §5. */
|
|
367
379
|
interface InteractionProfile {
|
|
368
380
|
/** Component name profiled. */
|
|
@@ -387,6 +399,18 @@ interface InteractionProfile {
|
|
|
387
399
|
flags: ProfileHeuristicFlag[];
|
|
388
400
|
/** Interaction steps that were replayed. */
|
|
389
401
|
interaction: InteractionStep$1[];
|
|
402
|
+
/** Interaction execution counts for validity-aware profiling. */
|
|
403
|
+
interactionSteps: InteractionExecutionSummary;
|
|
404
|
+
/** Backwards-compatible attempted interaction count. */
|
|
405
|
+
interactionStepsAttempted: number;
|
|
406
|
+
/** Backwards-compatible executed interaction count. */
|
|
407
|
+
interactionStepsExecuted: number;
|
|
408
|
+
/** Backwards-compatible skipped interaction count. */
|
|
409
|
+
interactionStepsSkipped: number;
|
|
410
|
+
/** Details for each skipped interaction step. */
|
|
411
|
+
skippedInteractions: SkippedInteraction[];
|
|
412
|
+
/** Whether timing/flags reflect a real interaction or setup-only work. */
|
|
413
|
+
profileValidity: ProfileValidity;
|
|
390
414
|
}
|
|
391
415
|
|
|
392
416
|
/** Root cause categories per Instrumentation Spec §2.1 */
|
package/dist/index.d.ts
CHANGED
|
@@ -363,6 +363,18 @@ interface LayoutShiftData {
|
|
|
363
363
|
* Heuristic flags from §6.1 catalog applicable to interaction profiling.
|
|
364
364
|
*/
|
|
365
365
|
type ProfileHeuristicFlag = "WASTED_RENDER" | "MEMO_INEFFECTIVE" | "EFFECT_EVERY_RENDER" | "MISSING_CLEANUP" | "HIGH_RENDER_COUNT" | "LAYOUT_SHIFT_DETECTED" | "SLOW_INTERACTION";
|
|
366
|
+
type ProfileValidity = "interaction" | "setup_only" | "no_interaction_requested";
|
|
367
|
+
interface SkippedInteraction {
|
|
368
|
+
index: number;
|
|
369
|
+
action: InteractionStep$1["action"];
|
|
370
|
+
target?: string;
|
|
371
|
+
reason: "not_found" | "missing_target" | "missing_value" | "unsupported";
|
|
372
|
+
}
|
|
373
|
+
interface InteractionExecutionSummary {
|
|
374
|
+
attempted: number;
|
|
375
|
+
executed: number;
|
|
376
|
+
skipped: number;
|
|
377
|
+
}
|
|
366
378
|
/** Full interaction profile result per Spec §5. */
|
|
367
379
|
interface InteractionProfile {
|
|
368
380
|
/** Component name profiled. */
|
|
@@ -387,6 +399,18 @@ interface InteractionProfile {
|
|
|
387
399
|
flags: ProfileHeuristicFlag[];
|
|
388
400
|
/** Interaction steps that were replayed. */
|
|
389
401
|
interaction: InteractionStep$1[];
|
|
402
|
+
/** Interaction execution counts for validity-aware profiling. */
|
|
403
|
+
interactionSteps: InteractionExecutionSummary;
|
|
404
|
+
/** Backwards-compatible attempted interaction count. */
|
|
405
|
+
interactionStepsAttempted: number;
|
|
406
|
+
/** Backwards-compatible executed interaction count. */
|
|
407
|
+
interactionStepsExecuted: number;
|
|
408
|
+
/** Backwards-compatible skipped interaction count. */
|
|
409
|
+
interactionStepsSkipped: number;
|
|
410
|
+
/** Details for each skipped interaction step. */
|
|
411
|
+
skippedInteractions: SkippedInteraction[];
|
|
412
|
+
/** Whether timing/flags reflect a real interaction or setup-only work. */
|
|
413
|
+
profileValidity: ProfileValidity;
|
|
390
414
|
}
|
|
391
415
|
|
|
392
416
|
/** Root cause categories per Instrumentation Spec §2.1 */
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,7 @@ import { createRequire } from 'module';
|
|
|
9
9
|
import { mkdir, access, writeFile } from 'fs/promises';
|
|
10
10
|
import * as readline from 'readline';
|
|
11
11
|
import { loadTrace, generateTest, getBrowserEntryScript } from '@agent-scope/playwright';
|
|
12
|
+
import { serialize } from '@agent-scope/core';
|
|
12
13
|
import { chromium } from 'playwright';
|
|
13
14
|
import { tmpdir } from 'os';
|
|
14
15
|
import { createServer } from 'http';
|
|
@@ -2278,26 +2279,7 @@ function buildHookInstrumentationScript() {
|
|
|
2278
2279
|
// Walk fiber tree and collect hook data per component
|
|
2279
2280
|
var components = [];
|
|
2280
2281
|
|
|
2281
|
-
|
|
2282
|
-
if (v === null) return null;
|
|
2283
|
-
if (v === undefined) return undefined;
|
|
2284
|
-
var t = typeof v;
|
|
2285
|
-
if (t === "string" || t === "number" || t === "boolean") return v;
|
|
2286
|
-
if (t === "function") return "[function " + (v.name || "anonymous") + "]";
|
|
2287
|
-
if (Array.isArray(v)) {
|
|
2288
|
-
try { return v.map(serializeValue); } catch(e) { return "[Array]"; }
|
|
2289
|
-
}
|
|
2290
|
-
if (t === "object") {
|
|
2291
|
-
try {
|
|
2292
|
-
var keys = Object.keys(v).slice(0, 5);
|
|
2293
|
-
var out = {};
|
|
2294
|
-
for (var k of keys) { out[k] = serializeValue(v[k]); }
|
|
2295
|
-
if (Object.keys(v).length > 5) out["..."] = "(truncated)";
|
|
2296
|
-
return out;
|
|
2297
|
-
} catch(e) { return "[Object]"; }
|
|
2298
|
-
}
|
|
2299
|
-
return String(v);
|
|
2300
|
-
}
|
|
2282
|
+
var serializeValue = __SCOPE_SERIALIZE_VALUE__;
|
|
2301
2283
|
|
|
2302
2284
|
// Hook node classifiers (mirrors hooks-extractor.ts logic)
|
|
2303
2285
|
var HookLayout = 0b00100;
|
|
@@ -2518,7 +2500,10 @@ async function runHooksProfiling(componentName, filePath, props) {
|
|
|
2518
2500
|
if (renderError !== null) {
|
|
2519
2501
|
throw new Error(`Component render error: ${renderError}`);
|
|
2520
2502
|
}
|
|
2521
|
-
const instrumentScript = buildHookInstrumentationScript()
|
|
2503
|
+
const instrumentScript = buildHookInstrumentationScript().replace(
|
|
2504
|
+
"__SCOPE_SERIALIZE_VALUE__",
|
|
2505
|
+
serialize.toString()
|
|
2506
|
+
);
|
|
2522
2507
|
const raw = await page.evaluate(instrumentScript);
|
|
2523
2508
|
const result = raw;
|
|
2524
2509
|
if (result.error) {
|
|
@@ -2714,44 +2699,79 @@ function buildProfilingCollectScript() {
|
|
|
2714
2699
|
`;
|
|
2715
2700
|
}
|
|
2716
2701
|
async function replayInteraction(page, steps) {
|
|
2717
|
-
|
|
2702
|
+
let executed = 0;
|
|
2703
|
+
const skippedInteractions = [];
|
|
2704
|
+
const skip = (index, step, reason) => {
|
|
2705
|
+
skippedInteractions.push({ index, action: step.action, target: step.target, reason });
|
|
2706
|
+
};
|
|
2707
|
+
for (const [index, step] of steps.entries()) {
|
|
2718
2708
|
switch (step.action) {
|
|
2719
2709
|
case "click":
|
|
2720
|
-
if (step.target) {
|
|
2721
|
-
|
|
2722
|
-
|
|
2710
|
+
if (!step.target) {
|
|
2711
|
+
skip(index, step, "missing_target");
|
|
2712
|
+
break;
|
|
2713
|
+
}
|
|
2714
|
+
try {
|
|
2715
|
+
await page.click(step.target, { timeout: 5e3 });
|
|
2716
|
+
executed++;
|
|
2717
|
+
} catch {
|
|
2718
|
+
process.stderr.write(` \u26A0 click target "${step.target}" not found, skipping
|
|
2723
2719
|
`);
|
|
2724
|
-
|
|
2720
|
+
skip(index, step, "not_found");
|
|
2725
2721
|
}
|
|
2726
2722
|
break;
|
|
2727
2723
|
case "fill":
|
|
2728
|
-
if (step.target
|
|
2729
|
-
|
|
2730
|
-
|
|
2724
|
+
if (!step.target) {
|
|
2725
|
+
skip(index, step, "missing_target");
|
|
2726
|
+
break;
|
|
2727
|
+
}
|
|
2728
|
+
if (step.value === void 0) {
|
|
2729
|
+
skip(index, step, "missing_value");
|
|
2730
|
+
break;
|
|
2731
|
+
}
|
|
2732
|
+
try {
|
|
2733
|
+
await page.fill(step.target, step.value, { timeout: 5e3 });
|
|
2734
|
+
executed++;
|
|
2735
|
+
} catch {
|
|
2736
|
+
process.stderr.write(` \u26A0 fill target "${step.target}" not found, skipping
|
|
2731
2737
|
`);
|
|
2732
|
-
|
|
2738
|
+
skip(index, step, "not_found");
|
|
2733
2739
|
}
|
|
2734
2740
|
break;
|
|
2735
2741
|
case "hover":
|
|
2736
|
-
if (step.target) {
|
|
2737
|
-
|
|
2738
|
-
|
|
2742
|
+
if (!step.target) {
|
|
2743
|
+
skip(index, step, "missing_target");
|
|
2744
|
+
break;
|
|
2745
|
+
}
|
|
2746
|
+
try {
|
|
2747
|
+
await page.hover(step.target, { timeout: 5e3 });
|
|
2748
|
+
executed++;
|
|
2749
|
+
} catch {
|
|
2750
|
+
process.stderr.write(` \u26A0 hover target "${step.target}" not found, skipping
|
|
2739
2751
|
`);
|
|
2740
|
-
|
|
2752
|
+
skip(index, step, "not_found");
|
|
2741
2753
|
}
|
|
2742
2754
|
break;
|
|
2743
2755
|
case "press":
|
|
2744
|
-
if (step.value) {
|
|
2745
|
-
|
|
2756
|
+
if (!step.value) {
|
|
2757
|
+
skip(index, step, "missing_value");
|
|
2758
|
+
break;
|
|
2746
2759
|
}
|
|
2760
|
+
await page.keyboard.press(step.value);
|
|
2761
|
+
executed++;
|
|
2747
2762
|
break;
|
|
2748
2763
|
case "wait":
|
|
2749
2764
|
await page.waitForTimeout(step.delay ?? 500);
|
|
2765
|
+
executed++;
|
|
2766
|
+
break;
|
|
2767
|
+
default:
|
|
2768
|
+
skip(index, step, "unsupported");
|
|
2750
2769
|
break;
|
|
2751
2770
|
}
|
|
2752
2771
|
}
|
|
2772
|
+
return { executed, skippedInteractions };
|
|
2753
2773
|
}
|
|
2754
|
-
function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts) {
|
|
2774
|
+
function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts, profileValidity) {
|
|
2755
2775
|
const flags = /* @__PURE__ */ new Set();
|
|
2756
2776
|
if (wastedRenders !== null && wastedRenders > 0 && wastedRenders / Math.max(1, totalRenders) > 0.3) {
|
|
2757
2777
|
flags.add("WASTED_RENDER");
|
|
@@ -2762,7 +2782,7 @@ function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts)
|
|
|
2762
2782
|
if (layoutShifts.cumulativeScore > 0.1) {
|
|
2763
2783
|
flags.add("LAYOUT_SHIFT_DETECTED");
|
|
2764
2784
|
}
|
|
2765
|
-
if (timing.js > 100) {
|
|
2785
|
+
if (profileValidity === "interaction" && timing.js > 100) {
|
|
2766
2786
|
flags.add("SLOW_INTERACTION");
|
|
2767
2787
|
}
|
|
2768
2788
|
return [...flags];
|
|
@@ -2801,13 +2821,18 @@ async function runInteractionProfile(componentName, filePath, props, interaction
|
|
|
2801
2821
|
throw new Error(`Profiling setup failed: ${setupData.error}`);
|
|
2802
2822
|
}
|
|
2803
2823
|
const jsStart = Date.now();
|
|
2824
|
+
let executedInteractions = 0;
|
|
2825
|
+
let skippedInteractions = [];
|
|
2804
2826
|
if (interaction.length > 0) {
|
|
2805
2827
|
process.stderr.write(` Replaying ${interaction.length} interaction step(s)\u2026
|
|
2806
2828
|
`);
|
|
2807
|
-
await replayInteraction(page, interaction);
|
|
2829
|
+
const replayResult = await replayInteraction(page, interaction);
|
|
2830
|
+
executedInteractions = replayResult.executed;
|
|
2831
|
+
skippedInteractions = replayResult.skippedInteractions;
|
|
2808
2832
|
await page.waitForTimeout(300);
|
|
2809
2833
|
}
|
|
2810
2834
|
const jsDuration = Date.now() - jsStart;
|
|
2835
|
+
const profileValidity = interaction.length === 0 ? "no_interaction_requested" : executedInteractions === 0 ? "setup_only" : "interaction";
|
|
2811
2836
|
const collected = await page.evaluate(buildProfilingCollectScript());
|
|
2812
2837
|
const profileData = collected;
|
|
2813
2838
|
if (profileData.error) {
|
|
@@ -2825,7 +2850,13 @@ async function runInteractionProfile(componentName, filePath, props, interaction
|
|
|
2825
2850
|
const totalRenders = profileData.commitCount ?? 0;
|
|
2826
2851
|
const uniqueComponents = profileData.uniqueComponents ?? 0;
|
|
2827
2852
|
const wastedRenders = profileData.wastedRenders ?? null;
|
|
2828
|
-
const flags = analyzeProfileFlags(
|
|
2853
|
+
const flags = analyzeProfileFlags(
|
|
2854
|
+
totalRenders,
|
|
2855
|
+
wastedRenders,
|
|
2856
|
+
timing,
|
|
2857
|
+
layoutShifts,
|
|
2858
|
+
profileValidity
|
|
2859
|
+
);
|
|
2829
2860
|
return {
|
|
2830
2861
|
component: componentName,
|
|
2831
2862
|
totalRenders,
|
|
@@ -2839,7 +2870,17 @@ async function runInteractionProfile(componentName, filePath, props, interaction
|
|
|
2839
2870
|
timing,
|
|
2840
2871
|
layoutShifts,
|
|
2841
2872
|
flags,
|
|
2842
|
-
interaction
|
|
2873
|
+
interaction,
|
|
2874
|
+
interactionSteps: {
|
|
2875
|
+
attempted: interaction.length,
|
|
2876
|
+
executed: executedInteractions,
|
|
2877
|
+
skipped: skippedInteractions.length
|
|
2878
|
+
},
|
|
2879
|
+
interactionStepsAttempted: interaction.length,
|
|
2880
|
+
interactionStepsExecuted: executedInteractions,
|
|
2881
|
+
interactionStepsSkipped: skippedInteractions.length,
|
|
2882
|
+
skippedInteractions,
|
|
2883
|
+
profileValidity
|
|
2843
2884
|
};
|
|
2844
2885
|
} finally {
|
|
2845
2886
|
await browser.close();
|
|
@@ -2939,6 +2980,7 @@ function flattenSerializedValue(sv) {
|
|
|
2939
2980
|
case "string":
|
|
2940
2981
|
case "number":
|
|
2941
2982
|
case "boolean":
|
|
2983
|
+
case "date":
|
|
2942
2984
|
return v.value;
|
|
2943
2985
|
case "object": {
|
|
2944
2986
|
if (!Array.isArray(v.entries)) return {};
|