@vite-plugin-opencode-assistant/opencode 1.0.56 → 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.
@@ -24,7 +24,7 @@ const l = async () => {
24
24
 
25
25
  ## ⚠️ 重要:页面上下文优先级规则
26
26
 
27
- **当用户在不同页面提问时,你必须优先根据用户当前浏览页面的上下文来理解问题,而不是依赖会话历史记录。**
27
+ **当用户在不同页面提问时,你必须优先根据用户当前浏览页面的上下文来理解问题,禁止依赖会话历史记录或其他上下文。**
28
28
 
29
29
  用户可能在不同页面之间切换,每次提问都应该基于当前页面上下文:
30
30
 
@@ -37,7 +37,7 @@ const l = async () => {
37
37
  1. **当前页面上下文**(最高优先级) - 根据用户当前所在页面的 URL 和标题理解问题背景
38
38
  2. **用户选中的元素** - 如果用户选中了页面元素,这些元素信息是理解问题的关键
39
39
  3. **用户当前输入** - 用户本次发送的具体问题内容
40
- 4. **会话历史**(最低优先级) - 仅作为辅助参考,不应优先于当前页面上下文
40
+ 4. **会话历史**(最低优先级) - 仅作为辅助参考,绝不能优先于当前页面上下文
41
41
 
42
42
  ## 你的工作环境
43
43
 
@@ -57,7 +57,7 @@ const PageContextPlugin = () => __async(null, null, function* () {
57
57
 
58
58
  ## \u26A0\uFE0F \u91CD\u8981\uFF1A\u9875\u9762\u4E0A\u4E0B\u6587\u4F18\u5148\u7EA7\u89C4\u5219
59
59
 
60
- **\u5F53\u7528\u6237\u5728\u4E0D\u540C\u9875\u9762\u63D0\u95EE\u65F6\uFF0C\u4F60\u5FC5\u987B\u4F18\u5148\u6839\u636E\u7528\u6237\u5F53\u524D\u6D4F\u89C8\u9875\u9762\u7684\u4E0A\u4E0B\u6587\u6765\u7406\u89E3\u95EE\u9898\uFF0C\u800C\u4E0D\u662F\u4F9D\u8D56\u4F1A\u8BDD\u5386\u53F2\u8BB0\u5F55\u3002**
60
+ **\u5F53\u7528\u6237\u5728\u4E0D\u540C\u9875\u9762\u63D0\u95EE\u65F6\uFF0C\u4F60\u5FC5\u987B\u4F18\u5148\u6839\u636E\u7528\u6237\u5F53\u524D\u6D4F\u89C8\u9875\u9762\u7684\u4E0A\u4E0B\u6587\u6765\u7406\u89E3\u95EE\u9898\uFF0C\u7981\u6B62\u4F9D\u8D56\u4F1A\u8BDD\u5386\u53F2\u8BB0\u5F55\u6216\u5176\u4ED6\u4E0A\u4E0B\u6587\u3002**
61
61
 
62
62
  \u7528\u6237\u53EF\u80FD\u5728\u4E0D\u540C\u9875\u9762\u4E4B\u95F4\u5207\u6362\uFF0C\u6BCF\u6B21\u63D0\u95EE\u90FD\u5E94\u8BE5\u57FA\u4E8E\u5F53\u524D\u9875\u9762\u4E0A\u4E0B\u6587\uFF1A
63
63
 
@@ -70,7 +70,7 @@ const PageContextPlugin = () => __async(null, null, function* () {
70
70
  1. **\u5F53\u524D\u9875\u9762\u4E0A\u4E0B\u6587**\uFF08\u6700\u9AD8\u4F18\u5148\u7EA7\uFF09 - \u6839\u636E\u7528\u6237\u5F53\u524D\u6240\u5728\u9875\u9762\u7684 URL \u548C\u6807\u9898\u7406\u89E3\u95EE\u9898\u80CC\u666F
71
71
  2. **\u7528\u6237\u9009\u4E2D\u7684\u5143\u7D20** - \u5982\u679C\u7528\u6237\u9009\u4E2D\u4E86\u9875\u9762\u5143\u7D20\uFF0C\u8FD9\u4E9B\u5143\u7D20\u4FE1\u606F\u662F\u7406\u89E3\u95EE\u9898\u7684\u5173\u952E
72
72
  3. **\u7528\u6237\u5F53\u524D\u8F93\u5165** - \u7528\u6237\u672C\u6B21\u53D1\u9001\u7684\u5177\u4F53\u95EE\u9898\u5185\u5BB9
73
- 4. **\u4F1A\u8BDD\u5386\u53F2**\uFF08\u6700\u4F4E\u4F18\u5148\u7EA7\uFF09 - \u4EC5\u4F5C\u4E3A\u8F85\u52A9\u53C2\u8003\uFF0C\u4E0D\u5E94\u4F18\u5148\u4E8E\u5F53\u524D\u9875\u9762\u4E0A\u4E0B\u6587
73
+ 4. **\u4F1A\u8BDD\u5386\u53F2**\uFF08\u6700\u4F4E\u4F18\u5148\u7EA7\uFF09 - \u4EC5\u4F5C\u4E3A\u8F85\u52A9\u53C2\u8003\uFF0C\u7EDD\u4E0D\u80FD\u4F18\u5148\u4E8E\u5F53\u524D\u9875\u9762\u4E0A\u4E0B\u6587
74
74
 
75
75
  ## \u4F60\u7684\u5DE5\u4F5C\u73AF\u5883
76
76
 
@@ -0,0 +1,3 @@
1
+ import type { Hooks } from "@opencode-ai/plugin";
2
+ export declare const ServiceLogsPlugin: () => Promise<Hooks>;
3
+ export default ServiceLogsPlugin;
@@ -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
+ };