@vite-plugin-opencode-assistant/opencode 1.0.58 → 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.
@@ -1,212 +1,219 @@
1
- import { t as h } from "./tool.js";
2
- import { c as v } from "./logger.js";
3
- const o = {};
4
- var y = Object.defineProperty, E = (s, e, t) => e in s ? y(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t, n = (s, e, t) => E(s, typeof e != "symbol" ? e + "" : e, t);
5
- const a = v("FileLogWatcher");
6
- class F {
7
- constructor(e) {
8
- n(this, "buffer", []), n(this, "maxSize"), n(this, "name"), n(this, "filePath"), n(this, "lastPosition", 0), n(this, "watcher", null), n(this, "enabled", !1);
9
- var t;
10
- this.name = e.name, this.filePath = e.filePath, this.maxSize = (t = e.maxBufferSize) != null ? t : 200, this.enabled = !0;
11
- }
12
- start(e) {
13
- const t = this.resolvePath(e);
14
- if (!o.existsSync(t)) {
15
- a.debug(`Log file does not exist: ${t}`);
16
- return;
17
- }
18
- const i = o.statSync(t);
19
- this.lastPosition = i.size, this.watcher = o.watch(t, (r) => {
20
- r === "change" && this.readNewLogs(t);
21
- }), this.watcher.on("error", (r) => {
22
- a.error(`Error watching file ${t}`, { error: r });
23
- }), a.info(`Started watching log file: ${t}`);
24
- }
25
- stop() {
26
- this.watcher && (this.watcher.close(), this.watcher = null, a.debug(`Stopped watching log file: ${this.filePath}`));
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 __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);
12
+ }
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";
27
30
  }
28
- resolvePath(e) {
29
- return o.isAbsolute(this.filePath) ? this.filePath : e ? o.resolve(e, this.filePath) : o.resolve(process.cwd(), this.filePath);
31
+ if (lowerLine.includes("warn") || lowerLine.includes("warning")) {
32
+ return "warn";
30
33
  }
31
- readNewLogs(e) {
32
- try {
33
- const t = o.statSync(e);
34
- if (t.size <= this.lastPosition)
35
- return;
36
- const i = o.openSync(e, "r"), r = Buffer.alloc(t.size - this.lastPosition);
37
- o.readSync(i, r, 0, r.length, this.lastPosition), o.closeSync(i), this.lastPosition = t.size;
38
- const f = r.toString("utf-8").trim();
39
- f && this.processLogContent(f);
40
- } catch (t) {
41
- a.error(`Error reading log file ${e}`, { error: t });
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
+ }
42
50
  }
43
51
  }
44
- processLogContent(e) {
45
- const t = e.split(`
46
- `);
47
- for (const i of t)
48
- i.trim() && this.addEntry({
49
- level: this.detectLogLevel(i),
50
- message: i,
51
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
52
- source: `file:${this.name}`
53
- });
54
- }
55
- detectLogLevel(e) {
56
- const t = e.toLowerCase();
57
- return t.includes("error") || t.includes("err") || t.includes("fatal") ? "error" : t.includes("warn") || t.includes("warning") ? "warn" : "info";
58
- }
59
- addEntry(e) {
60
- this.enabled && (this.buffer.length >= this.maxSize && this.buffer.shift(), this.buffer.push(e));
61
- }
62
- getLogs(e = {}) {
63
- let t = [...this.buffer];
64
- if (e.level) {
65
- const i = Array.isArray(e.level) ? e.level : [e.level];
66
- t = t.filter((r) => i.includes(r.level));
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 [];
67
61
  }
68
- if (e.since) {
69
- const i = new Date(e.since);
70
- t = t.filter((r) => new Date(r.timestamp) >= i);
62
+ try {
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
+ }
91
+ }
92
+ fs.closeSync(fd);
93
+ const content = buffer.toString("utf-8").trim();
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);
117
+ }
118
+ return entries;
119
+ } catch (err) {
120
+ log$1.error(`Error reading log file ${resolvedPath}`, { error: err });
121
+ return [];
71
122
  }
72
- return e.limit && e.limit > 0 && (t = t.slice(-e.limit)), t;
73
- }
74
- clear() {
75
- this.buffer = [];
76
- }
77
- size() {
78
- return this.buffer.length;
79
- }
80
- setEnabled(e) {
81
- this.enabled = e;
82
- }
83
- isEnabled() {
84
- return this.enabled;
85
- }
86
- getName() {
87
- return this.name;
88
- }
89
- getFilePath() {
90
- return this.filePath;
91
- }
123
+ });
92
124
  }
