@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 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();
@@ -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 PRIMARY_LOG_NAME_SET = new Set([
8
- ...MAIN_LOG_NAMES,
9
- ...BAK_LOG_NAMES,
10
- ].map((name) => name.toLowerCase()));
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 (MAIN_LOG_NAMES.includes(name))
95
+ if (isCoreLogName(name))
88
96
  return true;
89
- if (BAK_LOG_NAMES.includes(name))
97
+ if ((lower.includes('/on_error/') || lower.startsWith('on_error/')) && lower.endsWith('.png'))
90
98
  return true;
91
- if (lower.includes('/on_error/') && lower.endsWith('.png'))
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 bakLogName = BAK_LOG_NAMES.find((name) => findZipEntry(files, paths, joinPath(basePath, name)));
116
- const mainLogName = MAIN_LOG_NAMES.find((name) => findZipEntry(files, paths, joinPath(basePath, name)));
117
- const bakData = bakLogName ? findZipEntry(files, paths, joinPath(basePath, bakLogName)) : null;
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
- const fileData = files[currentPath];
158
- if (!fileData)
159
- continue;
160
- textFiles.push({
161
- path: normalizedPath,
162
- name: fileName,
163
- content: decodeNodeBytes(fileData),
164
- reference: toZipReference(sourceRef, normalizedPath),
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
- export const loadNodeLogDirectory = async (inputDirectoryPath) => {
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
- let content = '';
369
+ const chunks = [];
255
370
  if (bakLogPath) {
256
- content += await readNodeTextFileContent(bakLogPath);
371
+ chunks.push(await readNodeTextFileContent(bakLogPath));
257
372
  }
258
373
  if (mainLogPath) {
259
- if (content && !content.endsWith('\n'))
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
- const lowerFileName = fileName.toLowerCase();
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.includes('/vision/') && lowerRelativePath.endsWith('.jpg')) {
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 (PRIMARY_LOG_NAME_SET.has(lowerFileName))
414
+ if (isCoreLogName(fileName))
293
415
  continue;
294
416
  textFiles.push({
295
417
  path: relativePath,
File without changes
File without changes
File without changes
File without changes
package/package.json CHANGED
@@ -1,13 +1,8 @@
1
1
  {
2
2
  "name": "@windsland52/maa-log-tools",
3
- "version": "0.0.1",
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
- "@windsland52/maa-log-adapter": "workspace:*",
50
- "@windsland52/maa-log-kernel": "workspace:*",
51
- "@windsland52/maa-log-parser": "workspace:*",
52
- "@windsland52/maa-log-runtime": "workspace:*",
53
- "fflate": "^0.8.2"
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
+ }