@windsland52/maa-log-tools 1.2.0 → 1.2.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 +6 -7
- package/dist/cli.js +1 -1
- package/dist/runtimeInspection.d.ts +13 -20
- package/dist/runtimeInspection.js +31 -21
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -71,7 +71,7 @@ process-start-bounded interval contains the timestamp; otherwise it returns `nul
|
|
|
71
71
|
|
|
72
72
|
## Runtime inspection
|
|
73
73
|
|
|
74
|
-
`buildRuntimeInspection(kernelOutput, frameworkExtraction, sourceSegments
|
|
74
|
+
`buildRuntimeInspection(kernelOutput, frameworkExtraction, sourceSegments)` emits
|
|
75
75
|
`mla-runtime-inspection/v1`. It nests task executions under their runtime session and keeps three
|
|
76
76
|
different semantics separate:
|
|
77
77
|
|
|
@@ -92,9 +92,8 @@ ambiguous tasks remain in `unscopedTasks` and produce a warning.
|
|
|
92
92
|
Use `--runtime-inspection` to emit this result directly from a file, zip, or directory. It is a
|
|
93
93
|
separate output mode from `--preflight`; the two flags are mutually exclusive.
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
file. Callers without segments leave these fields as `null`.
|
|
95
|
+
Each evidence position carries `source`, `path`, and `localLine` describing which original log
|
|
96
|
+
file the evidence came from and its 1-based line within that file. Segments are built by the Node
|
|
97
|
+
extraction helpers (`loadNodeLogDirectory`, `extractZipContentFromNodeFile`) when merging multiple
|
|
98
|
+
log files and are required by `buildRuntimeInspection`; `localLine` is the offset within the
|
|
99
|
+
individual source file, not the merged parser input line.
|
package/dist/cli.js
CHANGED
|
@@ -108,7 +108,7 @@ export const main = async () => {
|
|
|
108
108
|
? extractFrameworkSessions(await loadFrameworkLogSources(resolvedPath))
|
|
109
109
|
: EMPTY_FRAMEWORK_EXTRACTION;
|
|
110
110
|
let result = null;
|
|
111
|
-
let sourceSegments;
|
|
111
|
+
let sourceSegments = [];
|
|
112
112
|
if (targetStat.isDirectory()) {
|
|
113
113
|
const extracted = await loadNodeLogDirectory(resolvedPath);
|
|
114
114
|
if (extracted) {
|
|
@@ -3,7 +3,6 @@ import type { FrameworkSession, FrameworkSessionExtraction } from './frameworkVe
|
|
|
3
3
|
export declare const MLA_RUNTIME_INSPECTION_SCHEMA_VERSION = "mla-runtime-inspection/v1";
|
|
4
4
|
export interface RuntimeEvidencePosition {
|
|
5
5
|
timestamp: string | null;
|
|
6
|
-
parserInputLine: number | null;
|
|
7
6
|
source: string | null;
|
|
8
7
|
path: string | null;
|
|
9
8
|
localLine: number | null;
|
|
@@ -62,6 +61,15 @@ export interface RecognitionOccurrenceSample {
|
|
|
62
61
|
end: RuntimeEvidencePosition;
|
|
63
62
|
};
|
|
64
63
|
}
|
|
64
|
+
export interface RepeatedNodeOccurrenceSample {
|
|
65
|
+
pattern: string[];
|
|
66
|
+
firstSeenAt: string;
|
|
67
|
+
lastSeenAt: string;
|
|
68
|
+
repeatCount: number;
|
|
69
|
+
durationMs: number;
|
|
70
|
+
termination: 'left_pattern' | 'task_ended' | 'still_repeating_at_log_end';
|
|
71
|
+
evidence: RuntimeEvidencePosition;
|
|
72
|
+
}
|
|
65
73
|
export interface RecognitionActivitySignal extends RuntimeScope {
|
|
66
74
|
signalId: string;
|
|
67
75
|
kind: 'recognition_activity';
|
|
@@ -117,24 +125,9 @@ export interface RepeatedNodeSequenceSignal extends RuntimeScope {
|
|
|
117
125
|
stillRepeatingAtLogEnd: number;
|
|
118
126
|
};
|
|
119
127
|
representatives: {
|
|
120
|
-
first:
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
repeatCount: number;
|
|
124
|
-
evidence: RuntimeEvidencePosition;
|
|
125
|
-
};
|
|
126
|
-
longest: {
|
|
127
|
-
firstSeenAt: string;
|
|
128
|
-
lastSeenAt: string;
|
|
129
|
-
repeatCount: number;
|
|
130
|
-
evidence: RuntimeEvidencePosition;
|
|
131
|
-
};
|
|
132
|
-
last: {
|
|
133
|
-
firstSeenAt: string;
|
|
134
|
-
lastSeenAt: string;
|
|
135
|
-
repeatCount: number;
|
|
136
|
-
evidence: RuntimeEvidencePosition;
|
|
137
|
-
};
|
|
128
|
+
first: RepeatedNodeOccurrenceSample;
|
|
129
|
+
longest: RepeatedNodeOccurrenceSample;
|
|
130
|
+
last: RepeatedNodeOccurrenceSample;
|
|
138
131
|
};
|
|
139
132
|
detector: {
|
|
140
133
|
name: 'repeated-completed-node-sequence';
|
|
@@ -221,5 +214,5 @@ export interface RuntimeInspection {
|
|
|
221
214
|
signals: RuntimeSignal[];
|
|
222
215
|
warnings: string[];
|
|
223
216
|
}
|
|
224
|
-
export declare const buildRuntimeInspection: (output: KernelOutput, framework: FrameworkSessionExtraction, sourceSegments
|
|
217
|
+
export declare const buildRuntimeInspection: (output: KernelOutput, framework: FrameworkSessionExtraction, sourceSegments: readonly SourceSegment[]) => RuntimeInspection;
|
|
225
218
|
export {};
|
|
@@ -30,11 +30,11 @@ const evidence = (index, timestamp) => {
|
|
|
30
30
|
if (timestamp) {
|
|
31
31
|
const exactLine = index.exactLines.get(timestamp);
|
|
32
32
|
if (exactLine != null)
|
|
33
|
-
return { timestamp,
|
|
33
|
+
return { timestamp, mergedLine: exactLine };
|
|
34
34
|
}
|
|
35
35
|
const target = timestampMs(timestamp);
|
|
36
36
|
if (target == null || index.ordered.length === 0) {
|
|
37
|
-
return { timestamp: timestamp ?? null,
|
|
37
|
+
return { timestamp: timestamp ?? null, mergedLine: null };
|
|
38
38
|
}
|
|
39
39
|
let low = 0;
|
|
40
40
|
let high = index.ordered.length;
|
|
@@ -50,7 +50,7 @@ const evidence = (index, timestamp) => {
|
|
|
50
50
|
const nearest = before == null
|
|
51
51
|
? after
|
|
52
52
|
: after == null || target - before.timestamp <= after.timestamp - target ? before : after;
|
|
53
|
-
return { timestamp: timestamp ?? null,
|
|
53
|
+
return { timestamp: timestamp ?? null, mergedLine: nearest?.line ?? null };
|
|
54
54
|
};
|
|
55
55
|
const recognitionItems = (items) => ((items ?? []).flatMap(item => [
|
|
56
56
|
...(item.type === 'recognition' || item.type === 'recognition_node' ? [item] : []),
|
|
@@ -161,25 +161,27 @@ const canonicalCycle = (pattern) => {
|
|
|
161
161
|
rotations.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
162
162
|
return rotations[0] ?? [...pattern];
|
|
163
163
|
};
|
|
164
|
-
const findSourceSegment = (segments,
|
|
164
|
+
const findSourceSegment = (segments, mergedLine) => {
|
|
165
165
|
for (const segment of segments) {
|
|
166
|
-
if (
|
|
166
|
+
if (mergedLine >= segment.startLine && mergedLine < segment.startLine + segment.lineCount) {
|
|
167
167
|
return segment;
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
170
|
return null;
|
|
171
171
|
};
|
|
172
172
|
const enrichWithSource = (position, segments) => {
|
|
173
|
-
if (
|
|
174
|
-
return position;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
173
|
+
if (position.mergedLine == null) {
|
|
174
|
+
return { timestamp: position.timestamp, source: null, path: null, localLine: null };
|
|
175
|
+
}
|
|
176
|
+
const segment = findSourceSegment(segments, position.mergedLine);
|
|
177
|
+
if (!segment) {
|
|
178
|
+
return { timestamp: position.timestamp, source: null, path: null, localLine: null };
|
|
179
|
+
}
|
|
178
180
|
return {
|
|
179
|
-
|
|
181
|
+
timestamp: position.timestamp,
|
|
180
182
|
source: segment.source,
|
|
181
183
|
path: segment.path,
|
|
182
|
-
localLine: position.
|
|
184
|
+
localLine: position.mergedLine - segment.startLine + 1,
|
|
183
185
|
};
|
|
184
186
|
};
|
|
185
187
|
const createEvidenceFn = (segments) => (index, timestamp) => enrichWithSource(evidence(index, timestamp), segments);
|
|
@@ -393,21 +395,30 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
393
395
|
const last = completed[lastIndex];
|
|
394
396
|
if (!first || !last)
|
|
395
397
|
continue;
|
|
396
|
-
const reachesEnd = lastIndex === completed.length - 1;
|
|
397
398
|
const rawPattern = completed.slice(repeated.start, repeated.start + repeated.length).map(node => node.name);
|
|
398
399
|
const pattern = repeated.length === 1 ? rawPattern : canonicalCycle(rawPattern);
|
|
399
400
|
const kind = repeated.length === 1 ? 'repeated_node' : 'repeated_node_cycle';
|
|
400
401
|
const key = JSON.stringify([kind, pattern]);
|
|
401
402
|
const group = repetitionGroups.get(key) ?? [];
|
|
403
|
+
const timelineLastIndex = timeline.findIndex(item => item.nodeInfo === last);
|
|
404
|
+
const trailing = timelineLastIndex < 0 ? [] : timeline.slice(timelineLastIndex + 1);
|
|
405
|
+
const reachesCompletedEnd = lastIndex === completed.length - 1;
|
|
406
|
+
const continuesAtLogEnd = reachesCompletedEnd
|
|
407
|
+
&& task.status === 'running'
|
|
408
|
+
&& trailing.every((item, offset) => (item.nodeInfo.status === 'running'
|
|
409
|
+
&& item.nodeInfo.name === rawPattern[offset % rawPattern.length]));
|
|
410
|
+
const taskEndedAtPattern = reachesCompletedEnd
|
|
411
|
+
&& timelineLastIndex === timeline.length - 1
|
|
412
|
+
&& task.status !== 'running';
|
|
402
413
|
group.push({
|
|
403
414
|
pattern,
|
|
404
415
|
repeatCount: repeated.count,
|
|
405
416
|
durationMs: elapsed(first.ts, last.end_ts ?? last.ts) ?? 0,
|
|
406
417
|
firstSeenAt: first.ts,
|
|
407
418
|
lastSeenAt: last.end_ts ?? last.ts,
|
|
408
|
-
termination:
|
|
409
|
-
?
|
|
410
|
-
: 'left_pattern',
|
|
419
|
+
termination: continuesAtLogEnd
|
|
420
|
+
? 'still_repeating_at_log_end'
|
|
421
|
+
: taskEndedAtPattern ? 'task_ended' : 'left_pattern',
|
|
411
422
|
evidence: evidenceAt(evidenceIndex, first.ts),
|
|
412
423
|
});
|
|
413
424
|
repetitionGroups.set(key, group);
|
|
@@ -427,8 +438,6 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
427
438
|
const totalRepeatCount = group.reduce((sum, item) => sum + item.repeatCount, 0);
|
|
428
439
|
const maximumRepeatCount = Math.max(...group.map(item => item.repeatCount));
|
|
429
440
|
const repetitionReasons = [];
|
|
430
|
-
if (terminations.leftPattern > 0)
|
|
431
|
-
repetitionReasons.push('incomplete_repetition');
|
|
432
441
|
if (terminations.stillRepeatingAtLogEnd > 0)
|
|
433
442
|
repetitionReasons.push('still_repeating_at_log_end');
|
|
434
443
|
if (maximumRepeatCount >= 10 || totalRepeatCount >= 20)
|
|
@@ -481,8 +490,9 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
481
490
|
const visionImages = imageSets.flatMap(set => set.vision);
|
|
482
491
|
const ownFailures = failures.filter(failure => directFailureIds.includes(failure.failureId));
|
|
483
492
|
const ownSignals = signals.filter(signal => signalIds.includes(signal.signalId));
|
|
484
|
-
const
|
|
485
|
-
.filter((signal) => (signal.kind === 'recognition_activity'))
|
|
493
|
+
const recognitionSignals = ownSignals
|
|
494
|
+
.filter((signal) => (signal.kind === 'recognition_activity'));
|
|
495
|
+
const recognitionActivity = [...recognitionSignals]
|
|
486
496
|
.sort((left, right) => (right.unsuccessfulAttempts.maximum - left.unsuccessfulAttempts.maximum
|
|
487
497
|
|| right.occurrenceCount - left.occurrenceCount))
|
|
488
498
|
.slice(0, 5)
|
|
@@ -517,7 +527,7 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
517
527
|
const statuses = new Set(attempts.map(attempt => attempt.status));
|
|
518
528
|
return statuses.has('failed') && statuses.has('success');
|
|
519
529
|
}).length,
|
|
520
|
-
recognitionActivityGroups:
|
|
530
|
+
recognitionActivityGroups: recognitionSignals.length,
|
|
521
531
|
maximumRecognitionAttemptsPerNode: Math.max(0, ...attemptsByNode.map(attempts => attempts.length)),
|
|
522
532
|
maximumUnsuccessfulRecognitionAttemptsPerNode: Math.max(0, ...attemptsByNode.map(attempts => attempts.filter(attempt => attempt.status === 'failed').length)),
|
|
523
533
|
actionAttempts: timeline.filter(item => item.nodeInfo.action_details != null).length,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windsland52/maa-log-tools",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"fflate": "^0.8.2",
|
|
45
45
|
"@windsland52/maa-log-adapter": "1.0.1",
|
|
46
|
-
"@windsland52/maa-log-parser": "1.0.1",
|
|
47
46
|
"@windsland52/maa-log-runtime": "1.0.1",
|
|
48
|
-
"@windsland52/maa-log-kernel": "1.0.1"
|
|
47
|
+
"@windsland52/maa-log-kernel": "1.0.1",
|
|
48
|
+
"@windsland52/maa-log-parser": "1.0.1"
|
|
49
49
|
},
|
|
50
50
|
"engines": {
|
|
51
51
|
"node": ">=24.0.0"
|