@vite-plugin-opencode-assistant/opencode 1.0.57 → 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.
@@ -0,0 +1,287 @@
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}`);
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);
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}`);
41
+ }
42
+ stop() {
43
+ if (this.watcher) {
44
+ this.watcher.close();
45
+ this.watcher = null;
46
+ log$1.debug(`Stopped watching log file: ${this.filePath}`);
47
+ }
48
+ }
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);
57
+ }
58
+ readNewLogs(filePath) {
59
+ try {
60
+ const stat = fs.statSync(filePath);
61
+ if (stat.size <= this.lastPosition) {
62
+ return;
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 });
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();
103
+ }
104
+ this.buffer.push(entry);
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
+ }
139
+ }
140
+ class ServiceLogWatcher {
141
+ constructor() {
142
+ __publicField(this, "buffers", /* @__PURE__ */ new Map());
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();
179
+ }
180
+ getLogFileNames() {
181
+ return Array.from(this.buffers.keys());
182
+ }
183
+ }
184
+ let globalWatcher = null;
185
+ function getServiceLogWatcher() {
186
+ if (!globalWatcher) {
187
+ globalWatcher = new ServiceLogWatcher();
188
+ }
189
+ return globalWatcher;
190
+ }
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;
201
+ try {
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} 的日志。
227
+
228
+ **何时使用此工具**:
229
+ ${logFileConfig.description}
230
+
231
+ **日志内容**:
232
+ - 来自日志文件 ${logFileConfig.path} 的实时日志
233
+ - 最多保留 ${logFileConfig.maxBufferSize ?? 200} 条日志`;
234
+ const getLogsTool = tool({
235
+ description,
236
+ args: {
237
+ level: tool.schema.string().optional().describe(
238
+ "日志级别过滤:error(错误)、warn(警告)、info(信息)。多个用逗号分隔,如 'error,warn'"
239
+ ),
240
+ limit: tool.schema.number().int().min(1).max(200).optional().default(50).describe("返回条数,默认 50,最大 200"),
241
+ since: tool.schema.string().optional().describe("起始时间(ISO 格式),获取此时间之后的日志")
242
+ },
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
249
+ });
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
258
+ });
259
+ if (logs.length === 0) {
260
+ return `当前没有符合条件的日志(缓冲区共 ${buffer.size()} 条)。
261
+
262
+ 建议:
263
+ - 不指定参数获取所有日志
264
+ - 使用 level=error,warn 获取错误和警告`;
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()} 条):
272
+
273
+ ${formattedLogs}`;
274
+ }
275
+ });
276
+ tools[toolName] = getLogsTool;
277
+ log.debug(`Registered tool: ${toolName}`);
278
+ }
279
+ log.debug(`Plugin initialized with ${Object.keys(tools).length} log tools`);
280
+ return {
281
+ tool: tools
282
+ };
283
+ };
284
+ export {
285
+ ServiceLogsPlugin,
286
+ ServiceLogsPlugin as default
287
+ };
@@ -0,0 +1,124 @@
1
+ var __async = (__this, __arguments, generator) => {
2
+ return new Promise((resolve, reject) => {
3
+ var fulfilled = (value) => {
4
+ try {
5
+ step(generator.next(value));
6
+ } catch (e) {
7
+ reject(e);
8
+ }
9
+ };
10
+ var rejected = (value) => {
11
+ try {
12
+ step(generator.throw(value));
13
+ } catch (e) {
14
+ reject(e);
15
+ }
16
+ };
17
+ var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
18
+ step((generator = generator.apply(__this, __arguments)).next());
19
+ });
20
+ };
21
+ import { tool } from "@opencode-ai/plugin";
22
+ import { createLogger, getServiceLogWatcher } from "@vite-plugin-opencode-assistant/shared";
23
+ const log = createLogger("ServiceLogsPlugin");
24
+ const ServiceLogsPlugin = () => __async(null, null, function* () {
25
+ var _a;
26
+ log.debug("ServiceLogsPlugin loading...");
27
+ const logFilesJson = process.env.OPENCODE_LOG_FILES_JSON;
28
+ log.debug("Log files JSON from env:", { logFilesJson: logFilesJson ? "set" : "not set" });
29
+ if (!logFilesJson) {
30
+ log.debug("OPENCODE_LOG_FILES_JSON is not set, service logs plugin will not register tools");
31
+ return {};
32
+ }
33
+ let logFiles;
34
+ try {
35
+ logFiles = JSON.parse(logFilesJson);
36
+ log.debug("Parsed log files config", { count: logFiles.length });
37
+ } catch (e) {
38
+ log.error("Failed to parse OPENCODE_LOG_FILES_JSON", { error: e });
39
+ return {};
40
+ }
41
+ if (!logFiles || logFiles.length === 0) {
42
+ log.debug("No log files configured, plugin will not register any tools");
43
+ return {};
44
+ }
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
+ const tools = {};
57
+ for (const logFileConfig of logFiles) {
58
+ let _b;
59
+ const toolName = `get_${logFileConfig.name}_logs`;
60
+ const description = `\u83B7\u53D6 ${logFileConfig.name} \u7684\u65E5\u5FD7\u3002
61
+
62
+ **\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A
63
+ ${logFileConfig.description}
64
+
65
+ **\u65E5\u5FD7\u5185\u5BB9**\uFF1A
66
+ - \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`;
68
+ const getLogsTool = tool({
69
+ description,
70
+ args: {
71
+ level: tool.schema.string().optional().describe(
72
+ "\u65E5\u5FD7\u7EA7\u522B\u8FC7\u6EE4\uFF1Aerror(\u9519\u8BEF)\u3001warn(\u8B66\u544A)\u3001info(\u4FE1\u606F)\u3002\u591A\u4E2A\u7528\u9017\u53F7\u5206\u9694\uFF0C\u5982 'error,warn'"
73
+ ),
74
+ limit: tool.schema.number().int().min(1).max(200).optional().default(50).describe("\u8FD4\u56DE\u6761\u6570\uFF0C\u9ED8\u8BA4 50\uFF0C\u6700\u5927 200"),
75
+ since: tool.schema.string().optional().describe("\u8D77\u59CB\u65F6\u95F4\uFF08ISO \u683C\u5F0F\uFF09\uFF0C\u83B7\u53D6\u6B64\u65F6\u95F4\u4E4B\u540E\u7684\u65E5\u5FD7")
76
+ },
77
+ execute(args, context) {
78
+ return __async(this, null, function* () {
79
+ const { level, limit, since } = args;
80
+ log.debug(`${toolName} called`, {
81
+ args,
82
+ sessionID: context.sessionID,
83
+ directory: context.directory
84
+ });
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({
90
+ level: level ? level.split(",").map((l) => l.trim()) : void 0,
91
+ limit,
92
+ since
93
+ });
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
96
+
97
+ \u5EFA\u8BAE\uFF1A
98
+ - \u4E0D\u6307\u5B9A\u53C2\u6570\u83B7\u53D6\u6240\u6709\u65E5\u5FD7
99
+ - \u4F7F\u7528 level=error,warn \u83B7\u53D6\u9519\u8BEF\u548C\u8B66\u544A`;
100
+ }
101
+ const formattedLogs = logs.map((entry) => {
102
+ const time = new Date(entry.timestamp).toLocaleTimeString();
103
+ const levelIcon = entry.level === "error" ? "\u274C" : entry.level === "warn" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
104
+ return `${time} ${levelIcon} ${entry.message}`;
105
+ }).join("\n");
106
+ return `${logFileConfig.name} \u65E5\u5FD7\uFF08${logs.length}/${buffer.size()} \u6761\uFF09\uFF1A
107
+
108
+ ${formattedLogs}`;
109
+ });
110
+ }
111
+ });
112
+ tools[toolName] = getLogsTool;
113
+ log.debug(`Registered tool: ${toolName}`);
114
+ }
115
+ log.debug(`Plugin initialized with ${Object.keys(tools).length} log tools`);
116
+ return {
117
+ tool: tools
118
+ };
119
+ });
120
+ var service_logs_default = ServiceLogsPlugin;
121
+ export {
122
+ ServiceLogsPlugin,
123
+ service_logs_default as default
124
+ };