@vite-plugin-opencode-assistant/opencode 1.0.59 → 1.0.60
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/es/plugins/service-logs.js +124 -192
- package/es/plugins/service-logs.mjs +13 -25
- package/lib/plugins/service-logs.cjs +12 -24
- package/package.json +2 -2
|
@@ -2,191 +2,134 @@ import { tool } from "@opencode-ai/plugin";
|
|
|
2
2
|
import fs from "fs";
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { c as createLogger } from "./logger.js";
|
|
5
|
-
var
|
|
6
|
-
|
|
7
|
-
var
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
__publicField(this, "maxSize");
|
|
13
|
-
__publicField(this, "name");
|
|
14
|
-
__publicField(this, "filePath");
|
|
15
|
-
__publicField(this, "lastPosition", 0);
|
|
16
|
-
__publicField(this, "watcher", null);
|
|
17
|
-
__publicField(this, "enabled", false);
|
|
18
|
-
var _a;
|
|
19
|
-
this.name = options.name;
|
|
20
|
-
this.filePath = options.filePath;
|
|
21
|
-
this.maxSize = (_a = options.maxBufferSize) != null ? _a : 200;
|
|
22
|
-
this.enabled = true;
|
|
23
|
-
}
|
|
24
|
-
start(projectRoot) {
|
|
25
|
-
const resolvedPath = this.resolvePath(projectRoot);
|
|
26
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
27
|
-
log$1.debug(`Log file does not exist: ${resolvedPath}`);
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
const stat = fs.statSync(resolvedPath);
|
|
31
|
-
this.lastPosition = stat.size;
|
|
32
|
-
this.watcher = fs.watch(resolvedPath, (eventType) => {
|
|
33
|
-
if (eventType === "change") {
|
|
34
|
-
this.readNewLogs(resolvedPath);
|
|
5
|
+
var __async = (__this, __arguments, generator) => {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
var fulfilled = (value) => {
|
|
8
|
+
try {
|
|
9
|
+
step(generator.next(value));
|
|
10
|
+
} catch (e) {
|
|
11
|
+
reject(e);
|
|
35
12
|
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
13
|
+
};
|
|
14
|
+
var rejected = (value) => {
|
|
15
|
+
try {
|
|
16
|
+
step(generator.throw(value));
|
|
17
|
+
} catch (e) {
|
|
18
|
+
reject(e);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
|
|
22
|
+
step((generator = generator.apply(__this, __arguments)).next());
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const log$1 = createLogger("FileLogReader");
|
|
26
|
+
function detectLogLevel(line) {
|
|
27
|
+
const lowerLine = line.toLowerCase();
|
|
28
|
+
if (lowerLine.includes("error") || lowerLine.includes("err") || lowerLine.includes("fatal")) {
|
|
29
|
+
return "error";
|
|
41
30
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
this.watcher.close();
|
|
45
|
-
this.watcher = null;
|
|
46
|
-
log$1.debug(`Stopped watching log file: ${this.filePath}`);
|
|
47
|
-
}
|
|
31
|
+
if (lowerLine.includes("warn") || lowerLine.includes("warning")) {
|
|
32
|
+
return "warn";
|
|
48
33
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
34
|
+
return "info";
|
|
35
|
+
}
|
|
36
|
+
function parseLogTimestamp(line) {
|
|
37
|
+
const timestampPatterns = [
|
|
38
|
+
/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z?)/,
|
|
39
|
+
/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/,
|
|
40
|
+
/(\[([^\]]+)\])/
|
|
41
|
+
];
|
|
42
|
+
for (const pattern of timestampPatterns) {
|
|
43
|
+
const match = line.match(pattern);
|
|
44
|
+
if (match) {
|
|
45
|
+
const timestampStr = match[1];
|
|
46
|
+
const date = new Date(timestampStr);
|
|
47
|
+
if (!isNaN(date.getTime())) {
|
|
48
|
+
return date.toISOString();
|
|
49
|
+
}
|
|
55
50
|
}
|
|
56
|
-
return path.resolve(process.cwd(), this.filePath);
|
|
57
51
|
}
|
|
58
|
-
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function readLogFileTail(options) {
|
|
55
|
+
return __async(this, null, function* () {
|
|
56
|
+
const { name, filePath, projectRoot, lines = 200, limit, level, since } = options;
|
|
57
|
+
const resolvedPath = resolvePath(filePath, projectRoot);
|
|
58
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
59
|
+
log$1.debug(`Log file does not exist: ${resolvedPath}`);
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
59
62
|
try {
|
|
60
|
-
const stat = fs.statSync(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
const stat = fs.statSync(resolvedPath);
|
|
64
|
+
const fd = fs.openSync(resolvedPath, "r");
|
|
65
|
+
const chunkSize = 16 * 1024;
|
|
66
|
+
let position = stat.size;
|
|
67
|
+
let buffer = Buffer.alloc(0);
|
|
68
|
+
const lineCount = 0;
|
|
69
|
+
while (position > 0 && lineCount <= lines) {
|
|
70
|
+
const readSize = Math.min(chunkSize, position);
|
|
71
|
+
position -= readSize;
|
|
72
|
+
const chunk = Buffer.alloc(readSize);
|
|
73
|
+
fs.readSync(fd, chunk, 0, readSize, position);
|
|
74
|
+
buffer = Buffer.concat([chunk, buffer]);
|
|
75
|
+
const newLineCount = buffer.filter((byte) => byte === 10).length;
|
|
76
|
+
if (newLineCount >= lines) {
|
|
77
|
+
const linesArray = buffer.toString("utf-8").split("\n");
|
|
78
|
+
const excessLines = newLineCount - lines;
|
|
79
|
+
let charsToRemove = 0;
|
|
80
|
+
let count = 0;
|
|
81
|
+
for (let i = 0; i < linesArray.length; i++) {
|
|
82
|
+
count += linesArray[i].length + 1;
|
|
83
|
+
if (count > excessLines) {
|
|
84
|
+
charsToRemove = linesArray.slice(0, i + 1).join("\n").length + 1;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
buffer = buffer.slice(charsToRemove);
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
63
91
|
}
|
|
64
|
-
const fd = fs.openSync(filePath, "r");
|
|
65
|
-
const buffer = Buffer.alloc(stat.size - this.lastPosition);
|
|
66
|
-
fs.readSync(fd, buffer, 0, buffer.length, this.lastPosition);
|
|
67
92
|
fs.closeSync(fd);
|
|
68
|
-
this.lastPosition = stat.size;
|
|
69
93
|
const content = buffer.toString("utf-8").trim();
|
|
70
|
-
|
|
71
|
-
|
|
94
|
+
const logLines = content.split("\n").filter((line) => line.trim());
|
|
95
|
+
const entries = [];
|
|
96
|
+
const sinceDate = since ? new Date(since) : null;
|
|
97
|
+
for (const line of logLines) {
|
|
98
|
+
const entry = {
|
|
99
|
+
level: detectLogLevel(line),
|
|
100
|
+
message: line,
|
|
101
|
+
timestamp: parseLogTimestamp(line) || (/* @__PURE__ */ new Date()).toISOString(),
|
|
102
|
+
source: `file:${name}`
|
|
103
|
+
};
|
|
104
|
+
if (sinceDate && new Date(entry.timestamp) < sinceDate) {
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (level) {
|
|
108
|
+
const levels = Array.isArray(level) ? level : [level];
|
|
109
|
+
if (!levels.includes(entry.level)) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
entries.push(entry);
|
|
114
|
+
}
|
|
115
|
+
if (limit && limit > 0) {
|
|
116
|
+
return entries.slice(-limit);
|
|
72
117
|
}
|
|
118
|
+
return entries;
|
|
73
119
|
} catch (err) {
|
|
74
|
-
log$1.error(`Error reading log file ${
|
|
75
|
-
|
|
76
|
-
}
|
|
77
|
-
processLogContent(content) {
|
|
78
|
-
const lines = content.split("\n");
|
|
79
|
-
for (const line of lines) {
|
|
80
|
-
if (!line.trim()) continue;
|
|
81
|
-
this.addEntry({
|
|
82
|
-
level: this.detectLogLevel(line),
|
|
83
|
-
message: line,
|
|
84
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
85
|
-
source: `file:${this.name}`
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
detectLogLevel(line) {
|
|
90
|
-
const lowerLine = line.toLowerCase();
|
|
91
|
-
if (lowerLine.includes("error") || lowerLine.includes("err") || lowerLine.includes("fatal")) {
|
|
92
|
-
return "error";
|
|
93
|
-
}
|
|
94
|
-
if (lowerLine.includes("warn") || lowerLine.includes("warning")) {
|
|
95
|
-
return "warn";
|
|
96
|
-
}
|
|
97
|
-
return "info";
|
|
98
|
-
}
|
|
99
|
-
addEntry(entry) {
|
|
100
|
-
if (!this.enabled) return;
|
|
101
|
-
if (this.buffer.length >= this.maxSize) {
|
|
102
|
-
this.buffer.shift();
|
|
120
|
+
log$1.error(`Error reading log file ${resolvedPath}`, { error: err });
|
|
121
|
+
return [];
|
|
103
122
|
}
|
|
104
|
-
|
|
105
|
-
}
|
|
106
|
-
getLogs(options = {}) {
|
|
107
|
-
let logs = [...this.buffer];
|
|
108
|
-
if (options.level) {
|
|
109
|
-
const levels = Array.isArray(options.level) ? options.level : [options.level];
|
|
110
|
-
logs = logs.filter((log2) => levels.includes(log2.level));
|
|
111
|
-
}
|
|
112
|
-
if (options.since) {
|
|
113
|
-
const sinceDate = new Date(options.since);
|
|
114
|
-
logs = logs.filter((log2) => new Date(log2.timestamp) >= sinceDate);
|
|
115
|
-
}
|
|
116
|
-
if (options.limit && options.limit > 0) {
|
|
117
|
-
logs = logs.slice(-options.limit);
|
|
118
|
-
}
|
|
119
|
-
return logs;
|
|
120
|
-
}
|
|
121
|
-
clear() {
|
|
122
|
-
this.buffer = [];
|
|
123
|
-
}
|
|
124
|
-
size() {
|
|
125
|
-
return this.buffer.length;
|
|
126
|
-
}
|
|
127
|
-
setEnabled(enabled) {
|
|
128
|
-
this.enabled = enabled;
|
|
129
|
-
}
|
|
130
|
-
isEnabled() {
|
|
131
|
-
return this.enabled;
|
|
132
|
-
}
|
|
133
|
-
getName() {
|
|
134
|
-
return this.name;
|
|
135
|
-
}
|
|
136
|
-
getFilePath() {
|
|
137
|
-
return this.filePath;
|
|
138
|
-
}
|
|
123
|
+
});
|
|
139
124
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
__publicField(this, "projectRoot", null);
|
|
144
|
-
}
|
|
145
|
-
setProjectRoot(root) {
|
|
146
|
-
this.projectRoot = root;
|
|
147
|
-
}
|
|
148
|
-
addLogFile(options) {
|
|
149
|
-
var _a;
|
|
150
|
-
if (this.buffers.has(options.name)) {
|
|
151
|
-
log$1.warn(`Log file "${options.name}" already exists, skipping`);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
const buffer = new FileLogBuffer(options);
|
|
155
|
-
buffer.start((_a = this.projectRoot) != null ? _a : void 0);
|
|
156
|
-
this.buffers.set(options.name, buffer);
|
|
157
|
-
log$1.info(`Added log file watcher: ${options.name} -> ${options.filePath}`);
|
|
158
|
-
}
|
|
159
|
-
removeLogFile(name) {
|
|
160
|
-
const buffer = this.buffers.get(name);
|
|
161
|
-
if (buffer) {
|
|
162
|
-
buffer.stop();
|
|
163
|
-
this.buffers.delete(name);
|
|
164
|
-
log$1.info(`Removed log file watcher: ${name}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
getBuffer(name) {
|
|
168
|
-
return this.buffers.get(name);
|
|
169
|
-
}
|
|
170
|
-
getAllBuffers() {
|
|
171
|
-
return this.buffers;
|
|
172
|
-
}
|
|
173
|
-
stopAll() {
|
|
174
|
-
for (const [name, buffer] of this.buffers) {
|
|
175
|
-
buffer.stop();
|
|
176
|
-
log$1.debug(`Stopped log file watcher: ${name}`);
|
|
177
|
-
}
|
|
178
|
-
this.buffers.clear();
|
|
125
|
+
function resolvePath(filePath, projectRoot) {
|
|
126
|
+
if (path.isAbsolute(filePath)) {
|
|
127
|
+
return filePath;
|
|
179
128
|
}
|
|
180
|
-
|
|
181
|
-
return
|
|
129
|
+
if (projectRoot) {
|
|
130
|
+
return path.resolve(projectRoot, filePath);
|
|
182
131
|
}
|
|
183
|
-
|
|
184
|
-
let globalWatcher = null;
|
|
185
|
-
function getServiceLogWatcher() {
|
|
186
|
-
if (!globalWatcher) {
|
|
187
|
-
globalWatcher = new ServiceLogWatcher();
|
|
188
|
-
}
|
|
189
|
-
return globalWatcher;
|
|
132
|
+
return path.resolve(process.cwd(), filePath);
|
|
190
133
|
}
|
|
191
134
|
const log = createLogger("ServiceLogsPlugin");
|
|
192
135
|
const ServiceLogsPlugin = async () => {
|
|
@@ -209,17 +152,6 @@ const ServiceLogsPlugin = async () => {
|
|
|
209
152
|
log.debug("No log files configured, plugin will not register any tools");
|
|
210
153
|
return {};
|
|
211
154
|
}
|
|
212
|
-
const watcher = getServiceLogWatcher();
|
|
213
|
-
watcher.setProjectRoot(process.cwd());
|
|
214
|
-
for (const logFileConfig of logFiles) {
|
|
215
|
-
watcher.addLogFile({
|
|
216
|
-
name: logFileConfig.name,
|
|
217
|
-
filePath: logFileConfig.path,
|
|
218
|
-
maxBufferSize: logFileConfig.maxBufferSize,
|
|
219
|
-
watchExisting: logFileConfig.watchExisting
|
|
220
|
-
});
|
|
221
|
-
log.debug(`Added log file watcher: ${logFileConfig.name} -> ${logFileConfig.path}`);
|
|
222
|
-
}
|
|
223
155
|
const tools = {};
|
|
224
156
|
for (const logFileConfig of logFiles) {
|
|
225
157
|
const toolName = `get_${logFileConfig.name}_logs`;
|
|
@@ -230,7 +162,7 @@ ${logFileConfig.description}
|
|
|
230
162
|
|
|
231
163
|
**日志内容**:
|
|
232
164
|
- 来自日志文件 ${logFileConfig.path} 的实时日志
|
|
233
|
-
-
|
|
165
|
+
- 默认返回最近 200 行日志`;
|
|
234
166
|
const getLogsTool = tool({
|
|
235
167
|
description,
|
|
236
168
|
args: {
|
|
@@ -247,28 +179,28 @@ ${logFileConfig.description}
|
|
|
247
179
|
sessionID: context.sessionID,
|
|
248
180
|
directory: context.directory
|
|
249
181
|
});
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
182
|
+
const entries = await readLogFileTail({
|
|
183
|
+
name: logFileConfig.name,
|
|
184
|
+
filePath: logFileConfig.path,
|
|
185
|
+
projectRoot: process.cwd(),
|
|
186
|
+
lines: limit ? Math.max(limit, 200) : 200,
|
|
255
187
|
level: level ? level.split(",").map((l) => l.trim()) : void 0,
|
|
256
|
-
limit,
|
|
257
188
|
since
|
|
258
189
|
});
|
|
259
|
-
|
|
260
|
-
|
|
190
|
+
const filteredEntries = entries.slice(0, limit ?? 50);
|
|
191
|
+
if (filteredEntries.length === 0) {
|
|
192
|
+
return `当前没有符合条件的日志。
|
|
261
193
|
|
|
262
194
|
建议:
|
|
263
195
|
- 不指定参数获取所有日志
|
|
264
196
|
- 使用 level=error,warn 获取错误和警告`;
|
|
265
197
|
}
|
|
266
|
-
const formattedLogs =
|
|
198
|
+
const formattedLogs = filteredEntries.map((entry) => {
|
|
267
199
|
const time = new Date(entry.timestamp).toLocaleTimeString();
|
|
268
200
|
const levelIcon = entry.level === "error" ? "❌" : entry.level === "warn" ? "⚠️" : "ℹ️";
|
|
269
201
|
return `${time} ${levelIcon} ${entry.message}`;
|
|
270
202
|
}).join("\n");
|
|
271
|
-
return `${logFileConfig.name} 日志(${
|
|
203
|
+
return `${logFileConfig.name} 日志(${filteredEntries.length} 条):
|
|
272
204
|
|
|
273
205
|
${formattedLogs}`;
|
|
274
206
|
}
|
|
@@ -19,10 +19,9 @@ var __async = (__this, __arguments, generator) => {
|
|
|
19
19
|
});
|
|
20
20
|
};
|
|
21
21
|
import { tool } from "@opencode-ai/plugin";
|
|
22
|
-
import {
|
|
22
|
+
import { readLogFileTail, createLogger } from "@vite-plugin-opencode-assistant/shared";
|
|
23
23
|
const log = createLogger("ServiceLogsPlugin");
|
|
24
24
|
const ServiceLogsPlugin = () => __async(null, null, function* () {
|
|
25
|
-
var _a;
|
|
26
25
|
log.debug("ServiceLogsPlugin loading...");
|
|
27
26
|
const logFilesJson = process.env.OPENCODE_LOG_FILES_JSON;
|
|
28
27
|
log.debug("Log files JSON from env:", { logFilesJson: logFilesJson ? "set" : "not set" });
|
|
@@ -42,20 +41,9 @@ const ServiceLogsPlugin = () => __async(null, null, function* () {
|
|
|
42
41
|
log.debug("No log files configured, plugin will not register any tools");
|
|
43
42
|
return {};
|
|
44
43
|
}
|
|
45
|
-
const watcher = getServiceLogWatcher();
|
|
46
|
-
watcher.setProjectRoot(process.cwd());
|
|
47
|
-
for (const logFileConfig of logFiles) {
|
|
48
|
-
watcher.addLogFile({
|
|
49
|
-
name: logFileConfig.name,
|
|
50
|
-
filePath: logFileConfig.path,
|
|
51
|
-
maxBufferSize: logFileConfig.maxBufferSize,
|
|
52
|
-
watchExisting: logFileConfig.watchExisting
|
|
53
|
-
});
|
|
54
|
-
log.debug(`Added log file watcher: ${logFileConfig.name} -> ${logFileConfig.path}`);
|
|
55
|
-
}
|
|
56
44
|
const tools = {};
|
|
57
45
|
for (const logFileConfig of logFiles) {
|
|
58
|
-
let
|
|
46
|
+
let _a;
|
|
59
47
|
const toolName = `get_${logFileConfig.name}_logs`;
|
|
60
48
|
const description = `\u83B7\u53D6 ${logFileConfig.name} \u7684\u65E5\u5FD7\u3002
|
|
61
49
|
|
|
@@ -64,7 +52,7 @@ ${logFileConfig.description}
|
|
|
64
52
|
|
|
65
53
|
**\u65E5\u5FD7\u5185\u5BB9**\uFF1A
|
|
66
54
|
- \u6765\u81EA\u65E5\u5FD7\u6587\u4EF6 ${logFileConfig.path} \u7684\u5B9E\u65F6\u65E5\u5FD7
|
|
67
|
-
- \
|
|
55
|
+
- \u9ED8\u8BA4\u8FD4\u56DE\u6700\u8FD1 200 \u884C\u65E5\u5FD7`;
|
|
68
56
|
const getLogsTool = tool({
|
|
69
57
|
description,
|
|
70
58
|
args: {
|
|
@@ -82,28 +70,28 @@ ${logFileConfig.description}
|
|
|
82
70
|
sessionID: context.sessionID,
|
|
83
71
|
directory: context.directory
|
|
84
72
|
});
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
73
|
+
const entries = yield readLogFileTail({
|
|
74
|
+
name: logFileConfig.name,
|
|
75
|
+
filePath: logFileConfig.path,
|
|
76
|
+
projectRoot: process.cwd(),
|
|
77
|
+
lines: limit ? Math.max(limit, 200) : 200,
|
|
90
78
|
level: level ? level.split(",").map((l) => l.trim()) : void 0,
|
|
91
|
-
limit,
|
|
92
79
|
since
|
|
93
80
|
});
|
|
94
|
-
|
|
95
|
-
|
|
81
|
+
const filteredEntries = entries.slice(0, limit != null ? limit : 50);
|
|
82
|
+
if (filteredEntries.length === 0) {
|
|
83
|
+
return `\u5F53\u524D\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u65E5\u5FD7\u3002
|
|
96
84
|
|
|
97
85
|
\u5EFA\u8BAE\uFF1A
|
|
98
86
|
- \u4E0D\u6307\u5B9A\u53C2\u6570\u83B7\u53D6\u6240\u6709\u65E5\u5FD7
|
|
99
87
|
- \u4F7F\u7528 level=error,warn \u83B7\u53D6\u9519\u8BEF\u548C\u8B66\u544A`;
|
|
100
88
|
}
|
|
101
|
-
const formattedLogs =
|
|
89
|
+
const formattedLogs = filteredEntries.map((entry) => {
|
|
102
90
|
const time = new Date(entry.timestamp).toLocaleTimeString();
|
|
103
91
|
const levelIcon = entry.level === "error" ? "\u274C" : entry.level === "warn" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
|
|
104
92
|
return `${time} ${levelIcon} ${entry.message}`;
|
|
105
93
|
}).join("\n");
|
|
106
|
-
return `${logFileConfig.name} \u65E5\u5FD7\uFF08${
|
|
94
|
+
return `${logFileConfig.name} \u65E5\u5FD7\uFF08${filteredEntries.length} \u6761\uFF09\uFF1A
|
|
107
95
|
|
|
108
96
|
${formattedLogs}`;
|
|
109
97
|
});
|
|
@@ -45,7 +45,6 @@ var import_plugin = require("@opencode-ai/plugin");
|
|
|
45
45
|
var import_shared = require("@vite-plugin-opencode-assistant/shared");
|
|
46
46
|
const log = (0, import_shared.createLogger)("ServiceLogsPlugin");
|
|
47
47
|
const ServiceLogsPlugin = () => __async(null, null, function* () {
|
|
48
|
-
var _a;
|
|
49
48
|
log.debug("ServiceLogsPlugin loading...");
|
|
50
49
|
const logFilesJson = process.env.OPENCODE_LOG_FILES_JSON;
|
|
51
50
|
log.debug("Log files JSON from env:", { logFilesJson: logFilesJson ? "set" : "not set" });
|
|
@@ -65,20 +64,9 @@ const ServiceLogsPlugin = () => __async(null, null, function* () {
|
|
|
65
64
|
log.debug("No log files configured, plugin will not register any tools");
|
|
66
65
|
return {};
|
|
67
66
|
}
|
|
68
|
-
const watcher = (0, import_shared.getServiceLogWatcher)();
|
|
69
|
-
watcher.setProjectRoot(process.cwd());
|
|
70
|
-
for (const logFileConfig of logFiles) {
|
|
71
|
-
watcher.addLogFile({
|
|
72
|
-
name: logFileConfig.name,
|
|
73
|
-
filePath: logFileConfig.path,
|
|
74
|
-
maxBufferSize: logFileConfig.maxBufferSize,
|
|
75
|
-
watchExisting: logFileConfig.watchExisting
|
|
76
|
-
});
|
|
77
|
-
log.debug(`Added log file watcher: ${logFileConfig.name} -> ${logFileConfig.path}`);
|
|
78
|
-
}
|
|
79
67
|
const tools = {};
|
|
80
68
|
for (const logFileConfig of logFiles) {
|
|
81
|
-
let
|
|
69
|
+
let _a;
|
|
82
70
|
const toolName = `get_${logFileConfig.name}_logs`;
|
|
83
71
|
const description = `\u83B7\u53D6 ${logFileConfig.name} \u7684\u65E5\u5FD7\u3002
|
|
84
72
|
|
|
@@ -87,7 +75,7 @@ ${logFileConfig.description}
|
|
|
87
75
|
|
|
88
76
|
**\u65E5\u5FD7\u5185\u5BB9**\uFF1A
|
|
89
77
|
- \u6765\u81EA\u65E5\u5FD7\u6587\u4EF6 ${logFileConfig.path} \u7684\u5B9E\u65F6\u65E5\u5FD7
|
|
90
|
-
- \
|
|
78
|
+
- \u9ED8\u8BA4\u8FD4\u56DE\u6700\u8FD1 200 \u884C\u65E5\u5FD7`;
|
|
91
79
|
const getLogsTool = (0, import_plugin.tool)({
|
|
92
80
|
description,
|
|
93
81
|
args: {
|
|
@@ -105,28 +93,28 @@ ${logFileConfig.description}
|
|
|
105
93
|
sessionID: context.sessionID,
|
|
106
94
|
directory: context.directory
|
|
107
95
|
});
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
96
|
+
const entries = yield (0, import_shared.readLogFileTail)({
|
|
97
|
+
name: logFileConfig.name,
|
|
98
|
+
filePath: logFileConfig.path,
|
|
99
|
+
projectRoot: process.cwd(),
|
|
100
|
+
lines: limit ? Math.max(limit, 200) : 200,
|
|
113
101
|
level: level ? level.split(",").map((l) => l.trim()) : void 0,
|
|
114
|
-
limit,
|
|
115
102
|
since
|
|
116
103
|
});
|
|
117
|
-
|
|
118
|
-
|
|
104
|
+
const filteredEntries = entries.slice(0, limit != null ? limit : 50);
|
|
105
|
+
if (filteredEntries.length === 0) {
|
|
106
|
+
return `\u5F53\u524D\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u65E5\u5FD7\u3002
|
|
119
107
|
|
|
120
108
|
\u5EFA\u8BAE\uFF1A
|
|
121
109
|
- \u4E0D\u6307\u5B9A\u53C2\u6570\u83B7\u53D6\u6240\u6709\u65E5\u5FD7
|
|
122
110
|
- \u4F7F\u7528 level=error,warn \u83B7\u53D6\u9519\u8BEF\u548C\u8B66\u544A`;
|
|
123
111
|
}
|
|
124
|
-
const formattedLogs =
|
|
112
|
+
const formattedLogs = filteredEntries.map((entry) => {
|
|
125
113
|
const time = new Date(entry.timestamp).toLocaleTimeString();
|
|
126
114
|
const levelIcon = entry.level === "error" ? "\u274C" : entry.level === "warn" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
|
|
127
115
|
return `${time} ${levelIcon} ${entry.message}`;
|
|
128
116
|
}).join("\n");
|
|
129
|
-
return `${logFileConfig.name} \u65E5\u5FD7\uFF08${
|
|
117
|
+
return `${logFileConfig.name} \u65E5\u5FD7\uFF08${filteredEntries.length} \u6761\uFF09\uFF1A
|
|
130
118
|
|
|
131
119
|
${formattedLogs}`;
|
|
132
120
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vite-plugin-opencode-assistant/opencode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.60",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "lib/index.cjs",
|
|
6
6
|
"module": "es/index.mjs",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"execa": "^9.6.1",
|
|
25
|
-
"@vite-plugin-opencode-assistant/shared": "1.0.
|
|
25
|
+
"@vite-plugin-opencode-assistant/shared": "1.0.60"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@opencode-ai/plugin": "^1.3.15",
|