93
- class z {
94
- constructor() {
95
- n(this, "buffers", /* @__PURE__ */ new Map()), n(this, "projectRoot", null);
96
- }
97
- setProjectRoot(e) {
98
- this.projectRoot = e;
125
+ function resolvePath(filePath, projectRoot) {
126
+ if (path.isAbsolute(filePath)) {
127
+ return filePath;
99
128
  }
100
- addLogFile(e) {
101
- var t;
102
- if (this.buffers.has(e.name)) {
103
- a.warn(`Log file "${e.name}" already exists, skipping`);
104
- return;
105
- }
106
- const i = new F(e);
107
- i.start((t = this.projectRoot) != null ? t : void 0), this.buffers.set(e.name, i), a.info(`Added log file watcher: ${e.name} -> ${e.filePath}`);
108
- }
109
- removeLogFile(e) {
110
- const t = this.buffers.get(e);
111
- t && (t.stop(), this.buffers.delete(e), a.info(`Removed log file watcher: ${e}`));
112
- }
113
- getBuffer(e) {
114
- return this.buffers.get(e);
115
- }
116
- getAllBuffers() {
117
- return this.buffers;
118
- }
119
- stopAll() {
120
- for (const [e, t] of this.buffers)
121
- t.stop(), a.debug(`Stopped log file watcher: ${e}`);
122
- this.buffers.clear();
123
- }
124
- getLogFileNames() {
125
- return Array.from(this.buffers.keys());
129
+ if (projectRoot) {
130
+ return path.resolve(projectRoot, filePath);
126
131
  }
132
+ return path.resolve(process.cwd(), filePath);
127
133
  }
128
- let d = null;
129
- function N() {
130
- return d || (d = new z()), d;
131
- }
132
- const l = v("ServiceLogsPlugin"), B = async () => {
133
- l.debug("ServiceLogsPlugin loading...");
134
- const s = process.env.OPENCODE_LOG_FILES_JSON;
135
- if (l.debug("Log files JSON from env:", { logFilesJson: s ? "set" : "not set" }), !s)
136
- return l.debug("OPENCODE_LOG_FILES_JSON is not set, service logs plugin will not register tools"), {};
137
- let e;
134
+ const log = createLogger("ServiceLogsPlugin");
135
+ const ServiceLogsPlugin = async () => {
136
+ log.debug("ServiceLogsPlugin loading...");
137
+ const logFilesJson = process.env.OPENCODE_LOG_FILES_JSON;
138
+ log.debug("Log files JSON from env:", { logFilesJson: logFilesJson ? "set" : "not set" });
139
+ if (!logFilesJson) {
140
+ log.debug("OPENCODE_LOG_FILES_JSON is not set, service logs plugin will not register tools");
141
+ return {};
142
+ }
143
+ let logFiles;
138
144
  try {
139
- e = JSON.parse(s), l.debug("Parsed log files config", { count: e.length });
140
- } catch (r) {
141
- return l.error("Failed to parse OPENCODE_LOG_FILES_JSON", { error: r }), {};
142
- }
143
- if (!e || e.length === 0)
144
- return l.debug("No log files configured, plugin will not register any tools"), {};
145
- const t = N();
146
- t.setProjectRoot(process.cwd());
147
- for (const r of e)
148
- t.addLogFile({
149
- name: r.name,
150
- filePath: r.path,
151
- maxBufferSize: r.maxBufferSize,
152
- watchExisting: r.watchExisting
153
- }), l.debug(`Added log file watcher: ${r.name} -> ${r.path}`);
154
- const i = {};
155
- for (const r of e) {
156
- const f = `get_${r.name}_logs`, L = `获取 ${r.name} 的日志。
145
+ logFiles = JSON.parse(logFilesJson);
146
+ log.debug("Parsed log files config", { count: logFiles.length });
147
+ } catch (e) {
148
+ log.error("Failed to parse OPENCODE_LOG_FILES_JSON", { error: e });
149
+ return {};
150
+ }
151
+ if (!logFiles || logFiles.length === 0) {
152
+ log.debug("No log files configured, plugin will not register any tools");
153
+ return {};
154
+ }
155
+ const tools = {};
156
+ for (const logFileConfig of logFiles) {
157
+ const toolName = `get_${logFileConfig.name}_logs`;
158
+ const description = `获取 ${logFileConfig.name} 的日志。
157
159
 
158
160
  **何时使用此工具**:
159
- ${r.description}
161
+ ${logFileConfig.description}
160
162
 
161
163
  **日志内容**:
162
- - 来自日志文件 ${r.path} 的实时日志
163
- - 最多保留 ${r.maxBufferSize ?? 200} 条日志`, S = h({
164
- description: L,
164
+ - 来自日志文件 ${logFileConfig.path} 的实时日志
165
+ - 默认返回最近 200 行日志`;
166
+ const getLogsTool = tool({
167
+ description,
165
168
  args: {
166
- level: h.schema.string().optional().describe(
169
+ level: tool.schema.string().optional().describe(
167
170
  "日志级别过滤:error(错误)、warn(警告)、info(信息)。多个用逗号分隔,如 'error,warn'"
168
171
  ),
169
- limit: h.schema.number().int().min(1).max(200).optional().default(50).describe("返回条数,默认 50,最大 200"),
170
- since: h.schema.string().optional().describe("起始时间(ISO 格式),获取此时间之后的日志")
172
+ limit: tool.schema.number().int().min(1).max(200).optional().default(50).describe("返回条数,默认 50,最大 200"),
173
+ since: tool.schema.string().optional().describe("起始时间(ISO 格式),获取此时间之后的日志")
171
174
  },
172
- async execute(m, b) {
173
- const { level: w, limit: p, since: P } = m;
174
- l.debug(`${f} called`, {
175
- args: m,
176
- sessionID: b.sessionID,
177
- directory: b.directory
175
+ async execute(args, context) {
176
+ const { level, limit, since } = args;
177
+ log.debug(`${toolName} called`, {
178
+ args,
179
+ sessionID: context.sessionID,
180
+ directory: context.directory
178
181
  });
179
- const g = t.getBuffer(r.name);
180
- if (!g)
181
- return `日志文件 "${r.name}" 未找到。请检查配置。`;
182
- const u = g.getLogs({
183
- level: w ? w.split(",").map((c) => c.trim()) : void 0,
184
- limit: p,
185
- since: P
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,
187
+ level: level ? level.split(",").map((l) => l.trim()) : void 0,
188
+ since
186
189
  });
187
- if (u.length === 0)
188
- return `当前没有符合条件的日志(缓冲区共 ${g.size()} 条)。
190
+ const filteredEntries = entries.slice(0, limit ?? 50);
191
+ if (filteredEntries.length === 0) {
192
+ return `当前没有符合条件的日志。
189
193
 
190
194
  建议:
191
195
  - 不指定参数获取所有日志
192
196
  - 使用 level=error,warn 获取错误和警告`;
193
- const $ = u.map((c) => {
194
- const _ = new Date(c.timestamp).toLocaleTimeString(), O = c.level === "error" ? "❌" : c.level === "warn" ? "⚠️" : "ℹ️";
195
- return `${_} ${O} ${c.message}`;
196
- }).join(`
197
- `);
198
- return `${r.name} 日志(${u.length}/${g.size()} 条):
197
+ }
198
+ const formattedLogs = filteredEntries.map((entry) => {
199
+ const time = new Date(entry.timestamp).toLocaleTimeString();
200
+ const levelIcon = entry.level === "error" ? "❌" : entry.level === "warn" ? "⚠️" : "ℹ️";
201
+ return `${time} ${levelIcon} ${entry.message}`;
202
+ }).join("\n");
203
+ return `${logFileConfig.name} 日志(${filteredEntries.length} 条):
199
204
 
200
- ${$}`;
205
+ ${formattedLogs}`;
201
206
  }
202
207
  });
203
- i[f] = S, l.debug(`Registered tool: ${f}`);
208
+ tools[toolName] = getLogsTool;
209
+ log.debug(`Registered tool: ${toolName}`);
204
210
  }
205
- return l.debug(`Plugin initialized with ${Object.keys(i).length} log tools`), {
206
- tool: i
211
+ log.debug(`Plugin initialized with ${Object.keys(tools).length} log tools`);
212
+ return {
213
+ tool: tools
207
214
  };
208
215
  };
209
216
  export {
210
- B as ServiceLogsPlugin,
211
- B as default
217
+ ServiceLogsPlugin,
218
+ ServiceLogsPlugin as default
212
219
  };
@@ -19,10 +19,9 @@ var __async = (__this, __arguments, generator) => {
19
19
  });
20
20
  };
21
21
  import { tool } from "@opencode-ai/plugin";
22
- import { createLogger, getServiceLogWatcher } from "@vite-plugin-opencode-assistant/shared";
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 _b;
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
- - \u6700\u591A\u4FDD\u7559 ${(_a = logFileConfig.maxBufferSize) != null ? _a : 200} \u6761\u65E5\u5FD7`;
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 buffer = watcher.getBuffer(logFileConfig.name);
86
- if (!buffer) {
87
- return `\u65E5\u5FD7\u6587\u4EF6 "${logFileConfig.name}" \u672A\u627E\u5230\u3002\u8BF7\u68C0\u67E5\u914D\u7F6E\u3002`;
88
- }
89
- const logs = buffer.getLogs({
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
- if (logs.length === 0) {
95
- return `\u5F53\u524D\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u65E5\u5FD7\uFF08\u7F13\u51B2\u533A\u5171 ${buffer.size()} \u6761\uFF09\u3002
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 = logs.map((entry) => {
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${logs.length}/${buffer.size()} \u6761\uFF09\uFF1A
94
+ return `${logFileConfig.name} \u65E5\u5FD7\uFF08${filteredEntries.length} \u6761\uFF09\uFF1A
107
95
 
108
96
  ${formattedLogs}`;
109
97
  });
@@ -1,12 +1,17 @@
1
- import { t as n } from "./tool.js";
2
- import { c as p } from "./logger.js";
3
- const e = p("OpenCodePluginViteLogs"), b = async () => {
4
- e.info("ViteLogsPlugin loading...");
5
- const l = process.env.OPENCODE_VITE_LOGS_API_URL;
6
- return e.debug("Vite Logs API URL:", { logsApiUrl: l }), l ? (e.info("Plugin initialized successfully"), {
7
- tool: {
8
- get_vite_dev_logs: n({
9
- description: `获取 Vite 开发服务器的运行日志。
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
- args: {
26
- level: n.schema.string().optional().describe(
27
- "日志级别过滤:error(错误)、warn(警告)、info(信息)、debug(调试)、log(普通)。多个用逗号分隔,如 'error,warn'"
28
- ),
29
- limit: n.schema.number().int().min(1).max(200).optional().default(50).describe("返回条数,默认 50,最大 200"),
30
- source: n.schema.string().optional().describe("来源过滤:console(控制台)、opencode-stdout(服务输出)、opencode-stderr(服务错误)")
31
- },
32
- async execute(a, i) {
33
- const { level: c, limit: g, source: u } = a;
34
- e.debug("get_vite_dev_logs called", {
35
- args: a,
36
- sessionID: i.sessionID,
37
- directory: i.directory
38
- });
39
- try {
40
- const r = new URL(l);
41
- c && r.searchParams.set("level", c), g && r.searchParams.set("limit", String(g)), u && r.searchParams.set("source", u), e.debug("Fetching logs from", { url: r.toString() });
42
- const t = await fetch(r.toString(), {
43
- method: "GET",
44
- headers: { Accept: "application/json" },
45
- signal: i.abort
46
- });
47
- if (!t.ok) {
48
- const o = await t.text();
49
- return e.error("Failed to fetch logs", { status: t.status, error: o }), `获取日志失败: HTTP ${t.status} - ${o}`;
50
- }
51
- const s = await t.json();
52
- if (e.debug("Logs fetched successfully", {
53
- count: s.logs.length,
54
- total: s.meta.total
55
- }), s.logs.length === 0)
56
- return `当前没有符合条件的日志(缓冲区共 ${s.meta.total} 条)。
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
- const d = s.logs.map((o) => {
62
- const m = new Date(o.timestamp).toLocaleTimeString(), f = o.level === "error" ? "❌" : o.level === "warn" ? "⚠️" : o.level === "info" ? "ℹ️" : "";
63
- return `${m} ${f} ${o.message}`;
64
- }).join(`
65
- `);
66
- return `Vite 开发服务器日志(${s.meta.returned}/${s.meta.total} 条):
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
- ${d}`;
69
- } catch (r) {
70
- const t = r;
71
- return i.abort.aborted ? (e.debug("Request aborted"), "请求已取消") : (e.error("Error fetching vite logs", { error: t }), `获取日志时发生错误: ${t.message}`);
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
- }) : (e.warn("OPENCODE_VITE_LOGS_API_URL is not set, vite logs plugin will not work"), {});
95
+ };
77
96
  };
78
97
  export {
79
- b as ViteLogsPlugin,
80
- b as default
98
+ ViteLogsPlugin,
99
+ ViteLogsPlugin as default
81
100
  };