@windsland52/maa-log-tools 1.3.0 → 2.0.0
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 +7 -1
- package/dist/archiveLimits.d.ts +1 -3
- package/dist/archiveLimits.js +42 -48
- package/dist/boundedFileReader.js +3 -3
- package/dist/cli.js +7 -7
- package/dist/frameworkInput.js +9 -5
- package/dist/frameworkVersion.js +7 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/nextListPresentation.js +1 -1
- package/dist/nodeExecutionName.js +1 -1
- package/dist/nodeExecutionTimeline.js +1 -1
- package/dist/nodeInput.js +22 -17
- package/dist/runtimeInspection.js +233 -87
- package/dist/taskExecutionOrder.js +1 -1
- package/dist/taskIdentity.js +7 -5
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -87,7 +87,9 @@ process-start-bounded interval contains the timestamp; otherwise it returns `nul
|
|
|
87
87
|
`mla-runtime-inspection/v1`. It nests task executions under their runtime session and keeps three
|
|
88
88
|
different semantics separate:
|
|
89
89
|
|
|
90
|
-
- `failures`:
|
|
90
|
+
- `failures`: directly observed `next_list_timeout` and `action_failed` facts, including failures
|
|
91
|
+
inside tasks launched by custom actions. Nested failures retain the nested task identity and
|
|
92
|
+
images while sharing the enclosing top-level `executionId`.
|
|
91
93
|
- `outcomes`: failed or still-running pipeline nodes and tasks, with direct-failure references
|
|
92
94
|
when the propagation can be linked deterministically.
|
|
93
95
|
- `signals`: useful non-failure behavior such as recognition succeeding after earlier misses and
|
|
@@ -97,6 +99,10 @@ An unsuccessful recognition attempt is retry telemetry, not a failure. A next-li
|
|
|
97
99
|
reported only when the node finishes without matching a candidate. Repeated recognition attempts
|
|
98
100
|
inside one node are not treated as pipeline loops.
|
|
99
101
|
|
|
102
|
+
`RuntimeTaskExecution.directFailureIds` and its failure statistics remain limited to the
|
|
103
|
+
top-level task's own pipeline nodes. Nested task failures are available through `failures` and
|
|
104
|
+
`outcomes`, so a propagated parent action failure and its underlying nested failure stay distinct.
|
|
105
|
+
|
|
100
106
|
Tasks are assigned to a process-start session by timestamp. A file segment without a
|
|
101
107
|
`MAA Process Start` marker can also contain tasks when it is the only matching partial interval;
|
|
102
108
|
ambiguous tasks remain in `unscopedTasks` and produce a warning.
|
package/dist/archiveLimits.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import { type Unzipped } from 'fflate';
|
|
|
2
2
|
export interface ArchiveLimits {
|
|
3
3
|
maxVolumes: number;
|
|
4
4
|
maxCompressedBytes: number;
|
|
5
|
-
maxEntries: number;
|
|
6
5
|
maxPathBytes: number;
|
|
7
6
|
maxTotalPathBytes: number;
|
|
8
7
|
maxFileBytes: number;
|
|
@@ -11,7 +10,7 @@ export interface ArchiveLimits {
|
|
|
11
10
|
maxCompressionRatio: number;
|
|
12
11
|
compressionRatioMinBytes: number;
|
|
13
12
|
}
|
|
14
|
-
export type ArchiveLimitCode = 'volume-count' | 'compressed-size' | '
|
|
13
|
+
export type ArchiveLimitCode = 'volume-count' | 'compressed-size' | 'path-size' | 'total-path-size' | 'file-size' | 'image-size' | 'extracted-size' | 'compression-ratio';
|
|
15
14
|
export declare class ArchiveLimitError extends Error {
|
|
16
15
|
readonly code: ArchiveLimitCode;
|
|
17
16
|
readonly actual: number;
|
|
@@ -33,7 +32,6 @@ export interface ArchiveEntryMetadata {
|
|
|
33
32
|
compression: number;
|
|
34
33
|
}
|
|
35
34
|
export interface ArchiveDirectoryBudget {
|
|
36
|
-
entryCount: number;
|
|
37
35
|
totalPathBytes: number;
|
|
38
36
|
}
|
|
39
37
|
export interface ExtractionBudget {
|
package/dist/archiveLimits.js
CHANGED
|
@@ -21,7 +21,6 @@ export class ArchiveFormatError extends Error {
|
|
|
21
21
|
export const DEFAULT_ARCHIVE_LIMITS = Object.freeze({
|
|
22
22
|
maxVolumes: 16,
|
|
23
23
|
maxCompressedBytes: 268435456,
|
|
24
|
-
maxEntries: 10000,
|
|
25
24
|
maxPathBytes: 4096,
|
|
26
25
|
maxTotalPathBytes: 8388608,
|
|
27
26
|
maxFileBytes: 268435456,
|
|
@@ -33,7 +32,6 @@ export const DEFAULT_ARCHIVE_LIMITS = Object.freeze({
|
|
|
33
32
|
const integerLimitKeys = [
|
|
34
33
|
'maxVolumes',
|
|
35
34
|
'maxCompressedBytes',
|
|
36
|
-
'maxEntries',
|
|
37
35
|
'maxPathBytes',
|
|
38
36
|
'maxTotalPathBytes',
|
|
39
37
|
'maxFileBytes',
|
|
@@ -58,7 +56,6 @@ export const resolveArchiveLimits = (overrides = {}) => validateLimits({
|
|
|
58
56
|
...overrides,
|
|
59
57
|
});
|
|
60
58
|
export const EMPTY_ARCHIVE_DIRECTORY_BUDGET = Object.freeze({
|
|
61
|
-
entryCount: 0,
|
|
62
59
|
totalPathBytes: 0,
|
|
63
60
|
});
|
|
64
61
|
export const EMPTY_EXTRACTION_BUDGET = Object.freeze({
|
|
@@ -111,13 +108,13 @@ const canonicalizeArchivePath = (rawPath) => {
|
|
|
111
108
|
}
|
|
112
109
|
const segments = canonical.split('/');
|
|
113
110
|
for (const [index, segment] of segments.entries()) {
|
|
114
|
-
if (segment.length === 0
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
111
|
+
if (segment.length === 0 ||
|
|
112
|
+
segment === '.' ||
|
|
113
|
+
segment === '..' ||
|
|
114
|
+
segment.endsWith('.') ||
|
|
115
|
+
segment.endsWith(' ') ||
|
|
116
|
+
segment.includes(':') ||
|
|
117
|
+
(index === 0 && /^[a-z]:$/iu.test(segment))) {
|
|
121
118
|
throwFormatError('invalid-path', rawPath, `Archive entry uses a path alias: ${rawPath}`);
|
|
122
119
|
}
|
|
123
120
|
}
|
|
@@ -133,10 +130,6 @@ export const addArchiveDirectoryEntry = (current, entry, limits = DEFAULT_ARCHIV
|
|
|
133
130
|
assertMetadataInteger(entry.size, 'compressed entry size');
|
|
134
131
|
assertMetadataInteger(entry.originalSize, 'original entry size');
|
|
135
132
|
assertMetadataInteger(entry.compression, 'compression method');
|
|
136
|
-
const entryCount = addSize(current.entryCount, 1, 'entry count');
|
|
137
|
-
if (entryCount > limits.maxEntries) {
|
|
138
|
-
throwLimitError('entry-count', entryCount, limits.maxEntries);
|
|
139
|
-
}
|
|
140
133
|
const pathBytes = utf8Encoder.encode(entry.name).byteLength;
|
|
141
134
|
if (pathBytes > limits.maxPathBytes) {
|
|
142
135
|
throwLimitError('path-size', pathBytes, limits.maxPathBytes);
|
|
@@ -145,7 +138,7 @@ export const addArchiveDirectoryEntry = (current, entry, limits = DEFAULT_ARCHIV
|
|
|
145
138
|
if (totalPathBytes > limits.maxTotalPathBytes) {
|
|
146
139
|
throwLimitError('total-path-size', totalPathBytes, limits.maxTotalPathBytes);
|
|
147
140
|
}
|
|
148
|
-
return {
|
|
141
|
+
return { totalPathBytes };
|
|
149
142
|
};
|
|
150
143
|
const copyEntryMetadata = (entry) => ({
|
|
151
144
|
name: entry.name,
|
|
@@ -163,10 +156,11 @@ const readU32 = (data, offset) => {
|
|
|
163
156
|
if (offset < 0 || offset + 4 > data.byteLength) {
|
|
164
157
|
throwFormatError('invalid-structure', '', 'ZIP record is truncated');
|
|
165
158
|
}
|
|
166
|
-
return (data[offset]
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
159
|
+
return ((data[offset] |
|
|
160
|
+
(data[offset + 1] << 8) |
|
|
161
|
+
(data[offset + 2] << 16) |
|
|
162
|
+
(data[offset + 3] << 24)) >>>
|
|
163
|
+
0);
|
|
170
164
|
};
|
|
171
165
|
const findEndOfCentralDirectory = (data) => {
|
|
172
166
|
const minimumOffset = Math.max(0, data.byteLength - 65557);
|
|
@@ -196,12 +190,12 @@ const parseAndValidateRawZipRecords = (data) => {
|
|
|
196
190
|
const totalEntries = readU16(data, eocdOffset + 10);
|
|
197
191
|
const centralSize = readU32(data, eocdOffset + 12);
|
|
198
192
|
const centralOffset = readU32(data, eocdOffset + 16);
|
|
199
|
-
if (diskNumber !== 0
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
193
|
+
if (diskNumber !== 0 ||
|
|
194
|
+
centralDisk !== 0 ||
|
|
195
|
+
entriesOnDisk !== totalEntries ||
|
|
196
|
+
totalEntries === 0xffff ||
|
|
197
|
+
centralSize === 4294967295 ||
|
|
198
|
+
centralOffset === 4294967295) {
|
|
205
199
|
throwFormatError('unsupported-archive', '', 'Multi-disk and ZIP64 archives are not supported');
|
|
206
200
|
}
|
|
207
201
|
if (centralOffset + centralSize !== eocdOffset) {
|
|
@@ -257,24 +251,26 @@ const parseAndValidateRawZipRecords = (data) => {
|
|
|
257
251
|
const extraBytes = readU16(data, localOffset + 28);
|
|
258
252
|
const payloadOffset = localOffset + 30 + nameBytes + extraBytes;
|
|
259
253
|
const rawLocalName = data.subarray(localOffset + 30, localOffset + 30 + nameBytes);
|
|
260
|
-
if (payloadOffset > centralOffset
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
254
|
+
if (payloadOffset > centralOffset ||
|
|
255
|
+
localFlags !== entry.flags ||
|
|
256
|
+
localCompression !== entry.compression ||
|
|
257
|
+
!equalBytes(rawLocalName, entry.rawName)) {
|
|
264
258
|
throwFormatError('local-entry-mismatch', '', 'ZIP local and central entry declarations differ');
|
|
265
259
|
}
|
|
266
260
|
if ((localFlags & 1) !== 0) {
|
|
267
261
|
throwFormatError('unsupported-archive', '', 'Encrypted ZIP entries are not supported');
|
|
268
262
|
}
|
|
269
263
|
const usesDescriptor = (localFlags & 8) !== 0;
|
|
270
|
-
if (!usesDescriptor &&
|
|
271
|
-
|
|
272
|
-
|
|
264
|
+
if (!usesDescriptor &&
|
|
265
|
+
(localCrc32 !== entry.crc32 ||
|
|
266
|
+
localSize !== entry.size ||
|
|
267
|
+
localOriginalSize !== entry.originalSize)) {
|
|
273
268
|
throwFormatError('declared-size-mismatch', '', 'ZIP local and central sizes differ');
|
|
274
269
|
}
|
|
275
|
-
if (usesDescriptor &&
|
|
276
|
-
|
|
277
|
-
|
|
270
|
+
if (usesDescriptor &&
|
|
271
|
+
((localCrc32 !== 0 && localCrc32 !== entry.crc32) ||
|
|
272
|
+
(localSize !== 0 && localSize !== entry.size) ||
|
|
273
|
+
(localOriginalSize !== 0 && localOriginalSize !== entry.originalSize))) {
|
|
278
274
|
throwFormatError('declared-size-mismatch', '', 'ZIP streaming local sizes conflict with the central directory');
|
|
279
275
|
}
|
|
280
276
|
const payloadEnd = payloadOffset + entry.size;
|
|
@@ -290,9 +286,9 @@ const parseAndValidateRawZipRecords = (data) => {
|
|
|
290
286
|
const descriptorSize = readU32(data, recordEnd + 4);
|
|
291
287
|
const descriptorOriginalSize = readU32(data, recordEnd + 8);
|
|
292
288
|
recordEnd += 12;
|
|
293
|
-
if (descriptorCrc32 !== entry.crc32
|
|
294
|
-
|
|
295
|
-
|
|
289
|
+
if (descriptorCrc32 !== entry.crc32 ||
|
|
290
|
+
descriptorSize !== entry.size ||
|
|
291
|
+
descriptorOriginalSize !== entry.originalSize) {
|
|
296
292
|
throwFormatError('declared-size-mismatch', '', 'ZIP data descriptor conflicts with the central directory');
|
|
297
293
|
}
|
|
298
294
|
}
|
|
@@ -320,10 +316,10 @@ export const inspectZipDirectory = (data, limits = DEFAULT_ARCHIVE_LIMITS) => {
|
|
|
320
316
|
filter: (entry) => {
|
|
321
317
|
const metadata = copyEntryMetadata(entry);
|
|
322
318
|
const rawEntry = rawEntries[entries.length];
|
|
323
|
-
if (!rawEntry
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
319
|
+
if (!rawEntry ||
|
|
320
|
+
rawEntry.size !== metadata.size ||
|
|
321
|
+
rawEntry.originalSize !== metadata.originalSize ||
|
|
322
|
+
rawEntry.compression !== metadata.compression) {
|
|
327
323
|
throwFormatError('local-entry-mismatch', metadata.name, `ZIP parsed metadata is inconsistent for ${metadata.name}`);
|
|
328
324
|
}
|
|
329
325
|
const archivePath = canonicalizeArchivePath(metadata.name);
|
|
@@ -357,9 +353,9 @@ export const addSelectedEntry = (current, entry, limits = DEFAULT_ARCHIVE_LIMITS
|
|
|
357
353
|
if (extractedBytes > limits.maxExtractedBytes) {
|
|
358
354
|
throwLimitError('extracted-size', extractedBytes, limits.maxExtractedBytes);
|
|
359
355
|
}
|
|
360
|
-
if (checkCompressionRatio
|
|
361
|
-
|
|
362
|
-
|
|
356
|
+
if (checkCompressionRatio &&
|
|
357
|
+
entry.originalSize >= limits.compressionRatioMinBytes &&
|
|
358
|
+
entry.originalSize > 0) {
|
|
363
359
|
const ratio = entry.size === 0 ? Number.POSITIVE_INFINITY : entry.originalSize / entry.size;
|
|
364
360
|
if (ratio > limits.maxCompressionRatio) {
|
|
365
361
|
throwLimitError('compression-ratio', ratio, limits.maxCompressionRatio);
|
|
@@ -451,9 +447,7 @@ const extractSelectedEntriesStreaming = (data, entries, selectedNames, limits) =
|
|
|
451
447
|
abortOutput(new ArchiveLimitError('extracted-size', nextTotalSize, limits.maxExtractedBytes));
|
|
452
448
|
}
|
|
453
449
|
if (nextFileSize >= limits.compressionRatioMinBytes && nextFileSize > 0) {
|
|
454
|
-
const actualRatio = central.size === 0
|
|
455
|
-
? Number.POSITIVE_INFINITY
|
|
456
|
-
: nextFileSize / central.size;
|
|
450
|
+
const actualRatio = central.size === 0 ? Number.POSITIVE_INFINITY : nextFileSize / central.size;
|
|
457
451
|
if (actualRatio > limits.maxCompressionRatio) {
|
|
458
452
|
abortOutput(new ArchiveLimitError('compression-ratio', actualRatio, limits.maxCompressionRatio));
|
|
459
453
|
}
|
|
@@ -27,9 +27,9 @@ const assertHandleIdentity = (filePath, expected, actual) => {
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
const assertStableContentState = (filePath, expected, actual) => {
|
|
30
|
-
if (expected.size !== actual.size
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
if (expected.size !== actual.size ||
|
|
31
|
+
expected.mtimeMs !== actual.mtimeMs ||
|
|
32
|
+
expected.ctimeMs !== actual.ctimeMs) {
|
|
33
33
|
throw new InputFileError('content-changed', filePath, `File content or metadata changed while opening or reading: ${filePath}`);
|
|
34
34
|
}
|
|
35
35
|
};
|
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,7 @@ import { pathToFileURL } from 'node:url';
|
|
|
5
5
|
import { loadFrameworkLogSources } from './frameworkInput.js';
|
|
6
6
|
import { extractFrameworkSessions, } from './frameworkVersion.js';
|
|
7
7
|
import { loadNodeLogDirectory, extractZipContentFromNodeFile, readNodeTextFileContent, } from './nodeInput.js';
|
|
8
|
-
import { analyzeLogContent, buildRuntimeInspection
|
|
8
|
+
import { analyzeLogContent, buildRuntimeInspection } from './index.js';
|
|
9
9
|
const printUsage = () => {
|
|
10
10
|
console.error('Usage: mla-log-tools <path> [--pretty] [--no-events] [--preflight|--runtime-inspection]');
|
|
11
11
|
console.error(' <path>: log file path, zip path, or log directory path');
|
|
@@ -44,9 +44,7 @@ const parseArgs = (argv) => {
|
|
|
44
44
|
return { targetPath, pretty, noEvents, preflight, runtimeInspection };
|
|
45
45
|
};
|
|
46
46
|
const renderOutput = (output, pretty, noEvents) => {
|
|
47
|
-
const payload = noEvents
|
|
48
|
-
? { ...output, events: [] }
|
|
49
|
-
: output;
|
|
47
|
+
const payload = noEvents ? { ...output, events: [] } : output;
|
|
50
48
|
return JSON.stringify(payload, null, pretty ? 2 : 0);
|
|
51
49
|
};
|
|
52
50
|
export const MLA_PREFLIGHT_SCHEMA_VERSION = 'mla-preflight/v1';
|
|
@@ -97,7 +95,7 @@ export const buildPreflightOutput = (output, framework = EMPTY_FRAMEWORK_EXTRACT
|
|
|
97
95
|
};
|
|
98
96
|
};
|
|
99
97
|
export const main = async () => {
|
|
100
|
-
const { targetPath, pretty, noEvents, preflight, runtimeInspection
|
|
98
|
+
const { targetPath, pretty, noEvents, preflight, runtimeInspection } = parseArgs(process.argv.slice(2));
|
|
101
99
|
if (preflight && runtimeInspection) {
|
|
102
100
|
console.error('--preflight and --runtime-inspection are mutually exclusive.');
|
|
103
101
|
process.exit(1);
|
|
@@ -140,12 +138,14 @@ export const main = async () => {
|
|
|
140
138
|
else {
|
|
141
139
|
const content = await readNodeTextFileContent(resolvedPath);
|
|
142
140
|
const lineCount = (content.match(/\n/g) ?? []).length + 1;
|
|
143
|
-
sourceSegments = [
|
|
141
|
+
sourceSegments = [
|
|
142
|
+
{
|
|
144
143
|
source: `file:${resolvedPath.replace(/\\/g, '/')}`,
|
|
145
144
|
path: path.basename(resolvedPath),
|
|
146
145
|
startLine: 1,
|
|
147
146
|
lineCount,
|
|
148
|
-
}
|
|
147
|
+
},
|
|
148
|
+
];
|
|
149
149
|
result = await analyzeLogContent({ content });
|
|
150
150
|
}
|
|
151
151
|
if (!result) {
|
package/dist/frameworkInput.js
CHANGED
|
@@ -18,7 +18,7 @@ const decodeBytes = (bytes) => {
|
|
|
18
18
|
};
|
|
19
19
|
const findEntryPath = (paths, target) => {
|
|
20
20
|
const normalizedTarget = toPosixPath(target).toLowerCase();
|
|
21
|
-
return paths.find((candidate) => toPosixPath(candidate).toLowerCase() === normalizedTarget) ?? null;
|
|
21
|
+
return (paths.find((candidate) => toPosixPath(candidate).toLowerCase() === normalizedTarget) ?? null);
|
|
22
22
|
};
|
|
23
23
|
const findZipBasePath = (paths) => {
|
|
24
24
|
for (const candidate of paths) {
|
|
@@ -53,12 +53,14 @@ const loadZipSources = async (zipPath, limits) => {
|
|
|
53
53
|
if (!bytes)
|
|
54
54
|
return [];
|
|
55
55
|
const normalized = toPosixPath(entryPath);
|
|
56
|
-
return [
|
|
56
|
+
return [
|
|
57
|
+
{
|
|
57
58
|
path: normalized,
|
|
58
59
|
name: path.posix.basename(normalized),
|
|
59
60
|
content: decodeBytes(bytes),
|
|
60
61
|
reference: `zip:${toPosixPath(zipPath)}#${normalized}`,
|
|
61
|
-
}
|
|
62
|
+
},
|
|
63
|
+
];
|
|
62
64
|
});
|
|
63
65
|
};
|
|
64
66
|
const loadDirectorySources = async (directoryPath, limits) => {
|
|
@@ -94,10 +96,12 @@ export const loadFrameworkLogSources = async (targetPath, options = {}) => {
|
|
|
94
96
|
}
|
|
95
97
|
if (targetPath.toLowerCase().endsWith('.zip'))
|
|
96
98
|
return loadZipSources(targetPath, limits);
|
|
97
|
-
return [
|
|
99
|
+
return [
|
|
100
|
+
{
|
|
98
101
|
path: toPosixPath(targetPath),
|
|
99
102
|
name: path.basename(targetPath),
|
|
100
103
|
content: await readNodeTextFileContent(targetPath, { archiveLimits: limits }),
|
|
101
104
|
reference: `file:${toPosixPath(targetPath)}`,
|
|
102
|
-
}
|
|
105
|
+
},
|
|
106
|
+
];
|
|
103
107
|
};
|
package/dist/frameworkVersion.js
CHANGED
|
@@ -39,11 +39,7 @@ const buildSession = (source, lines, startIndex, endIndex, startKind, sessionInd
|
|
|
39
39
|
});
|
|
40
40
|
}
|
|
41
41
|
const versions = [...new Set(versionEvidence.map((item) => item.version))];
|
|
42
|
-
const status = versions.length === 0
|
|
43
|
-
? 'missing_version'
|
|
44
|
-
: versions.length === 1
|
|
45
|
-
? 'resolved'
|
|
46
|
-
: 'conflict';
|
|
42
|
+
const status = versions.length === 0 ? 'missing_version' : versions.length === 1 ? 'resolved' : 'conflict';
|
|
47
43
|
const start = position(source, lines, startIndex);
|
|
48
44
|
start.timestamp = findTimestamp(lines, startIndex, endIndex, 1);
|
|
49
45
|
const end = position(source, lines, endIndex);
|
|
@@ -71,12 +67,12 @@ export const extractFrameworkSessions = (sources) => {
|
|
|
71
67
|
processStarts.push(index);
|
|
72
68
|
}
|
|
73
69
|
const firstProcessStart = processStarts[0];
|
|
74
|
-
const hasPartialPrefix = firstProcessStart != null
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
70
|
+
const hasPartialPrefix = firstProcessStart != null &&
|
|
71
|
+
firstProcessStart > 0 &&
|
|
72
|
+
lines
|
|
73
|
+
.slice(0, firstProcessStart)
|
|
74
|
+
.some((line) => VERSION_PATTERN.test(line) || line.includes('!!!OnEventNotify!!!'));
|
|
75
|
+
const boundaries = processStarts.length === 0 || hasPartialPrefix ? [0, ...processStarts] : processStarts;
|
|
80
76
|
for (let index = 0; index < boundaries.length; index += 1) {
|
|
81
77
|
const startIndex = boundaries[index];
|
|
82
78
|
if (startIndex == null)
|
package/dist/index.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export declare const analyzeLogContent: (input: AnalyzeLogContentInput) => Promi
|
|
|
28
28
|
export declare const analyzeZipBuffer: (input: AnalyzeZipBufferInput) => Promise<KernelOutput | null>;
|
|
29
29
|
export declare const analyzeZipFile: (input: AnalyzeZipFileInput) => Promise<KernelOutput | null>;
|
|
30
30
|
export declare const analyzeDirectory: (input: AnalyzeDirectoryInput) => Promise<KernelOutput | null>;
|
|
31
|
-
export { DEFAULT_CORE_PARSE_OPTIONS
|
|
31
|
+
export { DEFAULT_CORE_PARSE_OPTIONS } from '@windsland52/maa-log-runtime';
|
|
32
32
|
export type { AnalyzeLogContentInput, ParseFileOptions } from '@windsland52/maa-log-runtime';
|
|
33
33
|
export * from './nodeInput.js';
|
|
34
34
|
export * from './frameworkInput.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { analyzeLogContentWith, DEFAULT_CORE_PARSE_OPTIONS
|
|
1
|
+
import { analyzeLogContentWith, DEFAULT_CORE_PARSE_OPTIONS } from '@windsland52/maa-log-runtime';
|
|
2
2
|
import { mlaRuntimeAdapter } from '@windsland52/maa-log-adapter';
|
|
3
3
|
import { extractZipContentFromNodeBuffer, extractZipContentFromNodeFile, loadNodeLogDirectory, } from './nodeInput.js';
|
|
4
4
|
export const analyzeLogContent = async (input) => {
|
|
@@ -55,7 +55,7 @@ export const analyzeDirectory = async (input) => {
|
|
|
55
55
|
parserVersion: input.parserVersion,
|
|
56
56
|
});
|
|
57
57
|
};
|
|
58
|
-
export { DEFAULT_CORE_PARSE_OPTIONS
|
|
58
|
+
export { DEFAULT_CORE_PARSE_OPTIONS } from '@windsland52/maa-log-runtime';
|
|
59
59
|
export * from './nodeInput.js';
|
|
60
60
|
export * from './frameworkInput.js';
|
|
61
61
|
export * from './frameworkVersion.js';
|
|
@@ -12,7 +12,7 @@ export const resolveRecognitionNextListName = (attempt, nextListNames) => {
|
|
|
12
12
|
return attempt.name || '';
|
|
13
13
|
};
|
|
14
14
|
export const buildRecognitionTargetByNextName = (attempts, nextList) => {
|
|
15
|
-
const nextListNames = new Set(nextList.map(item => item.name));
|
|
15
|
+
const nextListNames = new Set(nextList.map((item) => item.name));
|
|
16
16
|
const result = new Map();
|
|
17
17
|
attempts.forEach((attempt) => {
|
|
18
18
|
const matchName = resolveRecognitionNextListName(attempt, nextListNames);
|
|
@@ -55,7 +55,7 @@ export const resolveNodeMatchedNextListItem = (node) => {
|
|
|
55
55
|
if (nextNames.size === 0)
|
|
56
56
|
return undefined;
|
|
57
57
|
const nextItemByName = new Map();
|
|
58
|
-
for (const nextItem of
|
|
58
|
+
for (const nextItem of node.next_list || []) {
|
|
59
59
|
if (!nextItem?.name || nextItemByName.has(nextItem.name))
|
|
60
60
|
continue;
|
|
61
61
|
nextItemByName.set(nextItem.name, nextItem);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolveNodeExecutionName, resolveNodeMatchedRecognitionName
|
|
1
|
+
import { resolveNodeExecutionName, resolveNodeMatchedRecognitionName } from './nodeExecutionName.js';
|
|
2
2
|
import { sortNodesByGlobalExecutionOrder } from './taskExecutionOrder.js';
|
|
3
3
|
const isNodeActionFailed = (node) => {
|
|
4
4
|
if (node.action_details && node.action_details.success === false)
|
package/dist/nodeInput.js
CHANGED
|
@@ -8,10 +8,7 @@ const MAIN_LOG_NAMES = ['maa.log', 'maafw.log'];
|
|
|
8
8
|
const BAK_LOG_NAMES = ['maa.bak.log', 'maafw.bak.log'];
|
|
9
9
|
const SEARCH_TEXT_EXTENSIONS = ['.log', '.txt', '.jsonl'];
|
|
10
10
|
const MAIN_LOG_NAME_SET = new Set(MAIN_LOG_NAMES.map((name) => name.toLowerCase()));
|
|
11
|
-
const HISTORY_LOG_NAME_PATTERNS = [
|
|
12
|
-
/^maa\.bak(?:\..+)?\.log$/i,
|
|
13
|
-
/^maafw\.bak(?:\..+)?\.log$/i,
|
|
14
|
-
];
|
|
11
|
+
const HISTORY_LOG_NAME_PATTERNS = [/^maa\.bak(?:\..+)?\.log$/i, /^maafw\.bak(?:\..+)?\.log$/i];
|
|
15
12
|
const toPosixPath = (value) => value.replace(/\\/g, '/');
|
|
16
13
|
const normalizeLowerPath = (value) => toPosixPath(value).toLowerCase();
|
|
17
14
|
const isSearchTextFile = (normalizedPath) => {
|
|
@@ -111,9 +108,9 @@ const toFileReference = (absolutePath) => {
|
|
|
111
108
|
};
|
|
112
109
|
const isRelativeImagePath = (relativePath, directory, extension) => {
|
|
113
110
|
const normalized = relativePath.toLowerCase();
|
|
114
|
-
return normalized === `${directory}${extension}`
|
|
115
|
-
|
|
116
|
-
|
|
111
|
+
return (normalized === `${directory}${extension}` ||
|
|
112
|
+
normalized.startsWith(`${directory}/`) ||
|
|
113
|
+
normalized.includes(`/${directory}/`));
|
|
117
114
|
};
|
|
118
115
|
const normalizeTimestampBoundary = (value) => {
|
|
119
116
|
if (!value)
|
|
@@ -159,7 +156,7 @@ const countNewlines = (content) => {
|
|
|
159
156
|
return count;
|
|
160
157
|
};
|
|
161
158
|
const joinMergedWithSources = (chunks) => {
|
|
162
|
-
let content =
|
|
159
|
+
let content = '';
|
|
163
160
|
let runningNewlines = 0;
|
|
164
161
|
const chunkStarts = [];
|
|
165
162
|
for (const chunk of chunks) {
|
|
@@ -180,7 +177,7 @@ const joinMergedWithSources = (chunks) => {
|
|
|
180
177
|
runningNewlines += countNewlines(chunk.content);
|
|
181
178
|
}
|
|
182
179
|
if (chunkStarts.length === 0) {
|
|
183
|
-
return { content:
|
|
180
|
+
return { content: '', segments: [] };
|
|
184
181
|
}
|
|
185
182
|
const totalLines = runningNewlines + 1;
|
|
186
183
|
const segments = chunkStarts.map((info, i) => ({
|
|
@@ -295,7 +292,9 @@ const readNodeTextFileWithinBudget = async (filePath, context) => {
|
|
|
295
292
|
const limitCode = context.limits.maxFileBytes <= remainingBytes ? 'file-size' : 'extracted-size';
|
|
296
293
|
const bytes = await readBoundedRegularFile(filePath, maxBytes, (actualBytes) => new ArchiveLimitError(limitCode, limitCode === 'extracted-size'
|
|
297
294
|
? context.extraction.extractedBytes + actualBytes
|
|
298
|
-
: actualBytes, limitCode === 'extracted-size'
|
|
295
|
+
: actualBytes, limitCode === 'extracted-size'
|
|
296
|
+
? context.limits.maxExtractedBytes
|
|
297
|
+
: context.limits.maxFileBytes), { expectedIdentity: context.discoveredIdentities.get(pathKey(filePath)) });
|
|
299
298
|
context.extraction = addSelectedEntry(context.extraction, createStoredFileMetadata(toPosixPath(filePath), bytes.byteLength), context.limits, false);
|
|
300
299
|
return decodeNodeBytes(bytes);
|
|
301
300
|
};
|
|
@@ -369,15 +368,14 @@ const buildDefaultZipContent = (entries, paths, basePath, sourceRef) => {
|
|
|
369
368
|
};
|
|
370
369
|
export const readNodeTextFileContent = async (filePath, options = {}) => {
|
|
371
370
|
const limits = resolveArchiveLimits(options.archiveLimits);
|
|
372
|
-
const context = options.budgetContext ??
|
|
371
|
+
const context = options.budgetContext ??
|
|
372
|
+
(await createNodeInputBudgetContext(path.dirname(path.resolve(filePath)), limits));
|
|
373
373
|
return readNodeTextFileWithinBudget(filePath, context);
|
|
374
374
|
};
|
|
375
375
|
export const readNodeTextFilesContent = async (filePaths, options = {}) => {
|
|
376
376
|
const limits = resolveArchiveLimits(options.archiveLimits);
|
|
377
|
-
const commonRoot = filePaths.length > 0
|
|
378
|
-
|
|
379
|
-
: process.cwd();
|
|
380
|
-
const context = options.budgetContext ?? await createNodeInputBudgetContext(commonRoot, limits);
|
|
377
|
+
const commonRoot = filePaths.length > 0 ? path.dirname(path.resolve(filePaths[0])) : process.cwd();
|
|
378
|
+
const context = options.budgetContext ?? (await createNodeInputBudgetContext(commonRoot, limits));
|
|
381
379
|
const contents = [];
|
|
382
380
|
for (const filePath of filePaths) {
|
|
383
381
|
contents.push(await readNodeTextFileWithinBudget(filePath, context));
|
|
@@ -437,7 +435,14 @@ export const extractZipContentFromNodeBuffer = (zipData, sourceRef = 'memory.zip
|
|
|
437
435
|
});
|
|
438
436
|
}
|
|
439
437
|
textFiles.sort((a, b) => a.path.localeCompare(b.path));
|
|
440
|
-
return {
|
|
438
|
+
return {
|
|
439
|
+
content: merged.content,
|
|
440
|
+
sourceSegments: merged.segments,
|
|
441
|
+
errorImages,
|
|
442
|
+
visionImages,
|
|
443
|
+
waitFreezesImages,
|
|
444
|
+
textFiles,
|
|
445
|
+
};
|
|
441
446
|
};
|
|
442
447
|
export const extractZipContentFromNodeFile = async (zipFilePath, options = {}) => {
|
|
443
448
|
const limits = resolveArchiveLimits(options.archiveLimits);
|
|
@@ -505,7 +510,7 @@ const tryInspectDirectory = async (context, directoryPath) => {
|
|
|
505
510
|
}
|
|
506
511
|
};
|
|
507
512
|
export const hasNodeMainLogInDirectory = async (context, directoryPath) => {
|
|
508
|
-
if (!await tryInspectDirectory(context, directoryPath))
|
|
513
|
+
if (!(await tryInspectDirectory(context, directoryPath)))
|
|
509
514
|
return false;
|
|
510
515
|
for (const name of MAIN_LOG_NAMES) {
|
|
511
516
|
const candidatePath = path.join(directoryPath, name);
|
|
@@ -49,21 +49,55 @@ const evidence = (index, timestamp) => {
|
|
|
49
49
|
const after = index.ordered[low];
|
|
50
50
|
const nearest = before == null
|
|
51
51
|
? after
|
|
52
|
-
: after == null || target - before.timestamp <= after.timestamp - target
|
|
52
|
+
: after == null || target - before.timestamp <= after.timestamp - target
|
|
53
|
+
? before
|
|
54
|
+
: after;
|
|
53
55
|
return { timestamp: timestamp ?? null, mergedLine: nearest?.line ?? null };
|
|
54
56
|
};
|
|
55
|
-
const recognitionItems = (items) => (
|
|
57
|
+
const recognitionItems = (items) => (items ?? []).flatMap((item) => [
|
|
56
58
|
...(item.type === 'recognition' || item.type === 'recognition_node' ? [item] : []),
|
|
57
59
|
...recognitionItems(item.children),
|
|
58
|
-
])
|
|
60
|
+
]);
|
|
59
61
|
const imagesFor = (node) => {
|
|
60
62
|
const attempts = recognitionItems(node.node_flow);
|
|
61
63
|
return {
|
|
62
|
-
error: [node.error_image, ...attempts.map(item => item.error_image)]
|
|
64
|
+
error: [node.error_image, ...attempts.map((item) => item.error_image)].filter((item) => Boolean(item)),
|
|
65
|
+
vision: attempts
|
|
66
|
+
.map((item) => item.vision_image)
|
|
63
67
|
.filter((item) => Boolean(item)),
|
|
64
|
-
vision: attempts.map(item => item.vision_image).filter((item) => Boolean(item)),
|
|
65
68
|
};
|
|
66
69
|
};
|
|
70
|
+
const ownedRecognitionItems = (items) => (items ?? []).flatMap((item) => {
|
|
71
|
+
if (item.type === 'task' || item.type === 'pipeline_node')
|
|
72
|
+
return [];
|
|
73
|
+
return [
|
|
74
|
+
...(item.type === 'recognition' || item.type === 'recognition_node' ? [item] : []),
|
|
75
|
+
...ownedRecognitionItems(item.children),
|
|
76
|
+
];
|
|
77
|
+
});
|
|
78
|
+
const imagesForFlowItem = (item) => {
|
|
79
|
+
const attempts = ownedRecognitionItems(item.children);
|
|
80
|
+
return {
|
|
81
|
+
error: [item.error_image, ...attempts.map((attempt) => attempt.error_image)].filter((image) => Boolean(image)),
|
|
82
|
+
vision: [item.vision_image, ...attempts.map((attempt) => attempt.vision_image)].filter((image) => Boolean(image)),
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
const hasFailedOwnedAction = (items) => (items ?? []).some((item) => {
|
|
86
|
+
if (item.type === 'task' || item.type === 'pipeline_node')
|
|
87
|
+
return false;
|
|
88
|
+
if ((item.type === 'action' || item.type === 'action_node') &&
|
|
89
|
+
(item.status === 'failed' || item.action_details?.success === false))
|
|
90
|
+
return true;
|
|
91
|
+
return hasFailedOwnedAction(item.children);
|
|
92
|
+
});
|
|
93
|
+
const hasFailedNestedTask = (items) => (items ?? []).some((item) => item.type === 'task' ? item.status === 'failed' : hasFailedNestedTask(item.children));
|
|
94
|
+
const nestedPipelineFailureKind = (item) => {
|
|
95
|
+
if (item.status !== 'failed')
|
|
96
|
+
return null;
|
|
97
|
+
if (item.action_details?.success === false || hasFailedOwnedAction(item.children))
|
|
98
|
+
return 'action_failed';
|
|
99
|
+
return hasFailedNestedTask(item.children) ? null : 'next_list_timeout';
|
|
100
|
+
};
|
|
67
101
|
const scopeFor = (task, sessionId, executionId) => ({
|
|
68
102
|
sessionId,
|
|
69
103
|
executionId,
|
|
@@ -71,14 +105,14 @@ const scopeFor = (task, sessionId, executionId) => ({
|
|
|
71
105
|
taskName: task.entry,
|
|
72
106
|
});
|
|
73
107
|
const sessionFor = (task, sessions) => {
|
|
74
|
-
const candidates = sessions.filter(session => session.start.timestamp != null
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const complete = candidates.filter(session => session.startKind === 'process_start');
|
|
108
|
+
const candidates = sessions.filter((session) => session.start.timestamp != null &&
|
|
109
|
+
session.end.timestamp != null &&
|
|
110
|
+
task.start_time >= session.start.timestamp &&
|
|
111
|
+
task.start_time <= session.end.timestamp);
|
|
112
|
+
const complete = candidates.filter((session) => session.startKind === 'process_start');
|
|
79
113
|
if (complete.length === 1)
|
|
80
114
|
return complete[0] ?? null;
|
|
81
|
-
const partial = candidates.filter(session => session.startKind === 'partial_file');
|
|
115
|
+
const partial = candidates.filter((session) => session.startKind === 'partial_file');
|
|
82
116
|
return complete.length === 0 && partial.length === 1 ? (partial[0] ?? null) : null;
|
|
83
117
|
};
|
|
84
118
|
const metricDistribution = (values) => {
|
|
@@ -86,7 +120,7 @@ const metricDistribution = (values) => {
|
|
|
86
120
|
return { count: 0, minimum: 0, p50: 0, p95: 0, maximum: 0, average: 0 };
|
|
87
121
|
}
|
|
88
122
|
const sorted = [...values].sort((left, right) => left - right);
|
|
89
|
-
const percentile = (ratio) =>
|
|
123
|
+
const percentile = (ratio) => sorted[Math.min(sorted.length - 1, Math.ceil(sorted.length * ratio) - 1)] ?? 0;
|
|
90
124
|
const total = sorted.reduce((sum, value) => sum + value, 0);
|
|
91
125
|
return {
|
|
92
126
|
count: sorted.length,
|
|
@@ -111,18 +145,18 @@ const increment = (map, key) => {
|
|
|
111
145
|
const prioritize = (reasons) => {
|
|
112
146
|
if (reasons.length === 0)
|
|
113
147
|
return { priority: 'low', priorityReasons: [] };
|
|
114
|
-
if (reasons.includes('timeout')
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
148
|
+
if (reasons.includes('timeout') ||
|
|
149
|
+
reasons.includes('unmatched_terminal') ||
|
|
150
|
+
reasons.includes('still_repeating_at_log_end') ||
|
|
151
|
+
reasons.includes('related_to_direct_failure') ||
|
|
152
|
+
reasons.includes('incomplete_repetition')) {
|
|
119
153
|
return { priority: 'high', priorityReasons: reasons };
|
|
120
154
|
}
|
|
121
|
-
if (reasons.includes('high_mixed_results')
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
155
|
+
if (reasons.includes('high_mixed_results') ||
|
|
156
|
+
reasons.includes('high_unsuccessful_attempts') ||
|
|
157
|
+
reasons.includes('high_occurrence_count') ||
|
|
158
|
+
reasons.includes('high_repeat_count') ||
|
|
159
|
+
reasons.includes('long_duration')) {
|
|
126
160
|
return { priority: 'normal', priorityReasons: reasons };
|
|
127
161
|
}
|
|
128
162
|
return { priority: 'low', priorityReasons: reasons };
|
|
@@ -135,7 +169,9 @@ const repetitions = (nodes) => {
|
|
|
135
169
|
for (let length = 1; length <= Math.min(8, Math.floor((nodes.length - start) / 2)); length += 1) {
|
|
136
170
|
let count = 1;
|
|
137
171
|
while (start + (count + 1) * length <= nodes.length) {
|
|
138
|
-
const same = nodes
|
|
172
|
+
const same = nodes
|
|
173
|
+
.slice(start, start + length)
|
|
174
|
+
.every((node, offset) => node.name === nodes[start + count * length + offset]?.name);
|
|
139
175
|
if (!same)
|
|
140
176
|
break;
|
|
141
177
|
count += 1;
|
|
@@ -215,7 +251,99 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
215
251
|
const recognitionOccurrences = [];
|
|
216
252
|
const evidenceIndex = buildEvidenceIndex(task);
|
|
217
253
|
const timeline = buildNodeExecutionTimeline(task.nodes, { rootTaskId: task.task_id });
|
|
254
|
+
const seenNestedTasks = new Set();
|
|
255
|
+
const seenNestedPipelines = new Set();
|
|
256
|
+
const inspectNestedTask = (taskItem) => {
|
|
257
|
+
if (seenNestedTasks.has(taskItem.id))
|
|
258
|
+
return;
|
|
259
|
+
seenNestedTasks.add(taskItem.id);
|
|
260
|
+
const taskId = taskItem.task_details?.task_id ?? taskItem.task_id;
|
|
261
|
+
if (taskId == null) {
|
|
262
|
+
inspectNestedTasks(taskItem.children);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
const nestedScope = {
|
|
266
|
+
sessionId,
|
|
267
|
+
executionId,
|
|
268
|
+
taskId,
|
|
269
|
+
taskName: taskItem.task_details?.entry ?? taskItem.name,
|
|
270
|
+
};
|
|
271
|
+
const nestedDirectFailureIds = [];
|
|
272
|
+
const inspectOwnedItems = (items) => {
|
|
273
|
+
for (const child of items ?? []) {
|
|
274
|
+
if (child.type === 'task') {
|
|
275
|
+
inspectNestedTask(child);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
if (child.type !== 'pipeline_node') {
|
|
279
|
+
inspectOwnedItems(child.children);
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (seenNestedPipelines.has(child.id))
|
|
283
|
+
continue;
|
|
284
|
+
seenNestedPipelines.add(child.id);
|
|
285
|
+
inspectOwnedItems(child.children);
|
|
286
|
+
const failureKind = nestedPipelineFailureKind(child);
|
|
287
|
+
let nodeFailureId = null;
|
|
288
|
+
if (failureKind && child.node_id != null) {
|
|
289
|
+
nodeFailureId = `failure-${failures.length + 1}`;
|
|
290
|
+
const images = imagesForFlowItem(child);
|
|
291
|
+
failures.push({
|
|
292
|
+
...nestedScope,
|
|
293
|
+
failureId: nodeFailureId,
|
|
294
|
+
kind: failureKind,
|
|
295
|
+
nodeId: child.node_id,
|
|
296
|
+
nodeName: child.name,
|
|
297
|
+
startedAt: child.ts,
|
|
298
|
+
endedAt: child.end_ts ?? null,
|
|
299
|
+
errorImages: [...new Set(images.error)],
|
|
300
|
+
visionImages: [...new Set(images.vision)],
|
|
301
|
+
evidence: evidenceAt(evidenceIndex, child.end_ts ?? child.ts),
|
|
302
|
+
});
|
|
303
|
+
nestedDirectFailureIds.push(nodeFailureId);
|
|
304
|
+
}
|
|
305
|
+
if (child.status !== 'success') {
|
|
306
|
+
const outcomeId = `outcome-${outcomes.length + 1}`;
|
|
307
|
+
outcomes.push({
|
|
308
|
+
...nestedScope,
|
|
309
|
+
outcomeId,
|
|
310
|
+
kind: 'pipeline_node',
|
|
311
|
+
status: child.status,
|
|
312
|
+
nodeId: child.node_id ?? null,
|
|
313
|
+
nodeName: child.name,
|
|
314
|
+
directFailureIds: nodeFailureId ? [nodeFailureId] : [],
|
|
315
|
+
evidence: evidenceAt(evidenceIndex, child.end_ts ?? child.ts),
|
|
316
|
+
});
|
|
317
|
+
outcomeIds.push(outcomeId);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
inspectOwnedItems(taskItem.children);
|
|
322
|
+
if (taskItem.status !== 'success') {
|
|
323
|
+
const outcomeId = `outcome-${outcomes.length + 1}`;
|
|
324
|
+
outcomes.push({
|
|
325
|
+
...nestedScope,
|
|
326
|
+
outcomeId,
|
|
327
|
+
kind: 'task',
|
|
328
|
+
status: taskItem.status,
|
|
329
|
+
nodeId: null,
|
|
330
|
+
nodeName: null,
|
|
331
|
+
directFailureIds: nestedDirectFailureIds,
|
|
332
|
+
evidence: evidenceAt(evidenceIndex, taskItem.end_ts ?? taskItem.ts),
|
|
333
|
+
});
|
|
334
|
+
outcomeIds.push(outcomeId);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
const inspectNestedTasks = (items) => {
|
|
338
|
+
for (const item of items ?? []) {
|
|
339
|
+
if (item.type === 'task')
|
|
340
|
+
inspectNestedTask(item);
|
|
341
|
+
else
|
|
342
|
+
inspectNestedTasks(item.children);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
218
345
|
for (const item of timeline) {
|
|
346
|
+
inspectNestedTasks(item.nodeInfo.node_flow);
|
|
219
347
|
const failureKind = item.navStatus === 'action-failed'
|
|
220
348
|
? 'action_failed'
|
|
221
349
|
: item.navStatus === 'timeout' && item.nodeInfo.next_list.length > 0
|
|
@@ -254,17 +382,19 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
254
382
|
outcomeIds.push(outcomeId);
|
|
255
383
|
}
|
|
256
384
|
const attempts = recognitionItems(item.nodeInfo.node_flow);
|
|
257
|
-
const missed = attempts.filter(attempt => attempt.status === 'failed');
|
|
385
|
+
const missed = attempts.filter((attempt) => attempt.status === 'failed');
|
|
258
386
|
if (item.nodeInfo.next_list.length > 0) {
|
|
259
387
|
const terminalOutcome = item.navStatus === 'timeout'
|
|
260
388
|
? 'timeout'
|
|
261
389
|
: item.nodeInfo.status === 'running'
|
|
262
390
|
? 'running'
|
|
263
|
-
: item.matchedRecognitionName
|
|
391
|
+
: item.matchedRecognitionName
|
|
392
|
+
? 'matched'
|
|
393
|
+
: 'unmatched';
|
|
264
394
|
recognitionOccurrences.push({
|
|
265
395
|
nodeId: item.nodeInfo.node_id,
|
|
266
396
|
pipelineNodeName: item.nodeInfo.name,
|
|
267
|
-
nextList: item.nodeInfo.next_list.map(next => ({
|
|
397
|
+
nextList: item.nodeInfo.next_list.map((next) => ({
|
|
268
398
|
name: next.name,
|
|
269
399
|
anchor: next.anchor,
|
|
270
400
|
jumpBack: next.jump_back,
|
|
@@ -303,14 +433,17 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
303
433
|
const signalId = `signal-${signals.length + 1}`;
|
|
304
434
|
const terminalMatches = new Map();
|
|
305
435
|
const terminalOutcomes = { matched: 0, timeout: 0, running: 0, unmatched: 0 };
|
|
306
|
-
const candidates = new Map(firstOccurrence.nextList.map(next => [
|
|
436
|
+
const candidates = new Map(firstOccurrence.nextList.map((next) => [
|
|
437
|
+
next.name,
|
|
438
|
+
{
|
|
307
439
|
name: next.name,
|
|
308
440
|
evaluationCount: 0,
|
|
309
441
|
matchedAttemptCount: 0,
|
|
310
442
|
unsuccessfulAttemptCount: 0,
|
|
311
443
|
runningAttemptCount: 0,
|
|
312
444
|
terminalMatchCount: 0,
|
|
313
|
-
}
|
|
445
|
+
},
|
|
446
|
+
]));
|
|
314
447
|
let unmappedAttemptCount = 0;
|
|
315
448
|
let occurrencesWithMixedResults = 0;
|
|
316
449
|
for (const occurrence of group) {
|
|
@@ -321,7 +454,7 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
321
454
|
if (candidate)
|
|
322
455
|
candidate.terminalMatchCount += 1;
|
|
323
456
|
}
|
|
324
|
-
const statuses = new Set(occurrence.attempts.map(attempt => attempt.status));
|
|
457
|
+
const statuses = new Set(occurrence.attempts.map((attempt) => attempt.status));
|
|
325
458
|
if (statuses.has('failed') && statuses.has('success'))
|
|
326
459
|
occurrencesWithMixedResults += 1;
|
|
327
460
|
const nextNames = new Set(candidates.keys());
|
|
@@ -341,11 +474,11 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
341
474
|
candidate.runningAttemptCount += 1;
|
|
342
475
|
}
|
|
343
476
|
}
|
|
344
|
-
const worstOccurrence = [...group].sort((left, right) =>
|
|
345
|
-
|
|
346
|
-
const attemptsDist = metricDistribution(group.map(item => item.sample.attemptCount));
|
|
347
|
-
const unsuccessfulDist = metricDistribution(group.map(item => item.sample.unsuccessfulAttempts));
|
|
348
|
-
const durationDist = metricDistribution(group.flatMap(item => item.durationMs == null ? [] : [item.durationMs]));
|
|
477
|
+
const worstOccurrence = [...group].sort((left, right) => right.sample.unsuccessfulAttempts - left.sample.unsuccessfulAttempts ||
|
|
478
|
+
right.sample.attemptCount - left.sample.attemptCount)[0] ?? firstOccurrence;
|
|
479
|
+
const attemptsDist = metricDistribution(group.map((item) => item.sample.attemptCount));
|
|
480
|
+
const unsuccessfulDist = metricDistribution(group.map((item) => item.sample.unsuccessfulAttempts));
|
|
481
|
+
const durationDist = metricDistribution(group.flatMap((item) => (item.durationMs == null ? [] : [item.durationMs])));
|
|
349
482
|
const reasons = [];
|
|
350
483
|
if (terminalOutcomes.timeout > 0)
|
|
351
484
|
reasons.push('timeout');
|
|
@@ -357,7 +490,8 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
357
490
|
reasons.push('high_unsuccessful_attempts');
|
|
358
491
|
if (group.length >= 20)
|
|
359
492
|
reasons.push('high_occurrence_count');
|
|
360
|
-
if (group.some(item => item.sample.nodeId &&
|
|
493
|
+
if (group.some((item) => item.sample.nodeId &&
|
|
494
|
+
failures.some((failure) => failure.nodeId === item.sample.nodeId && failure.executionId === scope.executionId))) {
|
|
361
495
|
reasons.push('related_to_direct_failure');
|
|
362
496
|
}
|
|
363
497
|
const ranking = prioritize(reasons);
|
|
@@ -370,7 +504,8 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
370
504
|
occurrenceCount: group.length,
|
|
371
505
|
occurrencesWithMixedResults,
|
|
372
506
|
terminalOutcomes,
|
|
373
|
-
terminalMatches: [...terminalMatches]
|
|
507
|
+
terminalMatches: [...terminalMatches]
|
|
508
|
+
.map(([name, count]) => ({ name, count }))
|
|
374
509
|
.sort((left, right) => right.count - left.count),
|
|
375
510
|
candidateStatistics: [...candidates.values()],
|
|
376
511
|
unmappedAttemptCount,
|
|
@@ -387,7 +522,9 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
387
522
|
});
|
|
388
523
|
signalIds.push(signalId);
|
|
389
524
|
}
|
|
390
|
-
const completed = timeline
|
|
525
|
+
const completed = timeline
|
|
526
|
+
.map((item) => item.nodeInfo)
|
|
527
|
+
.filter((node) => node.status !== 'running');
|
|
391
528
|
const repetitionGroups = new Map();
|
|
392
529
|
for (const repeated of repetitions(completed)) {
|
|
393
530
|
const first = completed[repeated.start];
|
|
@@ -395,21 +532,23 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
395
532
|
const last = completed[lastIndex];
|
|
396
533
|
if (!first || !last)
|
|
397
534
|
continue;
|
|
398
|
-
const rawPattern = completed
|
|
535
|
+
const rawPattern = completed
|
|
536
|
+
.slice(repeated.start, repeated.start + repeated.length)
|
|
537
|
+
.map((node) => node.name);
|
|
399
538
|
const pattern = repeated.length === 1 ? rawPattern : canonicalCycle(rawPattern);
|
|
400
539
|
const kind = repeated.length === 1 ? 'repeated_node' : 'repeated_node_cycle';
|
|
401
540
|
const key = JSON.stringify([kind, pattern]);
|
|
402
541
|
const group = repetitionGroups.get(key) ?? [];
|
|
403
|
-
const timelineLastIndex = timeline.findIndex(item => item.nodeInfo === last);
|
|
542
|
+
const timelineLastIndex = timeline.findIndex((item) => item.nodeInfo === last);
|
|
404
543
|
const trailing = timelineLastIndex < 0 ? [] : timeline.slice(timelineLastIndex + 1);
|
|
405
544
|
const reachesCompletedEnd = lastIndex === completed.length - 1;
|
|
406
|
-
const continuesAtLogEnd = reachesCompletedEnd
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const taskEndedAtPattern = reachesCompletedEnd
|
|
411
|
-
|
|
412
|
-
|
|
545
|
+
const continuesAtLogEnd = reachesCompletedEnd &&
|
|
546
|
+
task.status === 'running' &&
|
|
547
|
+
trailing.every((item, offset) => item.nodeInfo.status === 'running' &&
|
|
548
|
+
item.nodeInfo.name === rawPattern[offset % rawPattern.length]);
|
|
549
|
+
const taskEndedAtPattern = reachesCompletedEnd &&
|
|
550
|
+
timelineLastIndex === timeline.length - 1 &&
|
|
551
|
+
task.status !== 'running';
|
|
413
552
|
group.push({
|
|
414
553
|
pattern,
|
|
415
554
|
repeatCount: repeated.count,
|
|
@@ -418,7 +557,9 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
418
557
|
lastSeenAt: last.end_ts ?? last.ts,
|
|
419
558
|
termination: continuesAtLogEnd
|
|
420
559
|
? 'still_repeating_at_log_end'
|
|
421
|
-
: taskEndedAtPattern
|
|
560
|
+
: taskEndedAtPattern
|
|
561
|
+
? 'task_ended'
|
|
562
|
+
: 'left_pattern',
|
|
422
563
|
evidence: evidenceAt(evidenceIndex, first.ts),
|
|
423
564
|
});
|
|
424
565
|
repetitionGroups.set(key, group);
|
|
@@ -431,12 +572,12 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
431
572
|
const longest = [...group].sort((left, right) => right.durationMs - left.durationMs)[0] ?? first;
|
|
432
573
|
const signalId = `signal-${signals.length + 1}`;
|
|
433
574
|
const terminations = {
|
|
434
|
-
leftPattern: group.filter(item => item.termination === 'left_pattern').length,
|
|
435
|
-
taskEnded: group.filter(item => item.termination === 'task_ended').length,
|
|
436
|
-
stillRepeatingAtLogEnd: group.filter(item => item.termination === 'still_repeating_at_log_end').length,
|
|
575
|
+
leftPattern: group.filter((item) => item.termination === 'left_pattern').length,
|
|
576
|
+
taskEnded: group.filter((item) => item.termination === 'task_ended').length,
|
|
577
|
+
stillRepeatingAtLogEnd: group.filter((item) => item.termination === 'still_repeating_at_log_end').length,
|
|
437
578
|
};
|
|
438
579
|
const totalRepeatCount = group.reduce((sum, item) => sum + item.repeatCount, 0);
|
|
439
|
-
const maximumRepeatCount = Math.max(...group.map(item => item.repeatCount));
|
|
580
|
+
const maximumRepeatCount = Math.max(...group.map((item) => item.repeatCount));
|
|
440
581
|
const repetitionReasons = [];
|
|
441
582
|
if (terminations.stillRepeatingAtLogEnd > 0)
|
|
442
583
|
repetitionReasons.push('still_repeating_at_log_end');
|
|
@@ -451,7 +592,7 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
451
592
|
segmentCount: group.length,
|
|
452
593
|
totalRepeatCount,
|
|
453
594
|
maximumRepeatCount,
|
|
454
|
-
durationMs: metricDistribution(group.map(item => item.durationMs)),
|
|
595
|
+
durationMs: metricDistribution(group.map((item) => item.durationMs)),
|
|
455
596
|
terminations,
|
|
456
597
|
representatives: {
|
|
457
598
|
first: first,
|
|
@@ -483,25 +624,25 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
483
624
|
});
|
|
484
625
|
outcomeIds.push(outcomeId);
|
|
485
626
|
}
|
|
486
|
-
const attemptsByNode = timeline.map(item => recognitionItems(item.nodeInfo.node_flow));
|
|
627
|
+
const attemptsByNode = timeline.map((item) => recognitionItems(item.nodeInfo.node_flow));
|
|
487
628
|
const allAttempts = attemptsByNode.flat();
|
|
488
|
-
const imageSets = timeline.map(item => imagesFor(item.nodeInfo));
|
|
489
|
-
const errorImages = imageSets.flatMap(set => set.error);
|
|
490
|
-
const visionImages = imageSets.flatMap(set => set.vision);
|
|
491
|
-
const ownFailures = failures.filter(failure => directFailureIds.includes(failure.failureId));
|
|
492
|
-
const ownSignals = signals.filter(signal => signalIds.includes(signal.signalId));
|
|
493
|
-
const recognitionSignals = ownSignals
|
|
494
|
-
.filter((signal) => (signal.kind === 'recognition_activity'));
|
|
629
|
+
const imageSets = timeline.map((item) => imagesFor(item.nodeInfo));
|
|
630
|
+
const errorImages = imageSets.flatMap((set) => set.error);
|
|
631
|
+
const visionImages = imageSets.flatMap((set) => set.vision);
|
|
632
|
+
const ownFailures = failures.filter((failure) => directFailureIds.includes(failure.failureId));
|
|
633
|
+
const ownSignals = signals.filter((signal) => signalIds.includes(signal.signalId));
|
|
634
|
+
const recognitionSignals = ownSignals.filter((signal) => signal.kind === 'recognition_activity');
|
|
495
635
|
const recognitionActivity = [...recognitionSignals]
|
|
496
|
-
.sort((left, right) =>
|
|
497
|
-
|
|
636
|
+
.sort((left, right) => right.unsuccessfulAttempts.maximum - left.unsuccessfulAttempts.maximum ||
|
|
637
|
+
right.occurrenceCount - left.occurrenceCount)
|
|
498
638
|
.slice(0, 5)
|
|
499
|
-
.map(signal => signal.signalId);
|
|
639
|
+
.map((signal) => signal.signalId);
|
|
500
640
|
const repetitionSignals = ownSignals
|
|
501
641
|
.filter((signal) => signal.kind !== 'recognition_activity')
|
|
502
|
-
.sort((left, right) =>
|
|
642
|
+
.sort((left, right) => right.totalRepeatCount * right.pattern.length -
|
|
643
|
+
left.totalRepeatCount * left.pattern.length)
|
|
503
644
|
.slice(0, 5)
|
|
504
|
-
.map(signal => signal.signalId);
|
|
645
|
+
.map((signal) => signal.signalId);
|
|
505
646
|
return {
|
|
506
647
|
executionId: scope.executionId,
|
|
507
648
|
taskId: task.task_id,
|
|
@@ -517,22 +658,24 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
517
658
|
lastNode: timeline[timeline.length - 1]?.executionName ?? null,
|
|
518
659
|
statistics: {
|
|
519
660
|
nodeExecutions: timeline.length,
|
|
520
|
-
succeededNodes: timeline.filter(item => item.nodeInfo.status === 'success').length,
|
|
521
|
-
failedNodes: timeline.filter(item => item.nodeInfo.status === 'failed').length,
|
|
522
|
-
runningNodes: timeline.filter(item => item.nodeInfo.status === 'running').length,
|
|
661
|
+
succeededNodes: timeline.filter((item) => item.nodeInfo.status === 'success').length,
|
|
662
|
+
failedNodes: timeline.filter((item) => item.nodeInfo.status === 'failed').length,
|
|
663
|
+
runningNodes: timeline.filter((item) => item.nodeInfo.status === 'running').length,
|
|
523
664
|
recognitionAttempts: allAttempts.length,
|
|
524
|
-
unsuccessfulRecognitionAttempts: allAttempts.filter(attempt => attempt.status === 'failed').length,
|
|
525
|
-
nodeExecutionsWithRecognition: attemptsByNode.filter(attempts => attempts.length > 0)
|
|
526
|
-
|
|
527
|
-
|
|
665
|
+
unsuccessfulRecognitionAttempts: allAttempts.filter((attempt) => attempt.status === 'failed').length,
|
|
666
|
+
nodeExecutionsWithRecognition: attemptsByNode.filter((attempts) => attempts.length > 0)
|
|
667
|
+
.length,
|
|
668
|
+
nodeExecutionsWithMixedRecognitionResults: attemptsByNode.filter((attempts) => {
|
|
669
|
+
const statuses = new Set(attempts.map((attempt) => attempt.status));
|
|
528
670
|
return statuses.has('failed') && statuses.has('success');
|
|
529
671
|
}).length,
|
|
530
672
|
recognitionActivityGroups: recognitionSignals.length,
|
|
531
|
-
maximumRecognitionAttemptsPerNode: Math.max(0, ...attemptsByNode.map(attempts => attempts.length)),
|
|
532
|
-
maximumUnsuccessfulRecognitionAttemptsPerNode: Math.max(0, ...attemptsByNode.map(attempts => attempts.filter(attempt => attempt.status === 'failed').length)),
|
|
533
|
-
actionAttempts: timeline.filter(item => item.nodeInfo.action_details != null).length,
|
|
534
|
-
actionFailures: ownFailures.filter(failure => failure.kind === 'action_failed').length,
|
|
535
|
-
nextListTimeouts: ownFailures.filter(failure => failure.kind === 'next_list_timeout')
|
|
673
|
+
maximumRecognitionAttemptsPerNode: Math.max(0, ...attemptsByNode.map((attempts) => attempts.length)),
|
|
674
|
+
maximumUnsuccessfulRecognitionAttemptsPerNode: Math.max(0, ...attemptsByNode.map((attempts) => attempts.filter((attempt) => attempt.status === 'failed').length)),
|
|
675
|
+
actionAttempts: timeline.filter((item) => item.nodeInfo.action_details != null).length,
|
|
676
|
+
actionFailures: ownFailures.filter((failure) => failure.kind === 'action_failed').length,
|
|
677
|
+
nextListTimeouts: ownFailures.filter((failure) => failure.kind === 'next_list_timeout')
|
|
678
|
+
.length,
|
|
536
679
|
errorImageReferences: errorImages.length,
|
|
537
680
|
uniqueErrorImages: new Set(errorImages).size,
|
|
538
681
|
visionImageReferences: visionImages.length,
|
|
@@ -550,9 +693,9 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
550
693
|
};
|
|
551
694
|
const tasks = output.tasks.map(buildTask);
|
|
552
695
|
const sessions = framework.sessions.map((session) => {
|
|
553
|
-
const scoped = tasks.filter(task => executionSessionIds.get(task.executionId) === session.sessionId);
|
|
554
|
-
const ids = new Set(scoped.flatMap(task => task.directFailureIds));
|
|
555
|
-
const scopedFailures = failures.filter(failure => ids.has(failure.failureId));
|
|
696
|
+
const scoped = tasks.filter((task) => executionSessionIds.get(task.executionId) === session.sessionId);
|
|
697
|
+
const ids = new Set(scoped.flatMap((task) => task.directFailureIds));
|
|
698
|
+
const scopedFailures = failures.filter((failure) => ids.has(failure.failureId));
|
|
556
699
|
return {
|
|
557
700
|
sessionId: session.sessionId,
|
|
558
701
|
startKind: session.startKind,
|
|
@@ -564,17 +707,18 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
564
707
|
tasks: scoped,
|
|
565
708
|
summary: {
|
|
566
709
|
taskExecutions: scoped.length,
|
|
567
|
-
succeededTasks: scoped.filter(task => task.status === 'succeeded').length,
|
|
568
|
-
failedTasks: scoped.filter(task => task.status === 'failed').length,
|
|
569
|
-
runningTasks: scoped.filter(task => task.status === 'running').length,
|
|
710
|
+
succeededTasks: scoped.filter((task) => task.status === 'succeeded').length,
|
|
711
|
+
failedTasks: scoped.filter((task) => task.status === 'failed').length,
|
|
712
|
+
runningTasks: scoped.filter((task) => task.status === 'running').length,
|
|
570
713
|
directFailures: scopedFailures.length,
|
|
571
|
-
nextListTimeouts: scopedFailures.filter(failure => failure.kind === 'next_list_timeout')
|
|
572
|
-
|
|
714
|
+
nextListTimeouts: scopedFailures.filter((failure) => failure.kind === 'next_list_timeout')
|
|
715
|
+
.length,
|
|
716
|
+
actionFailures: scopedFailures.filter((failure) => failure.kind === 'action_failed').length,
|
|
573
717
|
signals: scoped.reduce((count, task) => count + task.signalIds.length, 0),
|
|
574
718
|
},
|
|
575
719
|
};
|
|
576
720
|
});
|
|
577
|
-
const unscopedTasks = tasks.filter(task => executionSessionIds.get(task.executionId) == null);
|
|
721
|
+
const unscopedTasks = tasks.filter((task) => executionSessionIds.get(task.executionId) == null);
|
|
578
722
|
return {
|
|
579
723
|
schemaVersion: MLA_RUNTIME_INSPECTION_SCHEMA_VERSION,
|
|
580
724
|
sessions,
|
|
@@ -585,7 +729,9 @@ export const buildRuntimeInspection = (output, framework, sourceSegments) => {
|
|
|
585
729
|
warnings: [
|
|
586
730
|
...framework.warnings,
|
|
587
731
|
...(unscopedTasks.length
|
|
588
|
-
? [
|
|
732
|
+
? [
|
|
733
|
+
`${unscopedTasks.length} task execution(s) could not be assigned to one runtime session.`,
|
|
734
|
+
]
|
|
589
735
|
: []),
|
|
590
736
|
],
|
|
591
737
|
};
|
package/dist/taskIdentity.js
CHANGED
|
@@ -19,8 +19,10 @@ export const buildTaskIdentity = (task) => {
|
|
|
19
19
|
export const isSameTask = (left, right) => {
|
|
20
20
|
if (left === right)
|
|
21
21
|
return true;
|
|
22
|
-
if (typeof left._startEventIndex === 'number' &&
|
|
23
|
-
|
|
22
|
+
if (typeof left._startEventIndex === 'number' &&
|
|
23
|
+
left._startEventIndex >= 0 &&
|
|
24
|
+
typeof right._startEventIndex === 'number' &&
|
|
25
|
+
right._startEventIndex >= 0) {
|
|
24
26
|
return left._startEventIndex === right._startEventIndex;
|
|
25
27
|
}
|
|
26
28
|
const leftUuid = normalize(left.uuid);
|
|
@@ -32,12 +34,12 @@ export const isSameTask = (left, right) => {
|
|
|
32
34
|
return buildCompositeIdentity(left) === buildCompositeIdentity(right);
|
|
33
35
|
};
|
|
34
36
|
export const findTaskIndex = (tasks, target) => {
|
|
35
|
-
const byRef = tasks.findIndex(task => task === target);
|
|
37
|
+
const byRef = tasks.findIndex((task) => task === target);
|
|
36
38
|
if (byRef >= 0)
|
|
37
39
|
return byRef;
|
|
38
|
-
const byIdentity = tasks.findIndex(task => isSameTask(task, target));
|
|
40
|
+
const byIdentity = tasks.findIndex((task) => isSameTask(task, target));
|
|
39
41
|
if (byIdentity >= 0)
|
|
40
42
|
return byIdentity;
|
|
41
43
|
const targetIdentity = buildTaskIdentity(target);
|
|
42
|
-
return tasks.findIndex(task => buildTaskIdentity(task) === targetIdentity);
|
|
44
|
+
return tasks.findIndex((task) => buildTaskIdentity(task) === targetIdentity);
|
|
43
45
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windsland52/maa-log-tools",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
6
|
"bin": {
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"dependencies": {
|
|
44
44
|
"fflate": "^0.8.2",
|
|
45
45
|
"@windsland52/maa-log-adapter": "1.1.0",
|
|
46
|
-
"@windsland52/maa-log-parser": "1.1.0",
|
|
47
46
|
"@windsland52/maa-log-kernel": "1.0.2",
|
|
47
|
+
"@windsland52/maa-log-parser": "1.2.0",
|
|
48
48
|
"@windsland52/maa-log-runtime": "1.1.0"
|
|
49
49
|
},
|
|
50
50
|
"engines": {
|