@vite-plugin-opencode-assistant/opencode 1.0.63 → 1.0.65

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/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from "./web.js";
@@ -1,22 +1,44 @@
1
- import { c as createLogger } from "./logger.js";
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 { createLogger } from "@vite-plugin-opencode-assistant/shared";
2
22
  const log = createLogger("OpenCodePluginPageContext");
3
- async function fetchPageContext(contextApiUrl) {
4
- try {
5
- const response = await fetch(contextApiUrl, {
6
- method: "GET",
7
- headers: { "Content-Type": "application/json" }
8
- });
9
- if (!response.ok) {
10
- log.debug("Failed to fetch page context", { status: response.status });
23
+ function fetchPageContext(contextApiUrl) {
24
+ return __async(this, null, function* () {
25
+ try {
26
+ const response = yield fetch(contextApiUrl, {
27
+ method: "GET",
28
+ headers: { "Content-Type": "application/json" }
29
+ });
30
+ if (!response.ok) {
31
+ log.debug("Failed to fetch page context", { status: response.status });
32
+ return null;
33
+ }
34
+ return yield response.json();
35
+ } catch (error) {
36
+ log.debug("Error fetching page context", { error });
11
37
  return null;
12
38
  }
13
- return await response.json();
14
- } catch (error) {
15
- log.debug("Error fetching page context", { error });
16
- return null;
17
- }
39
+ });
18
40
  }
19
- const PageContextPlugin = async () => {
41
+ const PageContextPlugin = () => __async(null, null, function* () {
20
42
  log.info("PageContextPlugin loading...");
21
43
  const contextApiUrl = process.env.OPENCODE_CONTEXT_API_URL;
22
44
  log.debug("Context API URL:", { contextApiUrl });
@@ -26,103 +48,104 @@ const PageContextPlugin = async () => {
26
48
  }
27
49
  log.info("Plugin initialized successfully");
28
50
  return {
29
- "experimental.chat.system.transform": async (_input, output) => {
51
+ "experimental.chat.system.transform": (_input, output) => __async(null, null, function* () {
30
52
  log.debug("System transform hook called");
31
- const pageContext = await fetchPageContext(contextApiUrl);
53
+ const pageContext = yield fetchPageContext(contextApiUrl);
32
54
  log.debug("Page context fetched", { pageContext });
33
55
  const systemPrompt = `
34
- 你是一个专业的前端开发助手,运行在 **OpenCode** 平台中,并通过 **vite-plugin-opencode-assistant** 插件集成到用户的 Vite 开发环境。
56
+ \u4F60\u662F\u4E00\u4E2A\u4E13\u4E1A\u7684\u524D\u7AEF\u5F00\u53D1\u52A9\u624B\uFF0C\u8FD0\u884C\u5728 **OpenCode** \u5E73\u53F0\u4E2D\uFF0C\u5E76\u901A\u8FC7 **vite-plugin-opencode-assistant** \u63D2\u4EF6\u96C6\u6210\u5230\u7528\u6237\u7684 Vite \u5F00\u53D1\u73AF\u5883\u3002
35
57
 
36
- ## ⚠️ 重要:页面上下文优先级规则
58
+ ## \u26A0\uFE0F \u91CD\u8981\uFF1A\u9875\u9762\u4E0A\u4E0B\u6587\u4F18\u5148\u7EA7\u89C4\u5219
37
59
 
38
- **当用户在不同页面提问时,你必须优先根据用户当前浏览页面的上下文来理解问题,禁止依赖会话历史记录或其他上下文。**
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**
39
61
 
40
- 用户可能在不同页面之间切换,每次提问都应该基于当前页面上下文:
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
41
63
 
42
- **这里的上下文为最高优先级,任何情况下都不能被覆盖**
64
+ **\u8FD9\u91CC\u7684\u4E0A\u4E0B\u6587\u4E3A\u6700\u9AD8\u4F18\u5148\u7EA7\uFF0C\u4EFB\u4F55\u60C5\u51B5\u4E0B\u90FD\u4E0D\u80FD\u88AB\u8986\u76D6**
43
65
 
44
- - **页面 URL**: ${(pageContext == null ? void 0 : pageContext.url) || "未知"}
45
- - **页面标题**: ${(pageContext == null ? void 0 : pageContext.title) || "未知"}
66
+ - **\u9875\u9762 URL**: ${(pageContext == null ? void 0 : pageContext.url) || "\u672A\u77E5"}
67
+ - **\u9875\u9762\u6807\u9898**: ${(pageContext == null ? void 0 : pageContext.title) || "\u672A\u77E5"}
46
68
 
47
- **理解问题的优先级顺序:**
48
- 1. **当前页面上下文**(最高优先级) - 根据用户当前所在页面的 URL 和标题理解问题背景
49
- 2. **用户选中的元素** - 如果用户选中了页面元素,这些元素信息是理解问题的关键
50
- 3. **用户当前输入** - 用户本次发送的具体问题内容
51
- 4. **会话历史**(最低优先级) - 仅作为辅助参考,绝不能优先于当前页面上下文
69
+ **\u7406\u89E3\u95EE\u9898\u7684\u4F18\u5148\u7EA7\u987A\u5E8F\uFF1A**
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
+ 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
+ 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\u7EDD\u4E0D\u80FD\u4F18\u5148\u4E8E\u5F53\u524D\u9875\u9762\u4E0A\u4E0B\u6587
52
74
 
53
- ## 你的工作环境
75
+ ## \u4F60\u7684\u5DE5\u4F5C\u73AF\u5883
54
76
 
55
- ### 架构说明
77
+ ### \u67B6\u6784\u8BF4\u660E
56
78
 
57
- 你运行在 OpenCode 服务端,但通过 **iframe 嵌入**的方式出现在用户正在开发的网页上:
79
+ \u4F60\u8FD0\u884C\u5728 OpenCode \u670D\u52A1\u7AEF\uFF0C\u4F46\u901A\u8FC7 **iframe \u5D4C\u5165**\u7684\u65B9\u5F0F\u51FA\u73B0\u5728\u7528\u6237\u6B63\u5728\u5F00\u53D1\u7684\u7F51\u9875\u4E0A\uFF1A
58
80
 
59
81
  \`\`\`
60
- ┌─────────────────────────────────────────────────────────────┐
61
- 用户正在开发的网页 (运行在 Vite Dev Server)
62
- ┌───────────────────────────────────────────────────────┐
63
- 页面内容
64
-
65
- ┌─────────────────────────────┐
66
- OpenCode iframe (你的界面) 浮动聊天窗口
67
- 用户在这里与你对话
68
- └─────────────────────────────┘
69
- └───────────────────────────────────────────────────────┘
70
- └─────────────────────────────────────────────────────────────┘
82
+ \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510
83
+ \u2502 \u7528\u6237\u6B63\u5728\u5F00\u53D1\u7684\u7F51\u9875 (\u8FD0\u884C\u5728 Vite Dev Server) \u2502
84
+ \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502
85
+ \u2502 \u2502 \u9875\u9762\u5185\u5BB9 \u2502 \u2502
86
+ \u2502 \u2502 \u2502 \u2502
87
+ \u2502 \u2502 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502 \u2502
88
+ \u2502 \u2502 \u2502 OpenCode iframe (\u4F60\u7684\u754C\u9762) \u2502 \u2190 \u6D6E\u52A8\u804A\u5929\u7A97\u53E3 \u2502 \u2502
89
+ \u2502 \u2502 \u2502 \u7528\u6237\u5728\u8FD9\u91CC\u4E0E\u4F60\u5BF9\u8BDD \u2502 \u2502 \u2502
90
+ \u2502 \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502 \u2502
91
+ \u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502
92
+ \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
71
93
  \`\`\`
72
94
 
73
- ## 工作流程
95
+ ## \u5DE5\u4F5C\u6D41\u7A0B
74
96
 
75
- 请严格按以下顺序处理用户请求:
97
+ \u8BF7\u4E25\u683C\u6309\u4EE5\u4E0B\u987A\u5E8F\u5904\u7406\u7528\u6237\u8BF7\u6C42\uFF1A
76
98
 
77
- ### 1. 定位节点位置(强制)
99
+ ### 1. \u5B9A\u4F4D\u8282\u70B9\u4F4D\u7F6E\uFF08\u5F3A\u5236\uFF09
78
100
 
79
- 当用户选中了页面节点时,**在处理用户请求之前**,你必须先确认这些节点在项目中的位置。选中的节点信息包含以下字段:
101
+ \u5F53\u7528\u6237\u9009\u4E2D\u4E86\u9875\u9762\u8282\u70B9\u65F6\uFF0C**\u5728\u5904\u7406\u7528\u6237\u8BF7\u6C42\u4E4B\u524D**\uFF0C\u4F60\u5FC5\u987B\u5148\u786E\u8BA4\u8FD9\u4E9B\u8282\u70B9\u5728\u9879\u76EE\u4E2D\u7684\u4F4D\u7F6E\u3002\u9009\u4E2D\u7684\u8282\u70B9\u4FE1\u606F\u5305\u542B\u4EE5\u4E0B\u5B57\u6BB5\uFF1A
80
102
 
81
- - \`filePath\`: 节点对应的源文件路径(可能为空)
82
- - \`line\` / \`column\`: 节点在源文件中的位置(可能为空)
83
- - \`innerText\`: 节点的文本内容
84
- - \`description\`: 节点描述(标签名+选择器)
103
+ - \`filePath\`: \u8282\u70B9\u5BF9\u5E94\u7684\u6E90\u6587\u4EF6\u8DEF\u5F84\uFF08\u53EF\u80FD\u4E3A\u7A7A\uFF09
104
+ - \`line\` / \`column\`: \u8282\u70B9\u5728\u6E90\u6587\u4EF6\u4E2D\u7684\u4F4D\u7F6E\uFF08\u53EF\u80FD\u4E3A\u7A7A\uFF09
105
+ - \`innerText\`: \u8282\u70B9\u7684\u6587\u672C\u5185\u5BB9
106
+ - \`description\`: \u8282\u70B9\u63CF\u8FF0\uFF08\u6807\u7B7E\u540D+\u9009\u62E9\u5668\uFF09
85
107
 
86
- **定位步骤(按优先级):**
108
+ **\u5B9A\u4F4D\u6B65\u9AA4\uFF08\u6309\u4F18\u5148\u7EA7\uFF09\uFF1A**
87
109
 
88
- 1. **检查已有文件路径**:如果 \`filePath\` 存在,优先使用该路径直接定位文件
89
- 2. **使用 Chrome DevTools MCP**:如果 \`filePath\` 为空或需要更多信息,通过浏览器快照获取节点的 DOM 结构、样式、事件绑定等详细信息
90
- 3. **文件搜索辅助**:根据 \`innerText\` \`description\` 在项目中搜索匹配的组件/元素
110
+ 1. **\u68C0\u67E5\u5DF2\u6709\u6587\u4EF6\u8DEF\u5F84**\uFF1A\u5982\u679C \`filePath\` \u5B58\u5728\uFF0C\u4F18\u5148\u4F7F\u7528\u8BE5\u8DEF\u5F84\u76F4\u63A5\u5B9A\u4F4D\u6587\u4EF6
111
+ 2. **\u4F7F\u7528 Chrome DevTools MCP**\uFF1A\u5982\u679C \`filePath\` \u4E3A\u7A7A\u6216\u9700\u8981\u66F4\u591A\u4FE1\u606F\uFF0C\u901A\u8FC7\u6D4F\u89C8\u5668\u5FEB\u7167\u83B7\u53D6\u8282\u70B9\u7684 DOM \u7ED3\u6784\u3001\u6837\u5F0F\u3001\u4E8B\u4EF6\u7ED1\u5B9A\u7B49\u8BE6\u7EC6\u4FE1\u606F
112
+ 3. **\u6587\u4EF6\u641C\u7D22\u8F85\u52A9**\uFF1A\u6839\u636E \`innerText\` \u6216 \`description\` \u5728\u9879\u76EE\u4E2D\u641C\u7D22\u5339\u914D\u7684\u7EC4\u4EF6/\u5143\u7D20
91
113
 
92
- 在明确知道节点位置后,再处理用户请求。
114
+ \u5728\u660E\u786E\u77E5\u9053\u8282\u70B9\u4F4D\u7F6E\u540E\uFF0C\u518D\u5904\u7406\u7528\u6237\u8BF7\u6C42\u3002
93
115
 
94
- ### 2. 理解上下文
116
+ ### 2. \u7406\u89E3\u4E0A\u4E0B\u6587
95
117
 
96
- 将页面 URL、标题和选中节点信息作为用户请求的背景,帮助理解用户的真实意图。
118
+ \u5C06\u9875\u9762 URL\u3001\u6807\u9898\u548C\u9009\u4E2D\u8282\u70B9\u4FE1\u606F\u4F5C\u4E3A\u7528\u6237\u8BF7\u6C42\u7684\u80CC\u666F\uFF0C\u5E2E\u52A9\u7406\u89E3\u7528\u6237\u7684\u771F\u5B9E\u610F\u56FE\u3002
97
119
 
98
- ### 3. 直接行动
120
+ ### 3. \u76F4\u63A5\u884C\u52A8
99
121
 
100
- 在明确节点位置后,针对用户的实际请求给出清晰、可执行的方案。
122
+ \u5728\u660E\u786E\u8282\u70B9\u4F4D\u7F6E\u540E\uFF0C\u9488\u5BF9\u7528\u6237\u7684\u5B9E\u9645\u8BF7\u6C42\u7ED9\u51FA\u6E05\u6670\u3001\u53EF\u6267\u884C\u7684\u65B9\u6848\u3002
101
123
 
102
- ## 工具使用指南
124
+ ## \u5DE5\u5177\u4F7F\u7528\u6307\u5357
103
125
 
104
126
  ### Chrome DevTools Mcp
105
127
 
106
- 1. **确保操作正确页面(强制)**
107
- 在使用 Chrome DevTools Mcp 执行任何与用户正在浏览的页面相关的任务之前,必须先确认当前操作的页面就是用户正在浏览的页面。如果不确定,应先获取当前页面 URL 并与上下文中的页面 URL 进行比对。
128
+ 1. **\u786E\u4FDD\u64CD\u4F5C\u6B63\u786E\u9875\u9762\uFF08\u5F3A\u5236\uFF09**
129
+ \u5728\u4F7F\u7528 Chrome DevTools Mcp \u6267\u884C\u4EFB\u4F55\u4E0E\u7528\u6237\u6B63\u5728\u6D4F\u89C8\u7684\u9875\u9762\u76F8\u5173\u7684\u4EFB\u52A1\u4E4B\u524D\uFF0C\u5FC5\u987B\u5148\u786E\u8BA4\u5F53\u524D\u64CD\u4F5C\u7684\u9875\u9762\u5C31\u662F\u7528\u6237\u6B63\u5728\u6D4F\u89C8\u7684\u9875\u9762\u3002\u5982\u679C\u4E0D\u786E\u5B9A\uFF0C\u5E94\u5148\u83B7\u53D6\u5F53\u524D\u9875\u9762 URL \u5E76\u4E0E\u4E0A\u4E0B\u6587\u4E2D\u7684\u9875\u9762 URL \u8FDB\u884C\u6BD4\u5BF9\u3002
108
130
 
109
- 2. **快照获取(强制)**
110
- 在没有获取到需要的节点信息时,使用 verbose 参数来获取更详细的节点信息。如果设置了 verbose 参数还是没有获取到节点信息,再尝试考虑其他方案。
131
+ 2. **\u5FEB\u7167\u83B7\u53D6\uFF08\u5F3A\u5236\uFF09**
132
+ \u5728\u6CA1\u6709\u83B7\u53D6\u5230\u9700\u8981\u7684\u8282\u70B9\u4FE1\u606F\u65F6\uFF0C\u4F7F\u7528 verbose \u53C2\u6570\u6765\u83B7\u53D6\u66F4\u8BE6\u7EC6\u7684\u8282\u70B9\u4FE1\u606F\u3002\u5982\u679C\u8BBE\u7F6E\u4E86 verbose \u53C2\u6570\u8FD8\u662F\u6CA1\u6709\u83B7\u53D6\u5230\u8282\u70B9\u4FE1\u606F\uFF0C\u518D\u5C1D\u8BD5\u8003\u8651\u5176\u4ED6\u65B9\u6848\u3002
111
133
 
112
- 3. **单页应用(SPA)特性**
113
- 如果用户开发的是单页应用(SPA),执行任务大部分情况下不需要刷新页面。
134
+ 3. **\u5355\u9875\u5E94\u7528\uFF08SPA\uFF09\u7279\u6027**
135
+ \u5982\u679C\u7528\u6237\u5F00\u53D1\u7684\u662F\u5355\u9875\u5E94\u7528\uFF08SPA\uFF09\uFF0C\u6267\u884C\u4EFB\u52A1\u5927\u90E8\u5206\u60C5\u51B5\u4E0B\u4E0D\u9700\u8981\u5237\u65B0\u9875\u9762\u3002
114
136
 
115
- 4. **HTTP 请求成功判断(强制)**
116
- 判断请求成功时,不要只看 HTTP 状态码!HTTP 状态码 200 并不代表业务逻辑成功。必须获取接口的详细响应内容,检查响应体中的业务状态码或错误信息。在确认请求成功之前,始终解析并检查响应体的完整内容
137
+ 4. **HTTP \u8BF7\u6C42\u6210\u529F\u5224\u65AD\uFF08\u5F3A\u5236\uFF09**
138
+ \u5224\u65AD\u8BF7\u6C42\u6210\u529F\u65F6\uFF0C\u4E0D\u8981\u53EA\u770B HTTP \u72B6\u6001\u7801\uFF01HTTP \u72B6\u6001\u7801 200 \u5E76\u4E0D\u4EE3\u8868\u4E1A\u52A1\u903B\u8F91\u6210\u529F\u3002\u5FC5\u987B\u83B7\u53D6\u63A5\u53E3\u7684\u8BE6\u7EC6\u54CD\u5E94\u5185\u5BB9\uFF0C\u68C0\u67E5\u54CD\u5E94\u4F53\u4E2D\u7684\u4E1A\u52A1\u72B6\u6001\u7801\u6216\u9519\u8BEF\u4FE1\u606F\u3002\u5728\u786E\u8BA4\u8BF7\u6C42\u6210\u529F\u4E4B\u524D\uFF0C\u59CB\u7EC8\u89E3\u6790\u5E76\u68C0\u67E5\u54CD\u5E94\u4F53\u7684\u5B8C\u6574\u5185\u5BB9
117
139
 
118
- 5. **工具使用优先级(强制)**
119
- 在使用 Chrome DevTools MCP 工具时,\`chrome-devtools_evaluate_script\` 工具的使用优先级最低。只有在其他工具无法满足需求时,才考虑使用该工具
140
+ 5. **\u5DE5\u5177\u4F7F\u7528\u4F18\u5148\u7EA7\uFF08\u5F3A\u5236\uFF09**
141
+ \u5728\u4F7F\u7528 Chrome DevTools MCP \u5DE5\u5177\u65F6\uFF0C\`chrome-devtools_evaluate_script\` \u5DE5\u5177\u7684\u4F7F\u7528\u4F18\u5148\u7EA7\u6700\u4F4E\u3002\u53EA\u6709\u5728\u5176\u4ED6\u5DE5\u5177\u65E0\u6CD5\u6EE1\u8DB3\u9700\u6C42\u65F6\uFF0C\u624D\u8003\u8651\u4F7F\u7528\u8BE5\u5DE5\u5177
120
142
  `.trim();
121
143
  output.system.push(systemPrompt);
122
- }
144
+ })
123
145
  };
124
- };
146
+ });
147
+ var page_context_default = PageContextPlugin;
125
148
  export {
126
149
  PageContextPlugin,
127
- PageContextPlugin as default
150
+ page_context_default as default
128
151
  };
@@ -1,7 +1,3 @@
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
1
  var __async = (__this, __arguments, generator) => {
6
2
  return new Promise((resolve, reject) => {
7
3
  var fulfilled = (value) => {
@@ -22,122 +18,10 @@ var __async = (__this, __arguments, generator) => {
22
18
  step((generator = generator.apply(__this, __arguments)).next());
23
19
  });
24
20
  };
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";
30
- }
31
- if (lowerLine.includes("warn") || lowerLine.includes("warning")) {
32
- return "warn";
33
- }
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}[+-]\d{2}:\d{2})/,
39
- /(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z?)/,
40
- /(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z)/,
41
- /(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})/,
42
- /(\d{2}\/\w{3}\/\d{4}:\d{2}:\d{2}:\d{2} [+-]\d{4})/,
43
- /(\d{2}\/\d{2}\/\d{4},\s*\d{1,2}:\d{2}:\d{2}\s*(?:AM|PM))/i,
44
- /([A-Z]{3}, \d{2} \w{3} \d{4} \d{2}:\d{2}:\d{2} GMT)/,
45
- /(\[([^\]]+)\])/
46
- ];
47
- for (const pattern of timestampPatterns) {
48
- const match = line.match(pattern);
49
- if (match) {
50
- const timestampStr = match[1];
51
- const date = new Date(timestampStr);
52
- if (!isNaN(date.getTime())) {
53
- return date.toISOString();
54
- }
55
- }
56
- }
57
- return null;
58
- }
59
- function readLogFileTail(options) {
60
- return __async(this, null, function* () {
61
- const { name, filePath, projectRoot, lines = 200, limit, level, since } = options;
62
- const resolvedPath = resolvePath(filePath, projectRoot);
63
- if (!fs.existsSync(resolvedPath)) {
64
- log$1.debug(`Log file does not exist: ${resolvedPath}`);
65
- return [];
66
- }
67
- try {
68
- const stat = fs.statSync(resolvedPath);
69
- const fd = fs.openSync(resolvedPath, "r");
70
- const chunkSize = 16 * 1024;
71
- let position = stat.size;
72
- let buffer = Buffer.alloc(0);
73
- const lineCount = 0;
74
- while (position > 0 && lineCount <= lines) {
75
- const readSize = Math.min(chunkSize, position);
76
- position -= readSize;
77
- const chunk = Buffer.alloc(readSize);
78
- fs.readSync(fd, chunk, 0, readSize, position);
79
- buffer = Buffer.concat([chunk, buffer]);
80
- const newLineCount = buffer.filter((byte) => byte === 10).length;
81
- if (newLineCount >= lines) {
82
- const linesArray = buffer.toString("utf-8").split("\n");
83
- const excessLines = newLineCount - lines;
84
- let charsToRemove = 0;
85
- let count = 0;
86
- for (let i = 0; i < linesArray.length; i++) {
87
- count += linesArray[i].length + 1;
88
- if (count > excessLines) {
89
- charsToRemove = linesArray.slice(0, i + 1).join("\n").length + 1;
90
- break;
91
- }
92
- }
93
- buffer = buffer.slice(charsToRemove);
94
- break;
95
- }
96
- }
97
- fs.closeSync(fd);
98
- const content = buffer.toString("utf-8").trim();
99
- const logLines = content.split("\n").filter((line) => line.trim());
100
- const entries = [];
101
- const sinceDate = since ? new Date(since) : null;
102
- for (const line of logLines) {
103
- const entry = {
104
- level: detectLogLevel(line),
105
- message: line,
106
- timestamp: parseLogTimestamp(line) || (/* @__PURE__ */ new Date()).toISOString(),
107
- source: `file:${name}`
108
- };
109
- if (sinceDate && new Date(entry.timestamp) < sinceDate) {
110
- continue;
111
- }
112
- if (level) {
113
- const levels = Array.isArray(level) ? level : [level];
114
- if (!levels.includes(entry.level)) {
115
- continue;
116
- }
117
- }
118
- entries.push(entry);
119
- }
120
- if (limit && limit > 0) {
121
- return entries.slice(-limit);
122
- }
123
- return entries;
124
- } catch (err) {
125
- log$1.error(`Error reading log file ${resolvedPath}`, { error: err });
126
- return [];
127
- }
128
- });
129
- }
130
- function resolvePath(filePath, projectRoot) {
131
- if (path.isAbsolute(filePath)) {
132
- return filePath;
133
- }
134
- if (projectRoot) {
135
- return path.resolve(projectRoot, filePath);
136
- }
137
- return path.resolve(process.cwd(), filePath);
138
- }
21
+ import { tool } from "@opencode-ai/plugin";
22
+ import { readLogFileTail, createLogger } from "@vite-plugin-opencode-assistant/shared";
139
23
  const log = createLogger("ServiceLogsPlugin");
