@windsland52/maa-log-tools 0.0.1 → 1.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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/cli.d.ts +0 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +9 -3
- package/dist/nextListPresentation.d.ts +0 -0
- package/dist/nextListPresentation.js +0 -0
- package/dist/nodeExecutionName.d.ts +0 -0
- package/dist/nodeExecutionName.js +0 -0
- package/dist/nodeExecutionTimeline.d.ts +0 -0
- package/dist/nodeExecutionTimeline.js +1 -1
- package/dist/nodeInput.d.ts +14 -3
- package/dist/nodeInput.js +172 -50
- package/dist/taskExecutionOrder.d.ts +0 -0
- package/dist/taskExecutionOrder.js +0 -0
- package/dist/taskIdentity.d.ts +0 -0
- package/dist/taskIdentity.js +0 -0
- package/package.json +18 -13
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Windsland52
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -24,9 +24,24 @@ The concrete adapter is provided by `@windsland52/maa-log-adapter`.
|
|
|
24
24
|
- `DEFAULT_CORE_PARSE_OPTIONS`
|
|
25
25
|
- `@windsland52/maa-log-tools/node-input`
|
|
26
26
|
- Node file/zip/folder extraction helpers
|
|
27
|
+
- `LogBundleFocus`
|
|
27
28
|
- `@windsland52/maa-log-tools/cli`
|
|
28
29
|
- CLI entry module
|
|
29
30
|
|
|
31
|
+
## Focused Loading
|
|
32
|
+
|
|
33
|
+
`analyzeZipBuffer`, `analyzeZipFile`, `analyzeDirectory`, `extractZipContentFromNodeBuffer`, `extractZipContentFromNodeFile`, and `loadNodeLogDirectory` all accept an optional `focus` selector:
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
{
|
|
37
|
+
keywords?: string[]
|
|
38
|
+
started_after?: string
|
|
39
|
+
started_before?: string
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
When `focus` is provided, the helpers scan candidate primary and history log files and only merge files whose content matches the keywords and/or timestamp boundaries. If `focus` is omitted, the previous default loading behavior is preserved.
|
|
44
|
+
|
|
30
45
|
## CLI
|
|
31
46
|
|
|
32
47
|
```bash
|
package/dist/cli.d.ts
CHANGED
|
File without changes
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import type { KernelOutput } from '@windsland52/maa-log-kernel';
|
|
2
2
|
import type { AnalyzeLogContentInput, ParseFileOptions } from '@windsland52/maa-log-runtime';
|
|
3
|
+
import { type LogBundleFocus } from './nodeInput';
|
|
3
4
|
type ParseOptions = ParseFileOptions;
|
|
4
5
|
export interface AnalyzeZipBufferInput {
|
|
5
6
|
zipData: Uint8Array;
|
|
6
7
|
sourceRef?: string;
|
|
8
|
+
focus?: LogBundleFocus;
|
|
7
9
|
parseOptions?: ParseOptions;
|
|
8
10
|
parserVersion?: string;
|
|
9
11
|
}
|
|
10
12
|
export interface AnalyzeZipFileInput {
|
|
11
13
|
zipFilePath: string;
|
|
14
|
+
focus?: LogBundleFocus;
|
|
12
15
|
parseOptions?: ParseOptions;
|
|
13
16
|
parserVersion?: string;
|
|
14
17
|
}
|
|
15
18
|
export interface AnalyzeDirectoryInput {
|
|
16
19
|
directoryPath: string;
|
|
20
|
+
focus?: LogBundleFocus;
|
|
17
21
|
parseOptions?: ParseOptions;
|
|
18
22
|
parserVersion?: string;
|
|
19
23
|
}
|
package/dist/index.js
CHANGED
|
@@ -8,7 +8,9 @@ export const analyzeLogContent = async (input) => {
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
export const analyzeZipBuffer = async (input) => {
|
|
11
|
-
const extracted = extractZipContentFromNodeBuffer(input.zipData, input.sourceRef
|
|
11
|
+
const extracted = extractZipContentFromNodeBuffer(input.zipData, input.sourceRef, {
|
|
12
|
+
focus: input.focus,
|
|
13
|
+
});
|
|
12
14
|
if (!extracted)
|
|
13
15
|
return null;
|
|
14
16
|
return analyzeLogContent({
|
|
@@ -21,7 +23,9 @@ export const analyzeZipBuffer = async (input) => {
|
|
|
21
23
|
});
|
|
22
24
|
};
|
|
23
25
|
export const analyzeZipFile = async (input) => {
|
|
24
|
-
const extracted = await extractZipContentFromNodeFile(input.zipFilePath
|
|
26
|
+
const extracted = await extractZipContentFromNodeFile(input.zipFilePath, {
|
|
27
|
+
focus: input.focus,
|
|
28
|
+
});
|
|
25
29
|
if (!extracted)
|
|
26
30
|
return null;
|
|
27
31
|
return analyzeLogContent({
|
|
@@ -34,7 +38,9 @@ export const analyzeZipFile = async (input) => {
|
|
|
34
38
|
});
|
|
35
39
|
};
|
|
36
40
|
export const analyzeDirectory = async (input) => {
|
|
37
|
-
const extracted = await loadNodeLogDirectory(input.directoryPath
|
|
41
|
+
const extracted = await loadNodeLogDirectory(input.directoryPath, {
|
|
42
|
+
focus: input.focus,
|
|
43
|
+
});
|
|
38
44
|
if (!extracted)
|
|
39
45
|
return null;
|
|
40
46
|
return analyzeLogContent({
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -3,7 +3,7 @@ import { sortNodesByGlobalExecutionOrder } from './taskExecutionOrder.js';
|
|
|
3
3
|
const isNodeActionFailed = (node) => {
|
|
4
4
|
if (node.action_details && node.action_details.success === false)
|
|
5
5
|
return true;
|
|
6
|
-
return (node.node_flow || []).some((item) => item.type === 'action' && item.status === 'failed');
|
|
6
|
+
return (node.node_flow || []).some((item) => (item.type === 'action' || item.type === 'action_node') && item.status === 'failed');
|
|
7
7
|
};
|
|
8
8
|
export const buildNodeExecutionTimeline = (nodes, options = {}) => {
|
|
9
9
|
const originalIndexByNode = new Map();
|
package/dist/nodeInput.d.ts
CHANGED
|
@@ -11,7 +11,18 @@ export interface NodeExtractedLogContent {
|
|
|
11
11
|
waitFreezesImages: Map<string, string>;
|
|
12
12
|
textFiles: KernelTextFile[];
|
|
13
13
|
}
|
|
14
|
+
export interface LogBundleFocus {
|
|
15
|
+
keywords?: string[];
|
|
16
|
+
started_after?: string;
|
|
17
|
+
started_before?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface ExtractZipContentOptions {
|
|
20
|
+
focus?: LogBundleFocus;
|
|
21
|
+
}
|
|
22
|
+
export interface LoadNodeLogDirectoryOptions {
|
|
23
|
+
focus?: LogBundleFocus;
|
|
24
|
+
}
|
|
14
25
|
export declare const readNodeTextFileContent: (filePath: string) => Promise<string>;
|
|
15
|
-
export declare const extractZipContentFromNodeBuffer: (zipData: Uint8Array, sourceRef?: string) => NodeExtractedLogContent | null;
|
|
16
|
-
export declare const extractZipContentFromNodeFile: (zipFilePath: string) => Promise<NodeExtractedLogContent | null>;
|
|
17
|
-
export declare const loadNodeLogDirectory: (inputDirectoryPath: string) => Promise<NodeExtractedLogContent | null>;
|
|
26
|
+
export declare const extractZipContentFromNodeBuffer: (zipData: Uint8Array, sourceRef?: string, options?: ExtractZipContentOptions) => NodeExtractedLogContent | null;
|
|
27
|
+
export declare const extractZipContentFromNodeFile: (zipFilePath: string, options?: ExtractZipContentOptions) => Promise<NodeExtractedLogContent | null>;
|
|
28
|
+
export declare const loadNodeLogDirectory: (inputDirectoryPath: string, options?: LoadNodeLogDirectoryOptions) => Promise<NodeExtractedLogContent | null>;
|
package/dist/nodeInput.js
CHANGED
|
@@ -4,16 +4,24 @@ import { unzipSync } from 'fflate';
|
|
|
4
4
|
const MAIN_LOG_NAMES = ['maa.log', 'maafw.log'];
|
|
5
5
|
const BAK_LOG_NAMES = ['maa.bak.log', 'maafw.bak.log'];
|
|
6
6
|
const SEARCH_TEXT_EXTENSIONS = ['.log', '.txt', '.jsonl'];
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const MAIN_LOG_NAME_SET = new Set(MAIN_LOG_NAMES.map((name) => name.toLowerCase()));
|
|
8
|
+
const HISTORY_LOG_NAME_PATTERNS = [
|
|
9
|
+
/^maa\.bak(?:\..+)?\.log$/i,
|
|
10
|
+
/^maafw\.bak(?:\..+)?\.log$/i,
|
|
11
|
+
];
|
|
11
12
|
const toPosixPath = (value) => value.replace(/\\/g, '/');
|
|
12
13
|
const normalizeLowerPath = (value) => toPosixPath(value).toLowerCase();
|
|
13
14
|
const isSearchTextFile = (normalizedPath) => {
|
|
14
15
|
const lower = normalizedPath.toLowerCase();
|
|
15
16
|
return SEARCH_TEXT_EXTENSIONS.some((ext) => lower.endsWith(ext));
|
|
16
17
|
};
|
|
18
|
+
const isHistoryLogName = (fileName) => {
|
|
19
|
+
return HISTORY_LOG_NAME_PATTERNS.some((pattern) => pattern.test(fileName));
|
|
20
|
+
};
|
|
21
|
+
const isCoreLogName = (fileName) => {
|
|
22
|
+
const lower = fileName.toLowerCase();
|
|
23
|
+
return MAIN_LOG_NAME_SET.has(lower) || isHistoryLogName(lower);
|
|
24
|
+
};
|
|
17
25
|
const decodeNodeBytes = (bytes) => {
|
|
18
26
|
const encodings = ['utf-8', 'gbk', 'gb18030', 'gb2312'];
|
|
19
27
|
for (const encoding of encodings) {
|
|
@@ -84,13 +92,11 @@ const isNeededZipEntry = (entryPath) => {
|
|
|
84
92
|
const name = lower.slice(lower.lastIndexOf('/') + 1);
|
|
85
93
|
if (isSearchTextFile(lower))
|
|
86
94
|
return true;
|
|
87
|
-
if (
|
|
95
|
+
if (isCoreLogName(name))
|
|
88
96
|
return true;
|
|
89
|
-
if (
|
|
97
|
+
if ((lower.includes('/on_error/') || lower.startsWith('on_error/')) && lower.endsWith('.png'))
|
|
90
98
|
return true;
|
|
91
|
-
if (lower.includes('/
|
|
92
|
-
return true;
|
|
93
|
-
if (lower.includes('/vision/') && lower.endsWith('.jpg'))
|
|
99
|
+
if ((lower.includes('/vision/') || lower.startsWith('vision/')) && lower.endsWith('.jpg'))
|
|
94
100
|
return true;
|
|
95
101
|
return false;
|
|
96
102
|
};
|
|
@@ -100,11 +106,132 @@ const toZipReference = (sourceRef, entryPath) => {
|
|
|
100
106
|
const toFileReference = (absolutePath) => {
|
|
101
107
|
return `file:${toPosixPath(absolutePath)}`;
|
|
102
108
|
};
|
|
109
|
+
const isRelativeImagePath = (relativePath, directory, extension) => {
|
|
110
|
+
const normalized = relativePath.toLowerCase();
|
|
111
|
+
return normalized === `${directory}${extension}`
|
|
112
|
+
|| normalized.startsWith(`${directory}/`)
|
|
113
|
+
|| normalized.includes(`/${directory}/`);
|
|
114
|
+
};
|
|
115
|
+
const normalizeTimestampBoundary = (value) => {
|
|
116
|
+
if (!value)
|
|
117
|
+
return null;
|
|
118
|
+
const trimmed = value.trim();
|
|
119
|
+
if (trimmed.length === 0)
|
|
120
|
+
return null;
|
|
121
|
+
return trimmed.includes('.') ? trimmed : `${trimmed}.000`;
|
|
122
|
+
};
|
|
123
|
+
const extractTimestamps = (content) => {
|
|
124
|
+
const matches = content.match(/\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{1,3})?)\]/g) ?? [];
|
|
125
|
+
return matches
|
|
126
|
+
.map((item) => item.slice(1, -1))
|
|
127
|
+
.map((item) => normalizeTimestampBoundary(item) ?? item);
|
|
128
|
+
};
|
|
129
|
+
const contentMatchesFocus = (content, focus) => {
|
|
130
|
+
const keywords = (focus.keywords ?? []).filter((keyword) => keyword.trim().length > 0);
|
|
131
|
+
if (keywords.length > 0 && !keywords.some((keyword) => content.includes(keyword))) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
const startedAfter = normalizeTimestampBoundary(focus.started_after);
|
|
135
|
+
const startedBefore = normalizeTimestampBoundary(focus.started_before);
|
|
136
|
+
if (!startedAfter && !startedBefore) {
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
return extractTimestamps(content).some((timestamp) => {
|
|
140
|
+
if (startedAfter && timestamp < startedAfter) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
if (startedBefore && timestamp > startedBefore) {
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
return true;
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const joinMergedContent = (chunks) => {
|
|
150
|
+
return chunks.reduce((result, chunk) => {
|
|
151
|
+
if (chunk.length === 0)
|
|
152
|
+
return result;
|
|
153
|
+
if (result.length === 0)
|
|
154
|
+
return chunk;
|
|
155
|
+
return result.endsWith('\n') ? `${result}${chunk}` : `${result}\n${chunk}`;
|
|
156
|
+
}, '');
|
|
157
|
+
};
|
|
158
|
+
const rankLogPath = (filePath) => {
|
|
159
|
+
const baseName = path.basename(filePath).toLowerCase();
|
|
160
|
+
if (baseName === 'maafw.bak.log' || baseName.startsWith('maafw.bak.')) {
|
|
161
|
+
return 0;
|
|
162
|
+
}
|
|
163
|
+
if (baseName === 'maa.bak.log' || baseName.startsWith('maa.bak.')) {
|
|
164
|
+
return 1;
|
|
165
|
+
}
|
|
166
|
+
if (baseName === 'maafw.log') {
|
|
167
|
+
return 2;
|
|
168
|
+
}
|
|
169
|
+
if (baseName === 'maa.log') {
|
|
170
|
+
return 3;
|
|
171
|
+
}
|
|
172
|
+
return 10;
|
|
173
|
+
};
|
|
174
|
+
const sortLogPaths = (paths) => {
|
|
175
|
+
return [...paths].sort((left, right) => {
|
|
176
|
+
const rankDiff = rankLogPath(left) - rankLogPath(right);
|
|
177
|
+
if (rankDiff !== 0)
|
|
178
|
+
return rankDiff;
|
|
179
|
+
return left.localeCompare(right);
|
|
180
|
+
});
|
|
181
|
+
};
|
|
182
|
+
const collectFocusedFileContents = async (logPaths, focus) => {
|
|
183
|
+
const chunks = [];
|
|
184
|
+
for (const logPath of sortLogPaths(logPaths)) {
|
|
185
|
+
const content = await readNodeTextFileContent(logPath);
|
|
186
|
+
if (!contentMatchesFocus(content, focus))
|
|
187
|
+
continue;
|
|
188
|
+
chunks.push(content);
|
|
189
|
+
}
|
|
190
|
+
return joinMergedContent(chunks);
|
|
191
|
+
};
|
|
192
|
+
const collectFocusedZipContents = (entries, paths, basePath, focus) => {
|
|
193
|
+
const normalizedBasePath = normalizeLowerPath(basePath);
|
|
194
|
+
const candidatePaths = sortLogPaths(paths.filter((entryPath) => {
|
|
195
|
+
const normalizedPath = toPosixPath(entryPath);
|
|
196
|
+
const lastSlash = normalizedPath.lastIndexOf('/');
|
|
197
|
+
const parentPath = lastSlash === -1 ? '' : normalizedPath.slice(0, lastSlash);
|
|
198
|
+
if (normalizeLowerPath(parentPath) !== normalizedBasePath) {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
const fileName = normalizedPath.slice(lastSlash + 1);
|
|
202
|
+
return isCoreLogName(fileName);
|
|
203
|
+
}));
|
|
204
|
+
const chunks = [];
|
|
205
|
+
for (const entryPath of candidatePaths) {
|
|
206
|
+
const bytes = entries[entryPath];
|
|
207
|
+
if (!bytes)
|
|
208
|
+
continue;
|
|
209
|
+
const content = decodeNodeBytes(bytes);
|
|
210
|
+
if (!contentMatchesFocus(content, focus))
|
|
211
|
+
continue;
|
|
212
|
+
chunks.push(content);
|
|
213
|
+
}
|
|
214
|
+
return joinMergedContent(chunks);
|
|
215
|
+
};
|
|
216
|
+
const buildDefaultZipContent = (entries, paths, basePath) => {
|
|
217
|
+
const bakLogName = BAK_LOG_NAMES.find((name) => findZipEntry(entries, paths, joinPath(basePath, name)));
|
|
218
|
+
const mainLogName = MAIN_LOG_NAMES.find((name) => findZipEntry(entries, paths, joinPath(basePath, name)));
|
|
219
|
+
const bakData = bakLogName ? findZipEntry(entries, paths, joinPath(basePath, bakLogName)) : null;
|
|
220
|
+
const mainData = mainLogName ? findZipEntry(entries, paths, joinPath(basePath, mainLogName)) : null;
|
|
221
|
+
const chunks = [];
|
|
222
|
+
if (bakData) {
|
|
223
|
+
chunks.push(decodeNodeBytes(bakData));
|
|
224
|
+
}
|
|
225
|
+
if (mainData) {
|
|
226
|
+
chunks.push(decodeNodeBytes(mainData));
|
|
227
|
+
}
|
|
228
|
+
return joinMergedContent(chunks);
|
|
229
|
+
};
|
|
103
230
|
export const readNodeTextFileContent = async (filePath) => {
|
|
104
231
|
const bytes = await readFile(filePath);
|
|
105
232
|
return decodeNodeBytes(new Uint8Array(bytes));
|
|
106
233
|
};
|
|
107
|
-
export const extractZipContentFromNodeBuffer = (zipData, sourceRef = 'memory.zip') => {
|
|
234
|
+
export const extractZipContentFromNodeBuffer = (zipData, sourceRef = 'memory.zip', options = {}) => {
|
|
108
235
|
const files = unzipSync(zipData, {
|
|
109
236
|
filter: (entry) => isNeededZipEntry(entry.name),
|
|
110
237
|
});
|
|
@@ -112,19 +239,9 @@ export const extractZipContentFromNodeBuffer = (zipData, sourceRef = 'memory.zip
|
|
|
112
239
|
const basePath = findBaseDirectory(paths);
|
|
113
240
|
if (basePath == null)
|
|
114
241
|
return null;
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const mainData = mainLogName ? findZipEntry(files, paths, joinPath(basePath, mainLogName)) : null;
|
|
119
|
-
let content = '';
|
|
120
|
-
if (bakData) {
|
|
121
|
-
content += decodeNodeBytes(bakData);
|
|
122
|
-
}
|
|
123
|
-
if (mainData) {
|
|
124
|
-
if (content && !content.endsWith('\n'))
|
|
125
|
-
content += '\n';
|
|
126
|
-
content += decodeNodeBytes(mainData);
|
|
127
|
-
}
|
|
242
|
+
const content = options.focus
|
|
243
|
+
? collectFocusedZipContents(files, paths, basePath, options.focus)
|
|
244
|
+
: buildDefaultZipContent(files, paths, basePath);
|
|
128
245
|
if (!content)
|
|
129
246
|
return null;
|
|
130
247
|
const errorImages = new Map();
|
|
@@ -153,24 +270,26 @@ export const extractZipContentFromNodeBuffer = (zipData, sourceRef = 'memory.zip
|
|
|
153
270
|
waitFreezesImages.set(waitKey, toZipReference(sourceRef, normalizedPath));
|
|
154
271
|
}
|
|
155
272
|
}
|
|
156
|
-
if (isSearchTextFile(normalizedPath))
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
273
|
+
if (!isSearchTextFile(normalizedPath))
|
|
274
|
+
continue;
|
|
275
|
+
if (isCoreLogName(fileName))
|
|
276
|
+
continue;
|
|
277
|
+
const fileData = files[currentPath];
|
|
278
|
+
if (!fileData)
|
|
279
|
+
continue;
|
|
280
|
+
textFiles.push({
|
|
281
|
+
path: normalizedPath,
|
|
282
|
+
name: fileName,
|
|
283
|
+
content: decodeNodeBytes(fileData),
|
|
284
|
+
reference: toZipReference(sourceRef, normalizedPath),
|
|
285
|
+
});
|
|
167
286
|
}
|
|
168
287
|
textFiles.sort((a, b) => a.path.localeCompare(b.path));
|
|
169
288
|
return { content, errorImages, visionImages, waitFreezesImages, textFiles };
|
|
170
289
|
};
|
|
171
|
-
export const extractZipContentFromNodeFile = async (zipFilePath) => {
|
|
290
|
+
export const extractZipContentFromNodeFile = async (zipFilePath, options = {}) => {
|
|
172
291
|
const bytes = await readFile(zipFilePath);
|
|
173
|
-
return extractZipContentFromNodeBuffer(new Uint8Array(bytes), zipFilePath);
|
|
292
|
+
return extractZipContentFromNodeBuffer(new Uint8Array(bytes), zipFilePath, options);
|
|
174
293
|
};
|
|
175
294
|
const pathExists = async (targetPath) => {
|
|
176
295
|
try {
|
|
@@ -244,22 +363,26 @@ const pickPrimaryLogPath = async (debugPath, allFiles, candidates) => {
|
|
|
244
363
|
}
|
|
245
364
|
return null;
|
|
246
365
|
};
|
|
247
|
-
|
|
248
|
-
const debugPath = await resolveDebugDirectory(inputDirectoryPath);
|
|
249
|
-
if (!debugPath)
|
|
250
|
-
return null;
|
|
251
|
-
const allFiles = await collectFilesRecursively(debugPath);
|
|
366
|
+
const buildDefaultDirectoryContent = async (debugPath, allFiles) => {
|
|
252
367
|
const bakLogPath = await pickPrimaryLogPath(debugPath, allFiles, BAK_LOG_NAMES);
|
|
253
368
|
const mainLogPath = await pickPrimaryLogPath(debugPath, allFiles, MAIN_LOG_NAMES);
|
|
254
|
-
|
|
369
|
+
const chunks = [];
|
|
255
370
|
if (bakLogPath) {
|
|
256
|
-
|
|
371
|
+
chunks.push(await readNodeTextFileContent(bakLogPath));
|
|
257
372
|
}
|
|
258
373
|
if (mainLogPath) {
|
|
259
|
-
|
|
260
|
-
content += '\n';
|
|
261
|
-
content += await readNodeTextFileContent(mainLogPath);
|
|
374
|
+
chunks.push(await readNodeTextFileContent(mainLogPath));
|
|
262
375
|
}
|
|
376
|
+
return joinMergedContent(chunks);
|
|
377
|
+
};
|
|
378
|
+
export const loadNodeLogDirectory = async (inputDirectoryPath, options = {}) => {
|
|
379
|
+
const debugPath = await resolveDebugDirectory(inputDirectoryPath);
|
|
380
|
+
if (!debugPath)
|
|
381
|
+
return null;
|
|
382
|
+
const allFiles = await collectFilesRecursively(debugPath);
|
|
383
|
+
const content = options.focus
|
|
384
|
+
? await collectFocusedFileContents(allFiles.filter((filePath) => isCoreLogName(path.basename(filePath))), options.focus)
|
|
385
|
+
: await buildDefaultDirectoryContent(debugPath, allFiles);
|
|
263
386
|
if (!content)
|
|
264
387
|
return null;
|
|
265
388
|
const errorImages = new Map();
|
|
@@ -270,14 +393,13 @@ export const loadNodeLogDirectory = async (inputDirectoryPath) => {
|
|
|
270
393
|
const relativePath = toPosixPath(path.relative(debugPath, absolutePath));
|
|
271
394
|
const lowerRelativePath = relativePath.toLowerCase();
|
|
272
395
|
const fileName = path.basename(absolutePath);
|
|
273
|
-
|
|
274
|
-
if (lowerRelativePath.includes('/on_error/') && lowerRelativePath.endsWith('.png')) {
|
|
396
|
+
if (isRelativeImagePath(lowerRelativePath, 'on_error', '.png')) {
|
|
275
397
|
const key = parseErrorImageKey(fileName);
|
|
276
398
|
if (key) {
|
|
277
399
|
errorImages.set(key, toFileReference(absolutePath));
|
|
278
400
|
}
|
|
279
401
|
}
|
|
280
|
-
if (lowerRelativePath
|
|
402
|
+
if (isRelativeImagePath(lowerRelativePath, 'vision', '.jpg')) {
|
|
281
403
|
const visionKey = parseVisionImageKey(fileName);
|
|
282
404
|
if (visionKey) {
|
|
283
405
|
visionImages.set(visionKey, toFileReference(absolutePath));
|
|
@@ -289,7 +411,7 @@ export const loadNodeLogDirectory = async (inputDirectoryPath) => {
|
|
|
289
411
|
}
|
|
290
412
|
if (!isSearchTextFile(relativePath))
|
|
291
413
|
continue;
|
|
292
|
-
if (
|
|
414
|
+
if (isCoreLogName(fileName))
|
|
293
415
|
continue;
|
|
294
416
|
textFiles.push({
|
|
295
417
|
path: relativePath,
|
|
File without changes
|
|
File without changes
|
package/dist/taskIdentity.d.ts
CHANGED
|
File without changes
|
package/dist/taskIdentity.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,13 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@windsland52/maa-log-tools",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"private": false,
|
|
6
|
-
"scripts": {
|
|
7
|
-
"typecheck": "tsc -p ./tsconfig.json",
|
|
8
|
-
"build": "pnpm run clean && tsc -p ./tsconfig.build.json && node ../../scripts/fix-esm-imports.mjs ./dist",
|
|
9
|
-
"clean": "node -e \"require('node:fs').rmSync('dist',{ recursive: true, force: true })\""
|
|
10
|
-
},
|
|
11
6
|
"bin": {
|
|
12
7
|
"mla-log-tools": "./dist/cli.js"
|
|
13
8
|
},
|
|
@@ -46,11 +41,11 @@
|
|
|
46
41
|
}
|
|
47
42
|
},
|
|
48
43
|
"dependencies": {
|
|
49
|
-
"
|
|
50
|
-
"@windsland52/maa-log-
|
|
51
|
-
"@windsland52/maa-log-
|
|
52
|
-
"@windsland52/maa-log-runtime": "
|
|
53
|
-
"
|
|
44
|
+
"fflate": "^0.8.2",
|
|
45
|
+
"@windsland52/maa-log-adapter": "1.0.0",
|
|
46
|
+
"@windsland52/maa-log-kernel": "1.0.0",
|
|
47
|
+
"@windsland52/maa-log-runtime": "1.0.0",
|
|
48
|
+
"@windsland52/maa-log-parser": "1.0.0"
|
|
54
49
|
},
|
|
55
50
|
"engines": {
|
|
56
51
|
"node": ">=24.0.0"
|
|
@@ -61,5 +56,15 @@
|
|
|
61
56
|
"files": [
|
|
62
57
|
"dist",
|
|
63
58
|
"README.md"
|
|
64
|
-
]
|
|
65
|
-
|
|
59
|
+
],
|
|
60
|
+
"repository": {
|
|
61
|
+
"type": "git",
|
|
62
|
+
"url": "https://github.com/MaaXYZ/MaaLogAnalyzer",
|
|
63
|
+
"directory": "packages/maa-log-tools"
|
|
64
|
+
},
|
|
65
|
+
"scripts": {
|
|
66
|
+
"typecheck": "tsc -p ./tsconfig.json",
|
|
67
|
+
"build": "pnpm run clean && tsc -p ./tsconfig.build.json && node ../../scripts/fix-esm-imports.mjs ./dist",
|
|
68
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{ recursive: true, force: true })\""
|
|
69
|
+
}
|
|
70
|
+
}
|