@vite-plugin-opencode-assistant/opencode 1.0.57 → 1.0.58
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.d.ts +3 -0
- package/es/plugins/service-logs.js +212 -0
- package/es/plugins/service-logs.mjs +124 -0
- package/es/plugins/tool.js +9389 -0
- package/es/plugins/vite-logs.js +36 -9421
- package/es/web.mjs +21 -5
- package/lib/plugins/service-logs.cjs +147 -0
- package/lib/plugins/service-logs.d.ts +3 -0
- package/lib/web.cjs +21 -5
- package/package.json +2 -2
|
@@ -0,0 +1,212 @@
|
|
|
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}`));
|
|
27
|
+
}
|
|
28
|
+
resolvePath(e) {
|
|
29
|
+
return o.isAbsolute(this.filePath) ? this.filePath : e ? o.resolve(e, this.filePath) : o.resolve(process.cwd(), this.filePath);
|
|
30
|
+
}
|
|
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 });
|
|
42
|
+
}
|
|
43
|
+
}
|
|
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));
|
|
67
|
+
}
|
|
68
|
+
if (e.since) {
|
|
69
|
+
const i = new Date(e.since);
|
|
70
|
+
t = t.filter((r) => new Date(r.timestamp) >= i);
|
|
71
|
+
}
|
|
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
|
+
}
|
|
92
|
+
}
|
|
93
|
+
class z {
|
|
94
|
+
constructor() {
|
|
95
|
+
n(this, "buffers", /* @__PURE__ */ new Map()), n(this, "projectRoot", null);
|
|
96
|
+
}
|
|
97
|
+
setProjectRoot(e) {
|
|
98
|
+
this.projectRoot = e;
|
|
99
|
+
}
|
|
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());
|
|
126
|
+
}
|
|
127
|
+
}
|
|
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;
|
|
138
|
+
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} 的日志。
|
|
157
|
+
|
|
158
|
+
**何时使用此工具**:
|
|
159
|
+
${r.description}
|
|
160
|
+
|
|
161
|
+
**日志内容**:
|
|
162
|
+
- 来自日志文件 ${r.path} 的实时日志
|
|
163
|
+
- 最多保留 ${r.maxBufferSize ?? 200} 条日志`, S = h({
|
|
164
|
+
description: L,
|
|
165
|
+
args: {
|
|
166
|
+
level: h.schema.string().optional().describe(
|
|
167
|
+
"日志级别过滤:error(错误)、warn(警告)、info(信息)。多个用逗号分隔,如 'error,warn'"
|
|
168
|
+
),
|
|
169
|
+
limit: h.schema.number().int().min(1).max(200).optional().default(50).describe("返回条数,默认 50,最大 200"),
|
|
170
|
+
since: h.schema.string().optional().describe("起始时间(ISO 格式),获取此时间之后的日志")
|
|
171
|
+
},
|
|
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
|
|
178
|
+
});
|
|
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
|
|
186
|
+
});
|
|
187
|
+
if (u.length === 0)
|
|
188
|
+
return `当前没有符合条件的日志(缓冲区共 ${g.size()} 条)。
|
|
189
|
+
|
|
190
|
+
建议:
|
|
191
|
+
- 不指定参数获取所有日志
|
|
192
|
+
- 使用 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()} 条):
|
|
199
|
+
|
|
200
|
+
${$}`;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
i[f] = S, l.debug(`Registered tool: ${f}`);
|
|
204
|
+
}
|
|
205
|
+
return l.debug(`Plugin initialized with ${Object.keys(i).length} log tools`), {
|
|
206
|
+
tool: i
|
|
207
|
+
};
|
|
208
|
+
};
|
|
209
|
+
export {
|
|
210
|
+
B as ServiceLogsPlugin,
|
|
211
|
+
B as default
|
|
212
|
+
};
|
|
@@ -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
|
+
};
|