140
- const ServiceLogsPlugin = async () => {
24
+ const ServiceLogsPlugin = () => __async(null, null, function* () {
141
25
  log.debug("ServiceLogsPlugin loading...");
142
26
  const logFilesJson = process.env.OPENCODE_LOG_FILES_JSON;
143
27
  log.debug("Log files JSON from env:", { logFilesJson: logFilesJson ? "set" : "not set" });
@@ -159,56 +43,59 @@ const ServiceLogsPlugin = async () => {
159
43
  }
160
44
  const tools = {};
161
45
  for (const logFileConfig of logFiles) {
46
+ let _a;
162
47
  const toolName = `get_${logFileConfig.name}_logs`;
163
- const description = `获取 ${logFileConfig.name} 的日志。
48
+ const description = `\u83B7\u53D6 ${logFileConfig.name} \u7684\u65E5\u5FD7\u3002
164
49
 
165
- **何时使用此工具**:
50
+ **\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A
166
51
  ${logFileConfig.description}
167
52
 
168
- **日志内容**:
169
- - 来自日志文件 ${logFileConfig.path} 的实时日志
170
- - 默认返回最近 200 行日志`;
53
+ **\u65E5\u5FD7\u5185\u5BB9**\uFF1A
54
+ - \u6765\u81EA\u65E5\u5FD7\u6587\u4EF6 ${logFileConfig.path} \u7684\u5B9E\u65F6\u65E5\u5FD7
55
+ - \u9ED8\u8BA4\u8FD4\u56DE\u6700\u8FD1 200 \u884C\u65E5\u5FD7`;
171
56
  const getLogsTool = tool({
172
57
  description,
173
58
  args: {
174
59
  level: tool.schema.string().optional().describe(
175
- "日志级别过滤:error(错误)、warn(警告)、info(信息)。多个用逗号分隔,如 'error,warn'"
60
+ "\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'"
176
61
  ),
177
- limit: tool.schema.number().int().min(1).max(200).optional().default(50).describe("返回条数,默认 50,最大 200"),
178
- since: tool.schema.string().optional().describe("起始时间(ISO 格式),获取此时间之后的日志")
62
+ 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"),
63
+ 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")
179
64
  },
180
- async execute(args, context) {
181
- const { level, limit, since } = args;
182
- log.debug(`${toolName} called`, {
183
- args,
184
- sessionID: context.sessionID,
185
- directory: context.directory
186
- });
187
- const requestedLimit = limit ?? 50;
188
- const entries = await readLogFileTail({
189
- name: logFileConfig.name,
190
- filePath: logFileConfig.path,
191
- projectRoot: process.cwd(),
192
- lines: Math.max(requestedLimit * 3, 500),
193
- level: level ? level.split(",").map((l) => l.trim()) : void 0,
194
- since,
195
- limit: requestedLimit
196
- });
197
- if (entries.length === 0) {
198
- return `当前没有符合条件的日志。
65
+ execute(args, context) {
66
+ return __async(this, null, function* () {
67
+ const { level, limit, since } = args;
68
+ log.debug(`${toolName} called`, {
69
+ args,
70
+ sessionID: context.sessionID,
71
+ directory: context.directory
72
+ });
73
+ const requestedLimit = limit != null ? limit : 50;
74
+ const entries = yield readLogFileTail({
75
+ name: logFileConfig.name,
76
+ filePath: logFileConfig.path,
77
+ projectRoot: process.cwd(),
78
+ lines: Math.max(requestedLimit * 3, 500),
79
+ level: level ? level.split(",").map((l) => l.trim()) : void 0,
80
+ since,
81
+ limit: requestedLimit
82
+ });
83
+ if (entries.length === 0) {
84
+ return `\u5F53\u524D\u6CA1\u6709\u7B26\u5408\u6761\u4EF6\u7684\u65E5\u5FD7\u3002
199
85
 
200
- 建议:
201
- - 不指定参数获取所有日志
202
- - 使用 level=error,warn 获取错误和警告`;
203
- }
204
- const formattedLogs = entries.map((entry) => {
205
- const time = new Date(entry.timestamp).toLocaleTimeString();
206
- const levelIcon = entry.level === "error" ? "" : entry.level === "warn" ? "⚠️" : "ℹ️";
207
- return `${time} ${levelIcon} ${entry.message}`;
208
- }).join("\n");
209
- return `${logFileConfig.name} 日志(${entries.length} 条):
86
+ \u5EFA\u8BAE\uFF1A
87
+ - \u4E0D\u6307\u5B9A\u53C2\u6570\u83B7\u53D6\u6240\u6709\u65E5\u5FD7
88
+ - \u4F7F\u7528 level=error,warn \u83B7\u53D6\u9519\u8BEF\u548C\u8B66\u544A`;
89
+ }
90
+ const formattedLogs = entries.map((entry) => {
91
+ const time = new Date(entry.timestamp).toLocaleTimeString();
92
+ const levelIcon = entry.level === "error" ? "\u274C" : entry.level === "warn" ? "\u26A0\uFE0F" : "\u2139\uFE0F";
93
+ return `${time} ${levelIcon} ${entry.message}`;
94
+ }).join("\n");
95
+ return `${logFileConfig.name} \u65E5\u5FD7\uFF08${entries.length} \u6761\uFF09\uFF1A
210
96
 
211
97
  ${formattedLogs}`;
98
+ });
212
99
  }
213
100
  });
214
101
  tools[toolName] = getLogsTool;
@@ -218,8 +105,9 @@ ${formattedLogs}`;
218
105
  return {
219
106
  tool: tools
220
107
  };
221
- };
108
+ });
109
+ var service_logs_default = ServiceLogsPlugin;
222
110
  export {
223
111
  ServiceLogsPlugin,
224
- ServiceLogsPlugin as default
112
+ service_logs_default as default
225
113
  };