@vite-plugin-opencode-assistant/opencode 1.0.58 → 1.0.59
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/logger.js +211 -108
- package/es/plugins/page-context.js +33 -22
- package/es/plugins/service-logs.js +209 -134
- package/es/plugins/vite-logs.js +75 -56
- package/package.json +2 -2
- package/es/plugins/tool.js +0 -9389
|
@@ -1,75 +1,122 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { c as createLogger } from "./logger.js";
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
7
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
8
|
+
const log$1 = createLogger("FileLogWatcher");
|
|
9
|
+
class FileLogBuffer {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
__publicField(this, "buffer", []);
|
|
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}`);
|
|
16
28
|
return;
|
|
17
29
|
}
|
|
18
|
-
const
|
|
19
|
-
this.lastPosition =
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
this.watcher.on("error", (err) => {
|
|
38
|
+
log$1.error(`Error watching file ${resolvedPath}`, { error: err });
|
|
39
|
+
});
|
|
40
|
+
log$1.info(`Started watching log file: ${resolvedPath}`);
|
|
24
41
|
}
|
|
25
42
|
stop() {
|
|
26
|
-
|
|
43
|
+
if (this.watcher) {
|
|
44
|
+
this.watcher.close();
|
|
45
|
+
this.watcher = null;
|
|
46
|
+
log$1.debug(`Stopped watching log file: ${this.filePath}`);
|
|
47
|
+
}
|
|
27
48
|
}
|
|
28
|
-
resolvePath(
|
|
29
|
-
|
|
49
|
+
resolvePath(projectRoot) {
|
|
50
|
+
if (path.isAbsolute(this.filePath)) {
|
|
51
|
+
return this.filePath;
|
|
52
|
+
}
|
|
53
|
+
if (projectRoot) {
|
|
54
|
+
return path.resolve(projectRoot, this.filePath);
|
|
55
|
+
}
|
|
56
|
+
return path.resolve(process.cwd(), this.filePath);
|
|
30
57
|
}
|
|
31
|
-
readNewLogs(
|
|
58
|
+
readNewLogs(filePath) {
|
|
32
59
|
try {
|
|
33
|
-
const
|
|
34
|
-
if (
|
|
60
|
+
const stat = fs.statSync(filePath);
|
|
61
|
+
if (stat.size <= this.lastPosition) {
|
|
35
62
|
return;
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
63
|
+
}
|
|
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
|
+
fs.closeSync(fd);
|
|
68
|
+
this.lastPosition = stat.size;
|
|
69
|
+
const content = buffer.toString("utf-8").trim();
|
|
70
|
+
if (content) {
|
|
71
|
+
this.processLogContent(content);
|
|
72
|
+
}
|
|
73
|
+
} catch (err) {
|
|
74
|
+
log$1.error(`Error reading log file ${filePath}`, { error: err });
|
|
42
75
|
}
|
|
43
76
|
}
|
|
44
|
-
processLogContent(
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
level: this.detectLogLevel(
|
|
50
|
-
message:
|
|
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,
|
|
51
84
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
52
85
|
source: `file:${this.name}`
|
|
53
86
|
});
|
|
87
|
+
}
|
|
54
88
|
}
|
|
55
|
-
detectLogLevel(
|
|
56
|
-
const
|
|
57
|
-
|
|
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";
|
|
58
98
|
}
|
|
59
|
-
addEntry(
|
|
60
|
-
|
|
99
|
+
addEntry(entry) {
|
|
100
|
+
if (!this.enabled) return;
|
|
101
|
+
if (this.buffer.length >= this.maxSize) {
|
|
102
|
+
this.buffer.shift();
|
|
103
|
+
}
|
|
104
|
+
this.buffer.push(entry);
|
|
61
105
|
}
|
|
62
|
-
getLogs(
|
|
63
|
-
let
|
|
64
|
-
if (
|
|
65
|
-
const
|
|
66
|
-
|
|
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);
|
|
67
115
|
}
|
|
68
|
-
if (
|
|
69
|
-
|
|
70
|
-
t = t.filter((r) => new Date(r.timestamp) >= i);
|
|
116
|
+
if (options.limit && options.limit > 0) {
|
|
117
|
+
logs = logs.slice(-options.limit);
|
|
71
118
|
}
|
|
72
|
-
return
|
|
119
|
+
return logs;
|
|
73
120
|
}
|
|
74
121
|
clear() {
|
|
75
122
|
this.buffer = [];
|
|
@@ -77,8 +124,8 @@ class F {
|
|
|
77
124
|
size() {
|
|
78
125
|
return this.buffer.length;
|
|
79
126
|
}
|
|
80
|
-
setEnabled(
|
|
81
|
-
this.enabled =
|
|
127
|
+
setEnabled(enabled) {
|
|
128
|
+
this.enabled = enabled;
|
|
82
129
|
}
|
|
83
130
|
isEnabled() {
|
|
84
131
|
return this.enabled;
|
|
@@ -90,123 +137,151 @@ class F {
|
|
|
90
137
|
return this.filePath;
|
|
91
138
|
}
|
|
92
139
|
}
|
|
93
|
-
class
|
|
140
|
+
class ServiceLogWatcher {
|
|
94
141
|
constructor() {
|
|
95
|
-
|
|
142
|
+
__publicField(this, "buffers", /* @__PURE__ */ new Map());
|
|
143
|
+
__publicField(this, "projectRoot", null);
|
|
96
144
|
}
|
|
97
|
-
setProjectRoot(
|
|
98
|
-
this.projectRoot =
|
|
145
|
+
setProjectRoot(root) {
|
|
146
|
+
this.projectRoot = root;
|
|
99
147
|
}
|
|
100
|
-
addLogFile(
|
|
101
|
-
var
|
|
102
|
-
if (this.buffers.has(
|
|
103
|
-
|
|
148
|
+
addLogFile(options) {
|
|
149
|
+
var _a;
|
|
150
|
+
if (this.buffers.has(options.name)) {
|
|
151
|
+
log$1.warn(`Log file "${options.name}" already exists, skipping`);
|
|
104
152
|
return;
|
|
105
153
|
}
|
|
106
|
-
const
|
|
107
|
-
|
|
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}`);
|
|
108
158
|
}
|
|
109
|
-
removeLogFile(
|
|
110
|
-
const
|
|
111
|
-
|
|
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
|
+
}
|
|
112
166
|
}
|
|
113
|
-
getBuffer(
|
|
114
|
-
return this.buffers.get(
|
|
167
|
+
getBuffer(name) {
|
|
168
|
+
return this.buffers.get(name);
|
|
115
169
|
}
|
|
116
170
|
getAllBuffers() {
|
|
117
171
|
return this.buffers;
|
|
118
172
|
}
|
|
119
173
|
stopAll() {
|
|
120
|
-
for (const [
|
|
121
|
-
|
|
174
|
+
for (const [name, buffer] of this.buffers) {
|
|
175
|
+
buffer.stop();
|
|
176
|
+
log$1.debug(`Stopped log file watcher: ${name}`);
|
|
177
|
+
}
|
|
122
178
|
this.buffers.clear();
|
|
123
179
|
}
|
|
124
180
|
getLogFileNames() {
|
|
125
181
|
return Array.from(this.buffers.keys());
|
|
126
182
|
}
|
|
127
183
|
}
|
|
128
|
-
let
|
|
129
|
-
function
|
|
130
|
-
|
|
184
|
+
let globalWatcher = null;
|
|
185
|
+
function getServiceLogWatcher() {
|
|
186
|
+
if (!globalWatcher) {
|
|
187
|
+
globalWatcher = new ServiceLogWatcher();
|
|
188
|
+
}
|
|
189
|
+
return globalWatcher;
|
|
131
190
|
}
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
191
|
+
const log = createLogger("ServiceLogsPlugin");
|
|
192
|
+
const ServiceLogsPlugin = async () => {
|
|
193
|
+
log.debug("ServiceLogsPlugin loading...");
|
|
194
|
+
const logFilesJson = process.env.OPENCODE_LOG_FILES_JSON;
|
|
195
|
+
log.debug("Log files JSON from env:", { logFilesJson: logFilesJson ? "set" : "not set" });
|
|
196
|
+
if (!logFilesJson) {
|
|
197
|
+
log.debug("OPENCODE_LOG_FILES_JSON is not set, service logs plugin will not register tools");
|
|
198
|
+
return {};
|
|
199
|
+
}
|
|
200
|
+
let logFiles;
|
|
138
201
|
try {
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
202
|
+
logFiles = JSON.parse(logFilesJson);
|
|
203
|
+
log.debug("Parsed log files config", { count: logFiles.length });
|
|
204
|
+
} catch (e) {
|
|
205
|
+
log.error("Failed to parse OPENCODE_LOG_FILES_JSON", { error: e });
|
|
206
|
+
return {};
|
|
207
|
+
}
|
|
208
|
+
if (!logFiles || logFiles.length === 0) {
|
|
209
|
+
log.debug("No log files configured, plugin will not register any tools");
|
|
210
|
+
return {};
|
|
211
|
+
}
|
|
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
|
+
const tools = {};
|
|
224
|
+
for (const logFileConfig of logFiles) {
|
|
225
|
+
const toolName = `get_${logFileConfig.name}_logs`;
|
|
226
|
+
const description = `获取 ${logFileConfig.name} 的日志。
|
|
157
227
|
|
|
158
228
|
**何时使用此工具**:
|
|
159
|
-
${
|
|
229
|
+
${logFileConfig.description}
|
|
160
230
|
|
|
161
231
|
**日志内容**:
|
|
162
|
-
- 来自日志文件 ${
|
|
163
|
-
- 最多保留 ${
|
|
164
|
-
|
|
232
|
+
- 来自日志文件 ${logFileConfig.path} 的实时日志
|
|
233
|
+
- 最多保留 ${logFileConfig.maxBufferSize ?? 200} 条日志`;
|
|
234
|
+
const getLogsTool = tool({
|
|
235
|
+
description,
|
|
165
236
|
args: {
|
|
166
|
-
level:
|
|
237
|
+
level: tool.schema.string().optional().describe(
|
|
167
238
|
"日志级别过滤:error(错误)、warn(警告)、info(信息)。多个用逗号分隔,如 'error,warn'"
|
|
168
239
|
),
|
|
169
|
-
limit:
|
|
170
|
-
since:
|
|
240
|
+
limit: tool.schema.number().int().min(1).max(200).optional().default(50).describe("返回条数,默认 50,最大 200"),
|
|
241
|
+
since: tool.schema.string().optional().describe("起始时间(ISO 格式),获取此时间之后的日志")
|
|
171
242
|
},
|
|
172
|
-
async execute(
|
|
173
|
-
const { level
|
|
174
|
-
|
|
175
|
-
args
|
|
176
|
-
sessionID:
|
|
177
|
-
directory:
|
|
243
|
+
async execute(args, context) {
|
|
244
|
+
const { level, limit, since } = args;
|
|
245
|
+
log.debug(`${toolName} called`, {
|
|
246
|
+
args,
|
|
247
|
+
sessionID: context.sessionID,
|
|
248
|
+
directory: context.directory
|
|
178
249
|
});
|
|
179
|
-
const
|
|
180
|
-
if (!
|
|
181
|
-
return `日志文件 "${
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
250
|
+
const buffer = watcher.getBuffer(logFileConfig.name);
|
|
251
|
+
if (!buffer) {
|
|
252
|
+
return `日志文件 "${logFileConfig.name}" 未找到。请检查配置。`;
|
|
253
|
+
}
|
|
254
|
+
const logs = buffer.getLogs({
|
|
255
|
+
level: level ? level.split(",").map((l) => l.trim()) : void 0,
|
|
256
|
+
limit,
|
|
257
|
+
since
|
|
186
258
|
});
|
|
187
|
-
if (
|
|
188
|
-
return `当前没有符合条件的日志(缓冲区共 ${
|
|
259
|
+
if (logs.length === 0) {
|
|
260
|
+
return `当前没有符合条件的日志(缓冲区共 ${buffer.size()} 条)。
|
|
189
261
|
|
|
190
262
|
建议:
|
|
191
263
|
- 不指定参数获取所有日志
|
|
192
264
|
- 使用 level=error,warn 获取错误和警告`;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
265
|
+
}
|
|
266
|
+
const formattedLogs = logs.map((entry) => {
|
|
267
|
+
const time = new Date(entry.timestamp).toLocaleTimeString();
|
|
268
|
+
const levelIcon = entry.level === "error" ? "❌" : entry.level === "warn" ? "⚠️" : "ℹ️";
|
|
269
|
+
return `${time} ${levelIcon} ${entry.message}`;
|
|
270
|
+
}).join("\n");
|
|
271
|
+
return `${logFileConfig.name} 日志(${logs.length}/${buffer.size()} 条):
|
|
199
272
|
|
|
200
|
-
${
|
|
273
|
+
${formattedLogs}`;
|
|
201
274
|
}
|
|
202
275
|
});
|
|
203
|
-
|
|
276
|
+
tools[toolName] = getLogsTool;
|
|
277
|
+
log.debug(`Registered tool: ${toolName}`);
|
|
204
278
|
}
|
|
205
|
-
|
|
206
|
-
|
|
279
|
+
log.debug(`Plugin initialized with ${Object.keys(tools).length} log tools`);
|
|
280
|
+
return {
|
|
281
|
+
tool: tools
|
|
207
282
|
};
|
|
208
283
|
};
|
|
209
284
|
export {
|
|
210
|
-
|
|
211
|
-
|
|
285
|
+
ServiceLogsPlugin,
|
|
286
|
+
ServiceLogsPlugin as default
|
|
212
287
|
};
|
package/es/plugins/vite-logs.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { c as
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
import { c as createLogger } from "./logger.js";
|
|
3
|
+
const log = createLogger("OpenCodePluginViteLogs");
|
|
4
|
+
const ViteLogsPlugin = async () => {
|
|
5
|
+
log.info("ViteLogsPlugin loading...");
|
|
6
|
+
const logsApiUrl = process.env.OPENCODE_VITE_LOGS_API_URL;
|
|
7
|
+
log.debug("Vite Logs API URL:", { logsApiUrl });
|
|
8
|
+
if (!logsApiUrl) {
|
|
9
|
+
log.warn("OPENCODE_VITE_LOGS_API_URL is not set, vite logs plugin will not work");
|
|
10
|
+
return {};
|
|
11
|
+
}
|
|
12
|
+
log.info("Plugin initialized successfully");
|
|
13
|
+
const getViteDevLogsTool = tool({
|
|
14
|
+
description: `获取 Vite 开发服务器的运行日志。
|
|
10
15
|
|
|
11
16
|
**何时使用此工具**:
|
|
12
17
|
- 用户报告"页面没更新"、"热更新不工作"、"HMR 失效"时
|
|
@@ -22,60 +27,74 @@ const e = p("OpenCodePluginViteLogs"), b = async () => {
|
|
|
22
27
|
- 插件运行日志
|
|
23
28
|
|
|
24
29
|
日志保存在内存缓冲区(最近 500 条)。`,
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
30
|
+
args: {
|
|
31
|
+
level: tool.schema.string().optional().describe(
|
|
32
|
+
"日志级别过滤:error(错误)、warn(警告)、info(信息)、debug(调试)、log(普通)。多个用逗号分隔,如 'error,warn'"
|
|
33
|
+
),
|
|
34
|
+
limit: tool.schema.number().int().min(1).max(200).optional().default(50).describe("返回条数,默认 50,最大 200"),
|
|
35
|
+
source: tool.schema.string().optional().describe("来源过滤:console(控制台)、opencode-stdout(服务输出)、opencode-stderr(服务错误)")
|
|
36
|
+
},
|
|
37
|
+
async execute(args, context) {
|
|
38
|
+
const { level, limit, source } = args;
|
|
39
|
+
log.debug("get_vite_dev_logs called", {
|
|
40
|
+
args,
|
|
41
|
+
sessionID: context.sessionID,
|
|
42
|
+
directory: context.directory
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
const url = new URL(logsApiUrl);
|
|
46
|
+
if (level) url.searchParams.set("level", level);
|
|
47
|
+
if (limit) url.searchParams.set("limit", String(limit));
|
|
48
|
+
if (source) url.searchParams.set("source", source);
|
|
49
|
+
log.debug("Fetching logs from", { url: url.toString() });
|
|
50
|
+
const response = await fetch(url.toString(), {
|
|
51
|
+
method: "GET",
|
|
52
|
+
headers: { Accept: "application/json" },
|
|
53
|
+
signal: context.abort
|
|
54
|
+
});
|
|
55
|
+
if (!response.ok) {
|
|
56
|
+
const errorText = await response.text();
|
|
57
|
+
log.error("Failed to fetch logs", { status: response.status, error: errorText });
|
|
58
|
+
return `获取日志失败: HTTP ${response.status} - ${errorText}`;
|
|
59
|
+
}
|
|
60
|
+
const data = await response.json();
|
|
61
|
+
log.debug("Logs fetched successfully", {
|
|
62
|
+
count: data.logs.length,
|
|
63
|
+
total: data.meta.total
|
|
64
|
+
});
|
|
65
|
+
if (data.logs.length === 0) {
|
|
66
|
+
return `当前没有符合条件的日志(缓冲区共 ${data.meta.total} 条)。
|
|
57
67
|
|
|
58
68
|
建议:
|
|
59
69
|
- 不指定参数获取所有日志
|
|
60
70
|
- 使用 level=error,warn 获取错误和警告`;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
71
|
+
}
|
|
72
|
+
const formattedLogs = data.logs.map((entry) => {
|
|
73
|
+
const time = new Date(entry.timestamp).toLocaleTimeString();
|
|
74
|
+
const levelIcon = entry.level === "error" ? "❌" : entry.level === "warn" ? "⚠️" : entry.level === "info" ? "ℹ️" : "";
|
|
75
|
+
return `${time} ${levelIcon} ${entry.message}`;
|
|
76
|
+
}).join("\n");
|
|
77
|
+
return `Vite 开发服务器日志(${data.meta.returned}/${data.meta.total} 条):
|
|
67
78
|
|
|
68
|
-
${
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
79
|
+
${formattedLogs}`;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
const err = error;
|
|
82
|
+
if (context.abort.aborted) {
|
|
83
|
+
log.debug("Request aborted");
|
|
84
|
+
return "请求已取消";
|
|
73
85
|
}
|
|
74
|
-
|
|
86
|
+
log.error("Error fetching vite logs", { error: err });
|
|
87
|
+
return `获取日志时发生错误: ${err.message}`;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
return {
|
|
92
|
+
tool: {
|
|
93
|
+
get_vite_dev_logs: getViteDevLogsTool
|
|
75
94
|
}
|
|
76
|
-
}
|
|
95
|
+
};
|
|
77
96
|
};
|
|
78
97
|
export {
|
|
79
|
-
|
|
80
|
-
|
|
98
|
+
ViteLogsPlugin,
|
|
99
|
+
ViteLogsPlugin as default
|
|
81
100
|
};
|
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.59",
|
|
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.59"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@opencode-ai/plugin": "^1.3.15",
|