@molecule/api-ai-tools 1.8.3 → 1.9.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/README.md +264 -14
- package/dist/failure-kind.d.ts +66 -0
- package/dist/failure-kind.d.ts.map +1 -0
- package/dist/failure-kind.js +134 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/pipeline-exit-note.d.ts +37 -0
- package/dist/pipeline-exit-note.d.ts.map +1 -0
- package/dist/pipeline-exit-note.js +154 -0
- package/dist/suite-overlap.d.ts +35 -1
- package/dist/suite-overlap.d.ts.map +1 -1
- package/dist/suite-overlap.js +57 -2
- package/dist/tools.d.ts.map +1 -1
- package/dist/tools.js +14 -0
- package/dist/trace-artifacts.d.ts +77 -7
- package/dist/trace-artifacts.d.ts.map +1 -1
- package/dist/trace-artifacts.js +320 -28
- package/dist/utilities.d.ts +25 -4
- package/dist/utilities.d.ts.map +1 -1
- package/dist/utilities.js +127 -13
- package/package.json +1 -1
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The exit code a hand-run check's result carries is the pipeline's LAST stage's, and a weak
|
|
3
|
+
* executor reads the number as the check's verdict.
|
|
4
|
+
*
|
|
5
|
+
* `npx tsc --noEmit 2>&1 | head -40` reports `head`'s 0 while the output above it carries a type
|
|
6
|
+
* error, and `… | head -40 && tail -30 missing.log` reports `tail`'s 1 while the checker was clean —
|
|
7
|
+
* both measured in xr-molecule-flash-m117 (+04:06, +08:05). A model that trusts the code concludes
|
|
8
|
+
* "clean" and ships a build that does not compile, or hunts an error that is not there. This module
|
|
9
|
+
* says which stage produced the code, and what the output actually carries, in one sentence beside
|
|
10
|
+
* `exitCode`.
|
|
11
|
+
*
|
|
12
|
+
* The parse is the package's own shell reader ({@link locatedCommandSegments}), so a `|` inside
|
|
13
|
+
* quotes or a heredoc is not a stage, and `set -o pipefail` (where it really runs) turns the note
|
|
14
|
+
* off: then the code IS the check's.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* The sentence an `exec_command` result carries beside `exitCode` when the code belongs to a
|
|
20
|
+
* pipeline's last stage rather than to the check that printed the output.
|
|
21
|
+
*
|
|
22
|
+
* Two shapes, both from a hand-run check with an unguarded pipe:
|
|
23
|
+
*
|
|
24
|
+
* - the code says 0 and the output carries failures (`tsc | head` reports `head`'s 0);
|
|
25
|
+
* - the code is non-zero and the checker printed no failures (`tsc | head && tail missing.log`
|
|
26
|
+
* reports `tail`'s 1, and the checker was clean).
|
|
27
|
+
*
|
|
28
|
+
* A command with no pipe, a pipe guarded by `pipefail`, a pipeline where no checker runs, or a
|
|
29
|
+
* code and an output that agree all return null.
|
|
30
|
+
*
|
|
31
|
+
* @param command - The command as the executor sent it.
|
|
32
|
+
* @param exitCode - The code the shell reported (the pipeline's last stage's).
|
|
33
|
+
* @param output - The command's raw stdout and stderr, for the checker's own text.
|
|
34
|
+
* @returns One sentence naming the stage the code came from, or null.
|
|
35
|
+
*/
|
|
36
|
+
export declare function pipelineExitCodeNote(command: string, exitCode: number, output: string): string | null;
|
|
37
|
+
//# sourceMappingURL=pipeline-exit-note.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pipeline-exit-note.d.ts","sourceRoot":"","sources":["../src/pipeline-exit-note.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA2GH;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,GACb,MAAM,GAAG,IAAI,CAsCf"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The exit code a hand-run check's result carries is the pipeline's LAST stage's, and a weak
|
|
3
|
+
* executor reads the number as the check's verdict.
|
|
4
|
+
*
|
|
5
|
+
* `npx tsc --noEmit 2>&1 | head -40` reports `head`'s 0 while the output above it carries a type
|
|
6
|
+
* error, and `… | head -40 && tail -30 missing.log` reports `tail`'s 1 while the checker was clean —
|
|
7
|
+
* both measured in xr-molecule-flash-m117 (+04:06, +08:05). A model that trusts the code concludes
|
|
8
|
+
* "clean" and ships a build that does not compile, or hunts an error that is not there. This module
|
|
9
|
+
* says which stage produced the code, and what the output actually carries, in one sentence beside
|
|
10
|
+
* `exitCode`.
|
|
11
|
+
*
|
|
12
|
+
* The parse is the package's own shell reader ({@link locatedCommandSegments}), so a `|` inside
|
|
13
|
+
* quotes or a heredoc is not a stage, and `set -o pipefail` (where it really runs) turns the note
|
|
14
|
+
* off: then the code IS the check's.
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import { locatedCommandSegments } from './command-position.js';
|
|
19
|
+
/** The shells whose `-o pipefail` makes a pipeline report its first failing stage. */
|
|
20
|
+
const PIPEFAIL_SHELLS = new Set(['ash', 'bash', 'dash', 'ksh', 'sh', 'zsh']);
|
|
21
|
+
/**
|
|
22
|
+
* The programs a pipeline may end with when all they do is read what was printed and print it
|
|
23
|
+
* again — the stages that hand back their own status in place of the check's.
|
|
24
|
+
*/
|
|
25
|
+
const OUTPUT_ONLY_FILTERS = new Set([
|
|
26
|
+
'awk',
|
|
27
|
+
'cat',
|
|
28
|
+
'column',
|
|
29
|
+
'cut',
|
|
30
|
+
'egrep',
|
|
31
|
+
'fgrep',
|
|
32
|
+
'fmt',
|
|
33
|
+
'fold',
|
|
34
|
+
'grep',
|
|
35
|
+
'head',
|
|
36
|
+
'jq',
|
|
37
|
+
'nl',
|
|
38
|
+
'rg',
|
|
39
|
+
'sed',
|
|
40
|
+
'sort',
|
|
41
|
+
'tail',
|
|
42
|
+
'tr',
|
|
43
|
+
'uniq',
|
|
44
|
+
'wc',
|
|
45
|
+
]);
|
|
46
|
+
/** `1 type error` / `2 type errors`. */
|
|
47
|
+
const plural = (count, one, many) => `${count} ${count === 1 ? one : many}`;
|
|
48
|
+
/**
|
|
49
|
+
* The failure count a runner's tally names. Vitest prints two tallies — `Test Files 1 failed`
|
|
50
|
+
* above `Tests 2 failed` — and the last one is the tests, which is the number the note means.
|
|
51
|
+
*
|
|
52
|
+
* @param output - The runner's output.
|
|
53
|
+
* @returns The count in the last `N failed` tally, or 0.
|
|
54
|
+
*/
|
|
55
|
+
const failedTally = (output) => {
|
|
56
|
+
const tallies = [...output.matchAll(/\b(\d+) failed\b/g)];
|
|
57
|
+
return Number(tallies.at(-1)?.[1] ?? 0);
|
|
58
|
+
};
|
|
59
|
+
/** The checkers whose output says pass or fail in text of its own. */
|
|
60
|
+
const CHECKERS = [
|
|
61
|
+
{
|
|
62
|
+
name: 'tsc',
|
|
63
|
+
runs: (program) => program === 'tsc' || program === 'vue-tsc',
|
|
64
|
+
failures: (output) => {
|
|
65
|
+
const errors = output.match(/\berror TS\d+/g)?.length ?? 0;
|
|
66
|
+
if (errors > 0)
|
|
67
|
+
return errors;
|
|
68
|
+
const found = /\bFound (\d+) errors?/.exec(output);
|
|
69
|
+
return found ? Number(found[1]) : 0;
|
|
70
|
+
},
|
|
71
|
+
label: (count) => plural(count, 'type error', 'type errors'),
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
name: 'eslint',
|
|
75
|
+
runs: (program) => program === 'eslint',
|
|
76
|
+
failures: (output) => {
|
|
77
|
+
const problems = /✖\s*(\d+)\s+problems?/.exec(output);
|
|
78
|
+
return problems ? Number(problems[1]) : 0;
|
|
79
|
+
},
|
|
80
|
+
label: (count) => plural(count, 'lint problem', 'lint problems'),
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'vitest',
|
|
84
|
+
runs: (program) => program === 'vitest',
|
|
85
|
+
failures: failedTally,
|
|
86
|
+
label: (count) => plural(count, 'failing test', 'failing tests'),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'playwright',
|
|
90
|
+
runs: (_program, text) => /^playwright\s+test\b/.test(text),
|
|
91
|
+
failures: failedTally,
|
|
92
|
+
label: (count) => plural(count, 'failing test', 'failing tests'),
|
|
93
|
+
},
|
|
94
|
+
];
|
|
95
|
+
/** Whether the command turns on `pipefail`, so a pipeline reports the check's own status. */
|
|
96
|
+
function guardedByPipefail(segments) {
|
|
97
|
+
return segments.some(({ program, args }) => (program === 'set' || PIPEFAIL_SHELLS.has(program)) && args.includes('pipefail'));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The sentence an `exec_command` result carries beside `exitCode` when the code belongs to a
|
|
101
|
+
* pipeline's last stage rather than to the check that printed the output.
|
|
102
|
+
*
|
|
103
|
+
* Two shapes, both from a hand-run check with an unguarded pipe:
|
|
104
|
+
*
|
|
105
|
+
* - the code says 0 and the output carries failures (`tsc | head` reports `head`'s 0);
|
|
106
|
+
* - the code is non-zero and the checker printed no failures (`tsc | head && tail missing.log`
|
|
107
|
+
* reports `tail`'s 1, and the checker was clean).
|
|
108
|
+
*
|
|
109
|
+
* A command with no pipe, a pipe guarded by `pipefail`, a pipeline where no checker runs, or a
|
|
110
|
+
* code and an output that agree all return null.
|
|
111
|
+
*
|
|
112
|
+
* @param command - The command as the executor sent it.
|
|
113
|
+
* @param exitCode - The code the shell reported (the pipeline's last stage's).
|
|
114
|
+
* @param output - The command's raw stdout and stderr, for the checker's own text.
|
|
115
|
+
* @returns One sentence naming the stage the code came from, or null.
|
|
116
|
+
*/
|
|
117
|
+
export function pipelineExitCodeNote(command, exitCode, output) {
|
|
118
|
+
const running = locatedCommandSegments(command).filter((entry) => entry.segment !== null);
|
|
119
|
+
if (running.length < 2)
|
|
120
|
+
return null;
|
|
121
|
+
if (!running.some((entry) => entry.separator.trim() === '|'))
|
|
122
|
+
return null;
|
|
123
|
+
const segments = running.map((entry) => entry.segment);
|
|
124
|
+
if (guardedByPipefail(segments))
|
|
125
|
+
return null;
|
|
126
|
+
let checkerAt = -1;
|
|
127
|
+
for (let i = segments.length - 1; i >= 0; i -= 1) {
|
|
128
|
+
const segment = segments[i];
|
|
129
|
+
if (CHECKERS.some((checker) => checker.runs(segment.program, segment.text))) {
|
|
130
|
+
checkerAt = i;
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (checkerAt < 0)
|
|
135
|
+
return null;
|
|
136
|
+
// The check's own status is lost only when a stage of the SAME pipeline follows it; a `&&` after
|
|
137
|
+
// the check already reports a real failure of its own, and a check followed by nothing reports
|
|
138
|
+
// its own code.
|
|
139
|
+
if (running[checkerAt + 1]?.separator.trim() !== '|')
|
|
140
|
+
return null;
|
|
141
|
+
const checkerSegment = segments[checkerAt];
|
|
142
|
+
const checker = CHECKERS.find((candidate) => candidate.runs(checkerSegment.program, checkerSegment.text));
|
|
143
|
+
const last = segments[segments.length - 1];
|
|
144
|
+
const failures = checker.failures(output);
|
|
145
|
+
if (exitCode === 0 && failures > 0) {
|
|
146
|
+
return (`the exit code 0 is the last stage's (${last.program}), not ${checker.name}'s — ` +
|
|
147
|
+
`the output carries ${checker.label(failures)}`);
|
|
148
|
+
}
|
|
149
|
+
if (exitCode !== 0 && failures === 0 && OUTPUT_ONLY_FILTERS.has(last.program)) {
|
|
150
|
+
return (`the exit code ${exitCode} is the last stage's (${last.program}), not ${checker.name}'s — ` +
|
|
151
|
+
'the checker printed no errors');
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
}
|
package/dist/suite-overlap.d.ts
CHANGED
|
@@ -50,12 +50,44 @@ export declare function withoutHeredocs(command: string): string;
|
|
|
50
50
|
* @returns The target key.
|
|
51
51
|
*/
|
|
52
52
|
export declare function suiteCommandTarget(command: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Whether a Playwright statement names its own config — the reading {@link withSuiteConfig} and
|
|
55
|
+
* {@link scopedRerunCommand} share, so neither adds a second `--config` to a command that has one.
|
|
56
|
+
*
|
|
57
|
+
* @param command - A command, or the Playwright statement inside it.
|
|
58
|
+
* @returns True when the statement loads a config of its own.
|
|
59
|
+
*/
|
|
60
|
+
export declare function namesSuiteConfig(command: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* The command with a `--config` added to the Playwright invocation it starts — or unchanged when
|
|
63
|
+
* it already names one, or starts no Playwright run.
|
|
64
|
+
*
|
|
65
|
+
* The flag rides the same statement the runner does: a `cd`, an env prefix and a `timeout` before
|
|
66
|
+
* it are left exactly as they were, and so is everything chained after the run (`| tail -50`).
|
|
67
|
+
*
|
|
68
|
+
* xr-molecule-flash-m113 (+19:09): the model's fix-round re-run dropped the
|
|
69
|
+
* `--config playwright.conduit.config.ts` both earlier runs had carried, so Playwright started with
|
|
70
|
+
* NO base URL and all 10 failures were `page.goto: Cannot navigate to invalid URL` — a failure list
|
|
71
|
+
* that says nothing about the app. The platform composes commands back to the executor too (the
|
|
72
|
+
* narrowed re-run of a finished run's failures), and a command it hands back has to carry the
|
|
73
|
+
* config the run was measured under, never the shape that caused that finding.
|
|
74
|
+
*
|
|
75
|
+
* @param command - The command as the executor sent it.
|
|
76
|
+
* @param config - The config the run loads, when the caller knows it.
|
|
77
|
+
* @returns The command with the flag, or the command unchanged.
|
|
78
|
+
*/
|
|
79
|
+
export declare function withSuiteConfig(command: string, config: string | null | undefined): string;
|
|
53
80
|
/**
|
|
54
81
|
* The whole-suite command made scoped to the tests it should re-run instead: the same
|
|
55
82
|
* invocation — same directory, same settings — with the named `file:line`s in place of the whole
|
|
56
83
|
* suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flash-m60) and
|
|
57
84
|
* `--retries=0` set (a re-run of a known failure does not need to pay for retries).
|
|
58
85
|
*
|
|
86
|
+
* `config` is the config the run was measured under ({@link withSuiteConfig}): the narrowed re-run
|
|
87
|
+
* carries it whenever the command itself does not, so a run that dropped `--config` cannot be
|
|
88
|
+
* handed back a command with the same defect (xr-molecule-flash-m113: every failing test was
|
|
89
|
+
* `Cannot navigate to invalid URL` because the re-run loaded no base URL).
|
|
90
|
+
*
|
|
59
91
|
* @param command - The whole-suite run's command.
|
|
60
92
|
* @param locations - The failing `file:line`s to re-run, as the run's own log printed them — or,
|
|
61
93
|
* for a run that started outside the suite, the absolute form Playwright matches each file
|
|
@@ -64,9 +96,11 @@ export declare function suiteCommandTarget(command: string): string;
|
|
|
64
96
|
* (`--grep-invert`) so a re-run of the failing locations cannot report one of them again —
|
|
65
97
|
* xr-molecule-flash-m101 re-ran the whole 139-test suite and spent ~4 minutes to be told about
|
|
66
98
|
* a test the request had already said does not count.
|
|
99
|
+
* @param config - The config the run loaded, when the caller knows it; added when the command
|
|
100
|
+
* does not name one.
|
|
67
101
|
* @returns The scoped command, or the command unchanged when it runs no Playwright suite.
|
|
68
102
|
*/
|
|
69
|
-
export declare function scopedRerunCommand(command: string, locations: readonly string[], excluded?: readonly string[]): string;
|
|
103
|
+
export declare function scopedRerunCommand(command: string, locations: readonly string[], excluded?: readonly string[], config?: string | null): string;
|
|
70
104
|
/**
|
|
71
105
|
* One suite run, as the rule that decides whether a second may start beside it reads it.
|
|
72
106
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAMH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAED
|
|
1
|
+
{"version":3,"file":"suite-overlap.d.ts","sourceRoot":"","sources":["../src/suite-overlap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAMH;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAoB1D;AAKD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEzD;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAc1F;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,QAAQ,GAAE,SAAS,MAAM,EAAO,EAChC,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,GACrB,MAAM,CA8BR;AAED;;;;;;GAMG;AACH,MAAM,WAAW,gBAAgB;IAC/B,+FAA+F;IAC/F,SAAS,EAAE,OAAO,CAAA;IAClB,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAA;IACd;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,gBAAgB,EACzD,SAAS,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,EAC7B,QAAQ,EAAE,SAAS,CAAC,EAAE,GACrB,CAAC,GAAG,IAAI,CAMV;AAqDD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQvD"}
|
package/dist/suite-overlap.js
CHANGED
|
@@ -74,12 +74,62 @@ export function suiteCommandTarget(command) {
|
|
|
74
74
|
}
|
|
75
75
|
return 'local';
|
|
76
76
|
}
|
|
77
|
+
/** Whether a Playwright statement already names its own config (`-c`, `--config`, `--config=`). */
|
|
78
|
+
const NAMES_CONFIG = /(?:^|\s)(?:-c|--config)(?:\s+|=)/;
|
|
79
|
+
/**
|
|
80
|
+
* Whether a Playwright statement names its own config — the reading {@link withSuiteConfig} and
|
|
81
|
+
* {@link scopedRerunCommand} share, so neither adds a second `--config` to a command that has one.
|
|
82
|
+
*
|
|
83
|
+
* @param command - A command, or the Playwright statement inside it.
|
|
84
|
+
* @returns True when the statement loads a config of its own.
|
|
85
|
+
*/
|
|
86
|
+
export function namesSuiteConfig(command) {
|
|
87
|
+
return NAMES_CONFIG.test(command);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The command with a `--config` added to the Playwright invocation it starts — or unchanged when
|
|
91
|
+
* it already names one, or starts no Playwright run.
|
|
92
|
+
*
|
|
93
|
+
* The flag rides the same statement the runner does: a `cd`, an env prefix and a `timeout` before
|
|
94
|
+
* it are left exactly as they were, and so is everything chained after the run (`| tail -50`).
|
|
95
|
+
*
|
|
96
|
+
* xr-molecule-flash-m113 (+19:09): the model's fix-round re-run dropped the
|
|
97
|
+
* `--config playwright.conduit.config.ts` both earlier runs had carried, so Playwright started with
|
|
98
|
+
* NO base URL and all 10 failures were `page.goto: Cannot navigate to invalid URL` — a failure list
|
|
99
|
+
* that says nothing about the app. The platform composes commands back to the executor too (the
|
|
100
|
+
* narrowed re-run of a finished run's failures), and a command it hands back has to carry the
|
|
101
|
+
* config the run was measured under, never the shape that caused that finding.
|
|
102
|
+
*
|
|
103
|
+
* @param command - The command as the executor sent it.
|
|
104
|
+
* @param config - The config the run loads, when the caller knows it.
|
|
105
|
+
* @returns The command with the flag, or the command unchanged.
|
|
106
|
+
*/
|
|
107
|
+
export function withSuiteConfig(command, config) {
|
|
108
|
+
if (!config)
|
|
109
|
+
return command;
|
|
110
|
+
const at = command.search(/\bplaywright\s+test\b/);
|
|
111
|
+
if (at === -1)
|
|
112
|
+
return command;
|
|
113
|
+
const head = command.slice(0, at);
|
|
114
|
+
const rest = command.slice(at);
|
|
115
|
+
const end = rest.search(/&&|\|\||;|\|/);
|
|
116
|
+
const statement = end === -1 ? rest : rest.slice(0, end);
|
|
117
|
+
const tail = end === -1 ? '' : rest.slice(end);
|
|
118
|
+
if (namesSuiteConfig(statement))
|
|
119
|
+
return command;
|
|
120
|
+
return `${head}${statement.replace(/^(playwright\s+test)\b/, `$1 --config ${shellQuote(config)}`)}${tail}`;
|
|
121
|
+
}
|
|
77
122
|
/**
|
|
78
123
|
* The whole-suite command made scoped to the tests it should re-run instead: the same
|
|
79
124
|
* invocation — same directory, same settings — with the named `file:line`s in place of the whole
|
|
80
125
|
* suite, `--last-failed` dropped (it re-runs the tests that never ran too, X1 flash-m60) and
|
|
81
126
|
* `--retries=0` set (a re-run of a known failure does not need to pay for retries).
|
|
82
127
|
*
|
|
128
|
+
* `config` is the config the run was measured under ({@link withSuiteConfig}): the narrowed re-run
|
|
129
|
+
* carries it whenever the command itself does not, so a run that dropped `--config` cannot be
|
|
130
|
+
* handed back a command with the same defect (xr-molecule-flash-m113: every failing test was
|
|
131
|
+
* `Cannot navigate to invalid URL` because the re-run loaded no base URL).
|
|
132
|
+
*
|
|
83
133
|
* @param command - The whole-suite run's command.
|
|
84
134
|
* @param locations - The failing `file:line`s to re-run, as the run's own log printed them — or,
|
|
85
135
|
* for a run that started outside the suite, the absolute form Playwright matches each file
|
|
@@ -88,9 +138,11 @@ export function suiteCommandTarget(command) {
|
|
|
88
138
|
* (`--grep-invert`) so a re-run of the failing locations cannot report one of them again —
|
|
89
139
|
* xr-molecule-flash-m101 re-ran the whole 139-test suite and spent ~4 minutes to be told about
|
|
90
140
|
* a test the request had already said does not count.
|
|
141
|
+
* @param config - The config the run loaded, when the caller knows it; added when the command
|
|
142
|
+
* does not name one.
|
|
91
143
|
* @returns The scoped command, or the command unchanged when it runs no Playwright suite.
|
|
92
144
|
*/
|
|
93
|
-
export function scopedRerunCommand(command, locations, excluded = []) {
|
|
145
|
+
export function scopedRerunCommand(command, locations, excluded = [], config) {
|
|
94
146
|
const at = command.search(/\bplaywright\s+test\b/);
|
|
95
147
|
if (at === -1)
|
|
96
148
|
return command;
|
|
@@ -110,10 +162,13 @@ export function scopedRerunCommand(command, locations, excluded = []) {
|
|
|
110
162
|
.map((t) => t.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'))
|
|
111
163
|
.join('|'))}`
|
|
112
164
|
: '';
|
|
165
|
+
// The run's own config: the command's `--config` when it has one, else the config the caller
|
|
166
|
+
// recorded for the run. A re-run without it measures nothing (m113).
|
|
167
|
+
const carry = config && !namesSuiteConfig(statement) ? ` --config ${shellQuote(config)}` : '';
|
|
113
168
|
const scoped = statement
|
|
114
169
|
.replace(/\s*--last-failed\b/g, '')
|
|
115
170
|
.replace(/\s*--retries(?:[= ]\d+)?/g, '')
|
|
116
|
-
.replace(/^(playwright\s+test)\b/, `$1 ${locations.join(' ')} --retries=0${invert}`);
|
|
171
|
+
.replace(/^(playwright\s+test)\b/, `$1 ${locations.join(' ')} --retries=0${carry}${invert}`);
|
|
117
172
|
return `${head}${scoped}${tail}`;
|
|
118
173
|
}
|
|
119
174
|
/**
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAiJ9C;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAahG;AAiOD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAmEnE;;;GAGG;AACH,eAAO,MAAM,iBAAiB,QAEA,CAAA;AAE9B;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,wBAAwB,QAI6C,CAAA;AAmBlF;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,qBAAqB;IACpC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,8FAA8F;IAC9F,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;CAC5B;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,qBAAqB,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,CAAC,CAAA;AAoBpF;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,GAAG,SAAS,CAAC,GACxE,qBAAqB,CAkBvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAsB,4BAA4B,CAChD,IAAI,EAAE,MAAM,EACZ,KAAK,CAAC,EAAE,qBAAqB,GAC5B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiCxB;AAgDD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,CAy3ExF"}
|
package/dist/tools.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { commandCutRun, CUT_AT_BUDGET, cutRunSentence, suiteRunExitCode } from './cut-run.js';
|
|
10
10
|
import { addChangeSpan, changedSpan, countLines, renderEditExcerpt, renderSmallFile, } from './edit-excerpt.js';
|
|
11
11
|
import { guardToolExecute, missingParamError, ToolInputError } from './input-normalizer.js';
|
|
12
|
+
import { pipelineExitCodeNote } from './pipeline-exit-note.js';
|
|
12
13
|
import { TOOL_SCHEMAS } from './schemas.js';
|
|
13
14
|
import { leadingSleepPeek } from './sleep-peek.js';
|
|
14
15
|
/**
|
|
@@ -2172,6 +2173,12 @@ export function buildTools(backend, config) {
|
|
|
2172
2173
|
const placement = suiteRunPipelinePlacement(streaming.command);
|
|
2173
2174
|
const reported = suiteRunExitCode(code, output, placement.piped);
|
|
2174
2175
|
const exitNote = suiteExitCodeNote(placement, reported);
|
|
2176
|
+
// The same question the foreground path asks: when the code is a filter's, say
|
|
2177
|
+
// which stage produced it (and what the output carries) rather than leaving a
|
|
2178
|
+
// 0 beside a printed error.
|
|
2179
|
+
const pipeNote = reported === null
|
|
2180
|
+
? null
|
|
2181
|
+
: pipelineExitCodeNote(streaming.command, reported, output);
|
|
2175
2182
|
const notes = [
|
|
2176
2183
|
...(sleepNote ? [sleepNote] : []),
|
|
2177
2184
|
...streaming.changes,
|
|
@@ -2181,6 +2188,7 @@ export function buildTools(backend, config) {
|
|
|
2181
2188
|
stdout,
|
|
2182
2189
|
stderr: '',
|
|
2183
2190
|
exitCode: reported,
|
|
2191
|
+
...(pipeNote === null ? {} : { exitCodeNote: pipeNote }),
|
|
2184
2192
|
...(output.length > MAX_OUTPUT_SIZE ? { truncated: true, fullOutput: log } : {}),
|
|
2185
2193
|
...(notes.length ? { note: notes.join(' ') } : {}),
|
|
2186
2194
|
};
|
|
@@ -2305,10 +2313,16 @@ export function buildTools(backend, config) {
|
|
|
2305
2313
|
(isSuiteRunCommand(command) ? SUITE_OVER_BUDGET_ADVICE : SPLIT_WORK_ADVICE),
|
|
2306
2314
|
};
|
|
2307
2315
|
}
|
|
2316
|
+
// A pipeline reports its LAST stage's code: `tsc | head` says 0 beside a printed type
|
|
2317
|
+
// error, and `tsc | head && tail missing.log` says 1 beside a clean check. Name the
|
|
2318
|
+
// stage the number came from (pipeline-exit-note.ts) — the field the executor trusts
|
|
2319
|
+
// most must not contradict the text it can read.
|
|
2320
|
+
const exitCodeNote = pipelineExitCodeNote(command, result.exitCode, `${result.stdout}\n${result.stderr}`);
|
|
2308
2321
|
return {
|
|
2309
2322
|
stdout,
|
|
2310
2323
|
stderr,
|
|
2311
2324
|
exitCode: result.exitCode,
|
|
2325
|
+
...(exitCodeNote === null ? {} : { exitCodeNote }),
|
|
2312
2326
|
...(peekedTask?.slice.trim() ? { outputSoFar: sanitizeOutput(peekedTask.slice) } : {}),
|
|
2313
2327
|
...(sleepNote || ceilingNote || traced.change
|
|
2314
2328
|
? {
|
|
@@ -32,8 +32,37 @@ export interface TraceRequest {
|
|
|
32
32
|
method: string;
|
|
33
33
|
/** The request's path (`/api/articles/x/favorite`), or the whole URL when it is cross-origin. */
|
|
34
34
|
path: string;
|
|
35
|
-
/**
|
|
35
|
+
/**
|
|
36
|
+
* The response status as the trace recorded it. Playwright writes `-1` when a request was never
|
|
37
|
+
* answered (its HAR entry starts at `-1` and only a response event overwrites it); `0` is the
|
|
38
|
+
* same absence when the snapshot carried no response at all. Neither is a server failure.
|
|
39
|
+
*/
|
|
36
40
|
status: number;
|
|
41
|
+
/**
|
|
42
|
+
* Playwright's own error text for a request that failed WITHOUT a response
|
|
43
|
+
* (`net::ERR_CONNECTION_REFUSED`) — a real failure. A request that got no response and recorded
|
|
44
|
+
* no text was aborted or still in flight when the test ended (the page closed under it), so it
|
|
45
|
+
* says nothing about the server (xr-molecule-flash-m112 +30:45).
|
|
46
|
+
*/
|
|
47
|
+
failure?: string;
|
|
48
|
+
/**
|
|
49
|
+
* What the page SENT, for a 4xx answered by the app's own API — bounded (see
|
|
50
|
+
* {@link MAX_TRACED_BODY_CHARS}). A body wrapped as `{"data":{…}}` is visible here in one line:
|
|
51
|
+
* app-http's verbs take the body as their second argument, so a helper that passes
|
|
52
|
+
* `{ data: body }` there ships the wrapper (xr-molecule-flash-m111 +11:36).
|
|
53
|
+
*/
|
|
54
|
+
sent?: string;
|
|
55
|
+
/** What came back for that 4xx, bounded the same way — the API's own error body. */
|
|
56
|
+
got?: string;
|
|
57
|
+
}
|
|
58
|
+
/** One action the traced page performed, with the trace's own timing for it. */
|
|
59
|
+
export interface TraceAction {
|
|
60
|
+
/** The Playwright API the entry names: `Frame.goto`, `Locator.fill`, `Locator.click`. */
|
|
61
|
+
api: string;
|
|
62
|
+
/** When the action started, on the trace's own monotonic clock (ms). */
|
|
63
|
+
start: number;
|
|
64
|
+
/** When it finished, or null when the run ended before it did. */
|
|
65
|
+
end: number | null;
|
|
37
66
|
}
|
|
38
67
|
/** What one trace says: the test it belongs to, the requests it made, and its console errors. */
|
|
39
68
|
export interface TraceArtifact {
|
|
@@ -45,6 +74,12 @@ export interface TraceArtifact {
|
|
|
45
74
|
requests: TraceRequest[];
|
|
46
75
|
/** The console errors and page errors the page raised. */
|
|
47
76
|
console: string[];
|
|
77
|
+
/**
|
|
78
|
+
* The page's own actions in the order the trace recorded them, bounded — what a test that ran
|
|
79
|
+
* out of a per-test budget spends its time on. A test that repeats one action prints it once per
|
|
80
|
+
* repeat, so the gap between two starts is one repeat's measured cost.
|
|
81
|
+
*/
|
|
82
|
+
actions: TraceAction[];
|
|
48
83
|
}
|
|
49
84
|
/** How many traces one read reports, newest first. */
|
|
50
85
|
export declare const MAX_TRACE_ARTIFACTS = 12;
|
|
@@ -52,12 +87,20 @@ export declare const MAX_TRACE_ARTIFACTS = 12;
|
|
|
52
87
|
export declare const MAX_TRACED_REQUESTS = 6;
|
|
53
88
|
/** How many console errors one test's trace contributes. */
|
|
54
89
|
export declare const MAX_TRACED_CONSOLE = 4;
|
|
90
|
+
/** How many of the page's own actions one test's trace contributes, the LAST ones. */
|
|
91
|
+
export declare const MAX_TRACED_ACTIONS = 40;
|
|
92
|
+
/** How much of a 4xx request's sent body (or its response body) the trace read keeps. */
|
|
93
|
+
export declare const MAX_TRACED_BODY_CHARS = 300;
|
|
55
94
|
/** How many tests the note in a run's result names. */
|
|
56
95
|
export declare const MAX_TRACE_NOTE_TESTS = 5;
|
|
57
96
|
/** How many requests the note names per test. */
|
|
58
97
|
export declare const MAX_TRACE_NOTE_REQUESTS = 4;
|
|
59
98
|
/** How long a URL the note prints, before the middle is cut. */
|
|
60
99
|
export declare const TRACE_NOTE_URL_CHARS = 96;
|
|
100
|
+
/** How much of the sent body the note prints — enough to see a `{"data":…}` wrapper. */
|
|
101
|
+
export declare const TRACE_NOTE_SENT_CHARS = 160;
|
|
102
|
+
/** How much of the response body the note prints — the API's first error line. */
|
|
103
|
+
export declare const TRACE_NOTE_GOT_CHARS = 120;
|
|
61
104
|
/**
|
|
62
105
|
* The Node program the sandbox runs to read a failed run's traces: `node <script> <dir>` prints one
|
|
63
106
|
* `TRACE <json>` line per `trace.zip` under `<dir>` (newest first, {@link MAX_TRACE_ARTIFACTS} of
|
|
@@ -69,10 +112,17 @@ export declare const TRACE_NOTE_URL_CHARS = 96;
|
|
|
69
112
|
* central directory and `zlib.inflateRawSync` — about forty lines, and the only thing that has to
|
|
70
113
|
* be true of a `trace.zip` is that it IS one.
|
|
71
114
|
*
|
|
72
|
-
* A request is kept over a static asset when it failed
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
115
|
+
* A request is kept over a static asset when it failed — a `status >= 400`, or no response at all
|
|
116
|
+
* together with Playwright's own error text (`-1` plus `_failureText`, a refused connection) — and
|
|
117
|
+
* over anything else when it is the page talking to the app (`fetch`/`xhr`/`document`): a failing
|
|
118
|
+
* test's trace holds the whole page load. A request with no response and NO error text was aborted
|
|
119
|
+
* or still in flight when the test ended; it is kept too, but as what it is, not as a failure.
|
|
120
|
+
*
|
|
121
|
+
* A request answered 4xx by the app's OWN API also keeps what the page SENT and the first of what
|
|
122
|
+
* came back: Playwright stores either inline (`text`) or in a resource entry (`_file`), and both
|
|
123
|
+
* are read. That is where a wrapped body shows: xr-molecule-flash-m111's helper passed
|
|
124
|
+
* `{ data: body }` as app-http's second argument, so every POST/PUT was `{"data":{…}}` and the API
|
|
125
|
+
* validated `{}`. Status alone said only "422".
|
|
76
126
|
*
|
|
77
127
|
* @returns The program's source, to write to a file and run.
|
|
78
128
|
*/
|
|
@@ -97,11 +147,31 @@ export declare function parseTraceArtifacts(stdout: string): TraceArtifact[];
|
|
|
97
147
|
* {@link MAX_TRACE_NOTE_TESTS} tests with {@link MAX_TRACE_NOTE_REQUESTS} requests each, because it
|
|
98
148
|
* rides in a result the model pays for.
|
|
99
149
|
*
|
|
150
|
+
* The three kinds are kept apart, because they mean different things. A FAILED request (4xx/5xx, or
|
|
151
|
+
* a network error with Playwright's own text) is a cause. A request with NO RESPONSE RECORDED —
|
|
152
|
+
* Playwright's `-1`, no error text — was still in flight when the test ended (the page closed under
|
|
153
|
+
* it); listing it as a failure sent a run hunting an API that never answered anything wrong
|
|
154
|
+
* (xr-molecule-flash-m112 +30:45). The rest are the page's last answered requests.
|
|
155
|
+
*
|
|
156
|
+
* A 4xx request carries what the page SENT beside its status, so a wrapped body is visible in one
|
|
157
|
+
* line rather than inferred from the status; when one of those bodies is an object whose only key
|
|
158
|
+
* is `data`, the note names the call shape that produces it (xr-molecule-flash-m111 +11:36).
|
|
159
|
+
*
|
|
160
|
+
* WHICH rows it spends is evidence, not artifact order (xr-molecule-flash-m112 +53:46): a row
|
|
161
|
+
* needs a recorded response, a real failure or a console error ({@link traceRowEvidence}), rows are
|
|
162
|
+
* deduplicated by the test's location, and they are ordered real failure → repeated from an
|
|
163
|
+
* earlier walk → the rest ({@link traceRowTier}). Without that the note spent all five rows on four
|
|
164
|
+
* traces of one `afterEach` line whose every request was `-1`, and left the favorite cause — the
|
|
165
|
+
* one failure that repeated in every walk — in its "… and N more".
|
|
166
|
+
*
|
|
100
167
|
* @param items - The traces {@link parseTraceArtifacts} read.
|
|
101
168
|
* @param target - Where the run went — the deployed host, as the note names it.
|
|
102
|
-
* @
|
|
169
|
+
* @param earlierFailures - The failing `file:line`s earlier walks of this suite left, so a repeat
|
|
170
|
+
* is ranked above a one-off. Empty when the caller knows of no earlier walk.
|
|
171
|
+
* @returns The note, or null when no trace held a recorded response, a real failure or a console
|
|
172
|
+
* error.
|
|
103
173
|
*/
|
|
104
|
-
export declare function failureTraceNote(items: readonly TraceArtifact[], target: string): string | null;
|
|
174
|
+
export declare function failureTraceNote(items: readonly TraceArtifact[], target: string, earlierFailures?: readonly string[]): string | null;
|
|
105
175
|
/**
|
|
106
176
|
* Whether a Playwright config records traces for the tests that fail.
|
|
107
177
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAA;IACZ,
|
|
1
|
+
{"version":3,"file":"trace-artifacts.d.ts","sourceRoot":"","sources":["../src/trace-artifacts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAMH,iEAAiE;AACjE,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAA;IACd,iGAAiG;IACjG,IAAI,EAAE,MAAM,CAAA;IACZ;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,GAAG,CAAC,EAAE,MAAM,CAAA;CACb;AAED,gFAAgF;AAChF,MAAM,WAAW,WAAW;IAC1B,yFAAyF;IACzF,GAAG,EAAE,MAAM,CAAA;IACX,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAA;IACb,kEAAkE;IAClE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACnB;AAED,iGAAiG;AACjG,MAAM,WAAW,aAAa;IAC5B,0FAA0F;IAC1F,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,+EAA+E;IAC/E,IAAI,EAAE,MAAM,CAAA;IACZ,6FAA6F;IAC7F,QAAQ,EAAE,YAAY,EAAE,CAAA;IACxB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB;;;;OAIG;IACH,OAAO,EAAE,WAAW,EAAE,CAAA;CACvB;AAED,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,KAAK,CAAA;AAErC,sDAAsD;AACtD,eAAO,MAAM,mBAAmB,IAAI,CAAA;AAEpC,4DAA4D;AAC5D,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAEnC,sFAAsF;AACtF,eAAO,MAAM,kBAAkB,KAAK,CAAA;AAEpC,yFAAyF;AACzF,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC,uDAAuD;AACvD,eAAO,MAAM,oBAAoB,IAAI,CAAA;AAErC,iDAAiD;AACjD,eAAO,MAAM,uBAAuB,IAAI,CAAA;AAExC,gEAAgE;AAChE,eAAO,MAAM,oBAAoB,KAAK,CAAA;AAEtC,wFAAwF;AACxF,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC,kFAAkF;AAClF,eAAO,MAAM,oBAAoB,MAAM,CAAA;AAKvC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,oBAAoB,QAAO,MAiQvC,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAqCnE;AAwJD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,MAAM,EAAE,MAAM,EACd,eAAe,GAAE,SAAS,MAAM,EAAO,GACtC,MAAM,GAAG,IAAI,CAmFf;AAaD;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAG9D;AAED,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,8BAA8B,CAAA;AA2G/D,oGAAoG;AACpG,MAAM,WAAW,qBAAqB;IACpC,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,4EAA4E;IAC5E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;CACtB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,qBAAqB,CAAC,IAAI,EAAE;IAChD,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CAC5C,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAyBjC"}
|