@aipanel/provider-opencode 1.2.6 → 1.2.8

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/constants.js CHANGED
@@ -17,7 +17,6 @@ const DEFAULT_OPENCODE_SETTINGS = {
17
17
  };
18
18
  const DEFAULT_OPENCODE_PROVIDER_OPTIONS = {
19
19
  enableLsp: true,
20
- enableBlockOnError: false,
21
20
  enablePrettier: true
22
21
  };
23
22
  export {
@@ -1,7 +1,7 @@
1
1
  import { execa } from "execa";
2
2
  import fs from "fs";
3
3
  import path from "path";
4
- import { pathToFileURL } from "url";
4
+ import { fileURLToPath, pathToFileURL } from "url";
5
5
  import {
6
6
  AIPANEL_CACHE_DIR,
7
7
  MCP_API_PATH,
@@ -9,11 +9,10 @@ import {
9
9
  ENV_VSCODE_PORT,
10
10
  createLogger,
11
11
  getProcessLogBuffer,
12
- createPackageRequire,
13
- resolvePackageDir
12
+ createPackageRequire
14
13
  } from "@aipanel/core/node";
15
14
  const require2 = createPackageRequire();
16
- const packageDir = resolvePackageDir("@aipanel/opencode-plugins");
15
+ const pluginsDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "plugins");
17
16
  const log = createLogger("OpenCodeWeb");
18
17
  function prepareOpenCodeRuntime(cwd, vitePort, enableLsp, enablePrettier) {
19
18
  const cacheDir = path.join(cwd, AIPANEL_CACHE_DIR, "opencode");
@@ -53,7 +52,6 @@ function startOpenCodeWeb(options) {
53
52
  contextApiUrl,
54
53
  logsApiUrl,
55
54
  logFilesJson,
56
- enableBlockOnError,
57
55
  verbose,
58
56
  enableLsp,
59
57
  vueDevtoolsApiUrl
@@ -65,7 +63,6 @@ function startOpenCodeWeb(options) {
65
63
  contextApiUrl,
66
64
  logsApiUrl,
67
65
  logFilesJson,
68
- enableBlockOnError,
69
66
  verbose,
70
67
  enableLsp
71
68
  });
@@ -75,7 +72,6 @@ function startOpenCodeWeb(options) {
75
72
  contextApiUrl,
76
73
  logsApiUrl,
77
74
  logFilesJson,
78
- enableBlockOnError,
79
75
  verbose,
80
76
  enableLsp,
81
77
  vueDevtoolsApiUrl,
@@ -201,7 +197,7 @@ function resolveFormatBridgePath() {
201
197
  return void 0;
202
198
  }
203
199
  function resolveSourcePluginsDir() {
204
- const candidatePaths = [path.join(packageDir, "es", "plugins")];
200
+ const candidatePaths = [pluginsDir];
205
201
  for (const candidatePath of candidatePaths) {
206
202
  if (fs.existsSync(candidatePath)) {
207
203
  return candidatePath;
@@ -209,9 +205,8 @@ function resolveSourcePluginsDir() {
209
205
  }
210
206
  return candidatePaths[0];
211
207
  }
212
- const MIGRATED_TO_MCP_PLUGINS = /* @__PURE__ */ new Set(["vue-devtools.js", "vite-logs.js", "service-logs.js"]);
213
208
  function resolvePluginEntries(sourceDir) {
214
- const files = fs.readdirSync(sourceDir).filter((f) => f.endsWith(".js") && !MIGRATED_TO_MCP_PLUGINS.has(f));
209
+ const files = fs.readdirSync(sourceDir).filter((f) => f.endsWith(".js"));
215
210
  const entries = files.map((file) => {
216
211
  const absolutePath = path.join(sourceDir, file);
217
212
  return pathToFileURL(absolutePath).href;
@@ -219,7 +214,7 @@ function resolvePluginEntries(sourceDir) {
219
214
  log.debug("Resolved plugin entries", { count: entries.length, entries });
220
215
  return entries;
221
216
  }
222
- function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFilesJson, enableBlockOnError, verbose, enableLsp, vueDevtoolsApiUrl, workspace) {
217
+ function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFilesJson, verbose, enableLsp, vueDevtoolsApiUrl, workspace) {
223
218
  const env = {
224
219
  ...Object.fromEntries(
225
220
  Object.entries(process.env).filter(([, v]) => v !== void 0)
@@ -244,10 +239,6 @@ function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFile
244
239
  env.OPENCODE_LOG_FILES_JSON = logFilesJson;
245
240
  log.debug("Set OPENCODE_LOG_FILES_JSON", { logFilesJson });
246
241
  }
247
- if (enableBlockOnError) {
248
- env.OPENCODE_BLOCK_ON_ERROR = "1";
249
- log.debug("Set OPENCODE_BLOCK_ON_ERROR=1");
250
- }
251
242
  if (verbose) {
252
243
  env.OPENCODE_VERBOSE = "1";
253
244
  log.debug("Set OPENCODE_VERBOSE=1");
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @fileoverview 编辑后诊断插件
3
+ * @description edit/write 工具执行后:
4
+ * 1. ESLint 检查(Node API)
5
+ * 2. vue-tsc 类型检查(过滤当前文件诊断)
6
+ * 3. 诊断结果追加到工具输出,供 Agent 查看(不做回滚)
7
+ *
8
+ * 诊断引擎(ESLint/vue-tsc/格式化/全量诊断)统一由 @aipanel/core/node 提供,
9
+ * 与 dsh 侧审查工具共用同一实现,保证行为一致。
10
+ */
11
+ import type { Hooks } from "@opencode-ai/plugin";
12
+ declare const _default: {
13
+ id: string;
14
+ server(): Promise<Hooks>;
15
+ };
16
+ export default _default;
@@ -0,0 +1,113 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { tool } from "@opencode-ai/plugin";
4
+ import {
5
+ setVerbose,
6
+ createLogger,
7
+ runAllChecks,
8
+ runProjectDiagnostics,
9
+ formatDiagnosticsSections,
10
+ isJsFile
11
+ } from "@aipanel/core/node";
12
+ if (process.env.OPENCODE_VERBOSE === "1") {
13
+ setVerbose(true);
14
+ }
15
+ const log = createLogger("EditDiagnostics");
16
+ const EDIT_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "apply_patch"]);
17
+ const isLintEnabled = () => process.env.OPENCODE_ENABLE_LINT === "1";
18
+ var edit_diagnostics_default = {
19
+ id: "vite-plugin-aipanel/edit-diagnostics",
20
+ async server() {
21
+ const workspace = process.env.OPENCODE_WORKSPACE || process.cwd();
22
+ const runDiagnosticsTool = tool({
23
+ description: `\u8FD0\u884C ESLint \u548C vue-tsc \u7C7B\u578B\u68C0\u67E5\uFF0C\u8FD4\u56DE\u8BCA\u65AD\u7ED3\u679C\u3002
24
+
25
+ **\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A
26
+ - \u521A\u5B8C\u6210\u4EE3\u7801\u4FEE\u6539\uFF0C\u60F3\u9A8C\u8BC1\u662F\u5426\u6709 ESLint \u9519\u8BEF\u6216\u7C7B\u578B\u9519\u8BEF
27
+ - \u5728\u63D0\u4EA4\u4EE3\u7801\u524D\u8FDB\u884C\u8D28\u91CF\u68C0\u67E5
28
+ - \u6392\u67E5\u7F16\u8F91\u5668\u672A\u663E\u793A\u4F46\u5B9E\u9645\u5B58\u5728\u7684\u7C7B\u578B\u95EE\u9898
29
+ - \u4E0D\u4F20\u53C2\u6570\u53EF\u5168\u91CF\u8BCA\u65AD\u6574\u4E2A\u9879\u76EE
30
+
31
+ **\u8BCA\u65AD\u5185\u5BB9**\uFF1A
32
+ - ESLint \u89C4\u5219\u68C0\u67E5\uFF08error \u548C warning\uFF09
33
+ - vue-tsc \u7C7B\u578B\u68C0\u67E5\uFF08TypeScript \u7C7B\u578B\u9519\u8BEF\u548C\u8B66\u544A\uFF09`,
34
+ args: {
35
+ filePath: tool.schema.string().optional().describe("\u8981\u8BCA\u65AD\u7684\u6587\u4EF6\u8DEF\u5F84\uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u8DEF\u5F84\uFF09\uFF0C\u4E0D\u4F20\u5219\u5168\u91CF\u8BCA\u65AD\u6574\u4E2A\u9879\u76EE")
36
+ },
37
+ async execute(args, context) {
38
+ const { filePath } = args;
39
+ const workspace2 = context.directory;
40
+ if (filePath) {
41
+ const resolved = path.resolve(workspace2, filePath);
42
+ log.debug("run_diagnostics called (single file)", {
43
+ filePath: resolved,
44
+ workspace: workspace2,
45
+ sessionID: context.sessionID
46
+ });
47
+ if (!fs.existsSync(resolved)) {
48
+ return `\u6587\u4EF6\u4E0D\u5B58\u5728: ${resolved}`;
49
+ }
50
+ const { eslintOutput: eslintOutput2, tscOutput: tscOutput2 } = await runAllChecks(resolved, workspace2);
51
+ return formatDiagnosticsSections(
52
+ `\u8BCA\u65AD\u7ED3\u679C: ${path.relative(workspace2, resolved)}`,
53
+ eslintOutput2,
54
+ tscOutput2
55
+ );
56
+ }
57
+ log.debug("run_diagnostics called (full project)", {
58
+ workspace: workspace2,
59
+ sessionID: context.sessionID
60
+ });
61
+ const { eslintOutput, tscOutput } = await runProjectDiagnostics(workspace2);
62
+ return formatDiagnosticsSections("\u5168\u91CF\u8BCA\u65AD\u7ED3\u679C", eslintOutput, tscOutput);
63
+ }
64
+ });
65
+ return {
66
+ "tool.execute.after": async (input, output) => {
67
+ if (!EDIT_TOOLS.has(input.tool)) return;
68
+ if (!isLintEnabled()) return;
69
+ const filePath = input.args?.filePath || "";
70
+ if (!filePath || !isJsFile(filePath)) return;
71
+ log.debug("Executing after hook", {
72
+ tool: input.tool,
73
+ filePath,
74
+ processCwd: workspace,
75
+ lintEnabled: isLintEnabled()
76
+ });
77
+ const { eslintOutput, tscOutput } = await runAllChecks(filePath, workspace);
78
+ const parts = [];
79
+ if (tscOutput.rawOutput.trim()) {
80
+ parts.push("## vue-tsc\n\n" + tscOutput.rawOutput.trim());
81
+ }
82
+ if (eslintOutput.text) {
83
+ parts.push("## ESLint\n\n" + eslintOutput.text);
84
+ }
85
+ const diagText = parts.join("\n\n");
86
+ log.debug("Diagnostics result", {
87
+ filePath,
88
+ eslintError: !!eslintOutput.text,
89
+ tscError: tscOutput.exitCode !== 0
90
+ });
91
+ if (diagText) {
92
+ output.output += "\n\n" + diagText;
93
+ }
94
+ const anyError = !!eslintOutput.text || tscOutput.exitCode !== 0;
95
+ if (anyError) {
96
+ const meta = output.metadata ?? (output.metadata = {});
97
+ const existing = meta.diagnostics ?? (meta.diagnostics = {});
98
+ const diags = [
99
+ ...eslintOutput.diagnostics ?? [],
100
+ ...tscOutput.diagnostics ?? []
101
+ ];
102
+ existing[filePath] = [...existing[filePath] ?? [], ...diags];
103
+ }
104
+ },
105
+ tool: {
106
+ run_diagnostics: runDiagnosticsTool
107
+ }
108
+ };
109
+ }
110
+ };
111
+ export {
112
+ edit_diagnostics_default as default
113
+ };
package/es/provider.js CHANGED
@@ -94,7 +94,6 @@ Please install OpenCode first:
94
94
  contextApiUrl: options.contextApiUrl,
95
95
  logsApiUrl: options.logsApiUrl,
96
96
  logFilesJson: this.opts.logFiles ? JSON.stringify(this.opts.logFiles) : void 0,
97
- enableBlockOnError: this.opts.enableBlockOnError,
98
97
  verbose: options.verbose,
99
98
  enableLsp: this.opts.enableLsp,
100
99
  enablePrettier: this.opts.enablePrettier,
@@ -248,7 +247,6 @@ function resolveOpenCodeOptions(options) {
248
247
  settings: po.settings ?? options.settings,
249
248
  logFiles: options.logFiles,
250
249
  enableLsp: po.enableLsp ?? options.enableLsp,
251
- enableBlockOnError: po.enableBlockOnError ?? options.enableBlockOnError,
252
250
  enablePrettier: po.enablePrettier ?? options.enablePrettier
253
251
  };
254
252
  }
package/es/types.d.ts CHANGED
@@ -79,8 +79,6 @@ export type OpenCodeProviderOptions = {
79
79
  logFiles?: LogFileConfig[];
80
80
  /** 启用 LSP 诊断(TypeScript + ESLint),agent 编辑文件后自动返回错误信息,默认 false */
81
81
  enableLsp?: boolean;
82
- /** 启用 LSP 错误硬阻止:编辑后有错误则回滚文件并拒绝修改,默认 false */
83
- enableBlockOnError?: boolean;
84
82
  /** 启用代码格式化功能(prettier),默认 true */
85
83
  enablePrettier?: boolean;
86
84
  /** 允许 Provider 自定义扩展字段(schema 由具体 Provider 定义) */
@@ -108,8 +106,6 @@ export interface WebOptions {
108
106
  logsApiUrl?: string;
109
107
  /** 日志文件配置(JSON 字符串) */
110
108
  logFilesJson?: string;
111
- /** 启用 LSP 错误硬阻止(环境变量透传给 OpenCode 插件) */
112
- enableBlockOnError?: boolean;
113
109
  /** 启用 verbose 模式(环境变量透传,调试日志输出) */
114
110
  verbose?: boolean;
115
111
  /** 启用 LSP / 质量门禁(环境变量透传,控制 block-on-error 插件运行) */
package/lib/constants.cjs CHANGED
@@ -41,7 +41,6 @@ const DEFAULT_OPENCODE_SETTINGS = {
41
41
  };
42
42
  const DEFAULT_OPENCODE_PROVIDER_OPTIONS = {
43
43
  enableLsp: true,
44
- enableBlockOnError: false,
45
44
  enablePrettier: true
46
45
  };
47
46
  // Annotate the CommonJS export names for ESM import in node:
@@ -36,8 +36,9 @@ var import_fs = __toESM(require("fs"));
36
36
  var import_path = __toESM(require("path"));
37
37
  var import_url = require("url");
38
38
  var import_node = require("@aipanel/core/node");
39
+ const import_meta = {};
39
40
  const require2 = (0, import_node.createPackageRequire)();
40
- const packageDir = (0, import_node.resolvePackageDir)("@aipanel/opencode-plugins");
41
+ const pluginsDir = import_path.default.join(import_path.default.dirname((0, import_url.fileURLToPath)(import_meta.url)), "plugins");
41
42
  const log = (0, import_node.createLogger)("OpenCodeWeb");
42
43
  function prepareOpenCodeRuntime(cwd, vitePort, enableLsp, enablePrettier) {
43
44
  const cacheDir = import_path.default.join(cwd, import_node.AIPANEL_CACHE_DIR, "opencode");
@@ -77,7 +78,6 @@ function startOpenCodeWeb(options) {
77
78
  contextApiUrl,
78
79
  logsApiUrl,
79
80
  logFilesJson,
80
- enableBlockOnError,
81
81
  verbose,
82
82
  enableLsp,
83
83
  vueDevtoolsApiUrl
@@ -89,7 +89,6 @@ function startOpenCodeWeb(options) {
89
89
  contextApiUrl,
90
90
  logsApiUrl,
91
91
  logFilesJson,
92
- enableBlockOnError,
93
92
  verbose,
94
93
  enableLsp
95
94
  });
@@ -99,7 +98,6 @@ function startOpenCodeWeb(options) {
99
98
  contextApiUrl,
100
99
  logsApiUrl,
101
100
  logFilesJson,
102
- enableBlockOnError,
103
101
  verbose,
104
102
  enableLsp,
105
103
  vueDevtoolsApiUrl,
@@ -225,7 +223,7 @@ function resolveFormatBridgePath() {
225
223
  return void 0;
226
224
  }
227
225
  function resolveSourcePluginsDir() {
228
- const candidatePaths = [import_path.default.join(packageDir, "es", "plugins")];
226
+ const candidatePaths = [pluginsDir];
229
227
  for (const candidatePath of candidatePaths) {
230
228
  if (import_fs.default.existsSync(candidatePath)) {
231
229
  return candidatePath;
@@ -233,9 +231,8 @@ function resolveSourcePluginsDir() {
233
231
  }
234
232
  return candidatePaths[0];
235
233
  }
236
- const MIGRATED_TO_MCP_PLUGINS = /* @__PURE__ */ new Set(["vue-devtools.js", "vite-logs.js", "service-logs.js"]);
237
234
  function resolvePluginEntries(sourceDir) {
238
- const files = import_fs.default.readdirSync(sourceDir).filter((f) => f.endsWith(".js") && !MIGRATED_TO_MCP_PLUGINS.has(f));
235
+ const files = import_fs.default.readdirSync(sourceDir).filter((f) => f.endsWith(".js"));
239
236
  const entries = files.map((file) => {
240
237
  const absolutePath = import_path.default.join(sourceDir, file);
241
238
  return (0, import_url.pathToFileURL)(absolutePath).href;
@@ -243,7 +240,7 @@ function resolvePluginEntries(sourceDir) {
243
240
  log.debug("Resolved plugin entries", { count: entries.length, entries });
244
241
  return entries;
245
242
  }
246
- function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFilesJson, enableBlockOnError, verbose, enableLsp, vueDevtoolsApiUrl, workspace) {
243
+ function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFilesJson, verbose, enableLsp, vueDevtoolsApiUrl, workspace) {
247
244
  const env = {
248
245
  ...Object.fromEntries(
249
246
  Object.entries(process.env).filter(([, v]) => v !== void 0)
@@ -268,10 +265,6 @@ function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFile
268
265
  env.OPENCODE_LOG_FILES_JSON = logFilesJson;
269
266
  log.debug("Set OPENCODE_LOG_FILES_JSON", { logFilesJson });
270
267
  }
271
- if (enableBlockOnError) {
272
- env.OPENCODE_BLOCK_ON_ERROR = "1";
273
- log.debug("Set OPENCODE_BLOCK_ON_ERROR=1");
274
- }
275
268
  if (verbose) {
276
269
  env.OPENCODE_VERBOSE = "1";
277
270
  log.debug("Set OPENCODE_VERBOSE=1");
@@ -0,0 +1,135 @@
1
+ var __create = Object.create;
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getProtoOf = Object.getPrototypeOf;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
+ // If the importer is in node compatibility mode or this is not an ESM
21
+ // file that has been converted to a CommonJS file using a Babel-
22
+ // compatible transform (i.e. "__esModule" has not been set), then set
23
+ // "default" to the CommonJS "module.exports" for node compatibility.
24
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
+ mod
26
+ ));
27
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
28
+ var edit_diagnostics_exports = {};
29
+ __export(edit_diagnostics_exports, {
30
+ default: () => edit_diagnostics_default
31
+ });
32
+ module.exports = __toCommonJS(edit_diagnostics_exports);
33
+ var import_node_fs = __toESM(require("node:fs"));
34
+ var import_node_path = __toESM(require("node:path"));
35
+ var import_plugin = require("@opencode-ai/plugin");
36
+ var import_node = require("@aipanel/core/node");
37
+ if (process.env.OPENCODE_VERBOSE === "1") {
38
+ (0, import_node.setVerbose)(true);
39
+ }
40
+ const log = (0, import_node.createLogger)("EditDiagnostics");
41
+ const EDIT_TOOLS = /* @__PURE__ */ new Set(["edit", "write", "apply_patch"]);
42
+ const isLintEnabled = () => process.env.OPENCODE_ENABLE_LINT === "1";
43
+ var edit_diagnostics_default = {
44
+ id: "vite-plugin-aipanel/edit-diagnostics",
45
+ async server() {
46
+ const workspace = process.env.OPENCODE_WORKSPACE || process.cwd();
47
+ const runDiagnosticsTool = (0, import_plugin.tool)({
48
+ description: `\u8FD0\u884C ESLint \u548C vue-tsc \u7C7B\u578B\u68C0\u67E5\uFF0C\u8FD4\u56DE\u8BCA\u65AD\u7ED3\u679C\u3002
49
+
50
+ **\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A
51
+ - \u521A\u5B8C\u6210\u4EE3\u7801\u4FEE\u6539\uFF0C\u60F3\u9A8C\u8BC1\u662F\u5426\u6709 ESLint \u9519\u8BEF\u6216\u7C7B\u578B\u9519\u8BEF
52
+ - \u5728\u63D0\u4EA4\u4EE3\u7801\u524D\u8FDB\u884C\u8D28\u91CF\u68C0\u67E5
53
+ - \u6392\u67E5\u7F16\u8F91\u5668\u672A\u663E\u793A\u4F46\u5B9E\u9645\u5B58\u5728\u7684\u7C7B\u578B\u95EE\u9898
54
+ - \u4E0D\u4F20\u53C2\u6570\u53EF\u5168\u91CF\u8BCA\u65AD\u6574\u4E2A\u9879\u76EE
55
+
56
+ **\u8BCA\u65AD\u5185\u5BB9**\uFF1A
57
+ - ESLint \u89C4\u5219\u68C0\u67E5\uFF08error \u548C warning\uFF09
58
+ - vue-tsc \u7C7B\u578B\u68C0\u67E5\uFF08TypeScript \u7C7B\u578B\u9519\u8BEF\u548C\u8B66\u544A\uFF09`,
59
+ args: {
60
+ filePath: import_plugin.tool.schema.string().optional().describe("\u8981\u8BCA\u65AD\u7684\u6587\u4EF6\u8DEF\u5F84\uFF08\u7EDD\u5BF9\u8DEF\u5F84\u6216\u76F8\u5BF9\u8DEF\u5F84\uFF09\uFF0C\u4E0D\u4F20\u5219\u5168\u91CF\u8BCA\u65AD\u6574\u4E2A\u9879\u76EE")
61
+ },
62
+ async execute(args, context) {
63
+ const { filePath } = args;
64
+ const workspace2 = context.directory;
65
+ if (filePath) {
66
+ const resolved = import_node_path.default.resolve(workspace2, filePath);
67
+ log.debug("run_diagnostics called (single file)", {
68
+ filePath: resolved,
69
+ workspace: workspace2,
70
+ sessionID: context.sessionID
71
+ });
72
+ if (!import_node_fs.default.existsSync(resolved)) {
73
+ return `\u6587\u4EF6\u4E0D\u5B58\u5728: ${resolved}`;
74
+ }
75
+ const { eslintOutput: eslintOutput2, tscOutput: tscOutput2 } = await (0, import_node.runAllChecks)(resolved, workspace2);
76
+ return (0, import_node.formatDiagnosticsSections)(
77
+ `\u8BCA\u65AD\u7ED3\u679C: ${import_node_path.default.relative(workspace2, resolved)}`,
78
+ eslintOutput2,
79
+ tscOutput2
80
+ );
81
+ }
82
+ log.debug("run_diagnostics called (full project)", {
83
+ workspace: workspace2,
84
+ sessionID: context.sessionID
85
+ });
86
+ const { eslintOutput, tscOutput } = await (0, import_node.runProjectDiagnostics)(workspace2);
87
+ return (0, import_node.formatDiagnosticsSections)("\u5168\u91CF\u8BCA\u65AD\u7ED3\u679C", eslintOutput, tscOutput);
88
+ }
89
+ });
90
+ return {
91
+ "tool.execute.after": async (input, output) => {
92
+ if (!EDIT_TOOLS.has(input.tool)) return;
93
+ if (!isLintEnabled()) return;
94
+ const filePath = input.args?.filePath || "";
95
+ if (!filePath || !(0, import_node.isJsFile)(filePath)) return;
96
+ log.debug("Executing after hook", {
97
+ tool: input.tool,
98
+ filePath,
99
+ processCwd: workspace,
100
+ lintEnabled: isLintEnabled()
101
+ });
102
+ const { eslintOutput, tscOutput } = await (0, import_node.runAllChecks)(filePath, workspace);
103
+ const parts = [];
104
+ if (tscOutput.rawOutput.trim()) {
105
+ parts.push("## vue-tsc\n\n" + tscOutput.rawOutput.trim());
106
+ }
107
+ if (eslintOutput.text) {
108
+ parts.push("## ESLint\n\n" + eslintOutput.text);
109
+ }
110
+ const diagText = parts.join("\n\n");
111
+ log.debug("Diagnostics result", {
112
+ filePath,
113
+ eslintError: !!eslintOutput.text,
114
+ tscError: tscOutput.exitCode !== 0
115
+ });
116
+ if (diagText) {
117
+ output.output += "\n\n" + diagText;
118
+ }
119
+ const anyError = !!eslintOutput.text || tscOutput.exitCode !== 0;
120
+ if (anyError) {
121
+ const meta = output.metadata ?? (output.metadata = {});
122
+ const existing = meta.diagnostics ?? (meta.diagnostics = {});
123
+ const diags = [
124
+ ...eslintOutput.diagnostics ?? [],
125
+ ...tscOutput.diagnostics ?? []
126
+ ];
127
+ existing[filePath] = [...existing[filePath] ?? [], ...diags];
128
+ }
129
+ },
130
+ tool: {
131
+ run_diagnostics: runDiagnosticsTool
132
+ }
133
+ };
134
+ }
135
+ };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @fileoverview 编辑后诊断插件
3
+ * @description edit/write 工具执行后:
4
+ * 1. ESLint 检查(Node API)
5
+ * 2. vue-tsc 类型检查(过滤当前文件诊断)
6
+ * 3. 诊断结果追加到工具输出,供 Agent 查看(不做回滚)
7
+ *
8
+ * 诊断引擎(ESLint/vue-tsc/格式化/全量诊断)统一由 @aipanel/core/node 提供,
9
+ * 与 dsh 侧审查工具共用同一实现,保证行为一致。
10
+ */
11
+ import type { Hooks } from "@opencode-ai/plugin";
12
+ declare const _default: {
13
+ id: string;
14
+ server(): Promise<Hooks>;
15
+ };
16
+ export default _default;
package/lib/provider.cjs CHANGED
@@ -125,7 +125,6 @@ Please install OpenCode first:
125
125
  contextApiUrl: options.contextApiUrl,
126
126
  logsApiUrl: options.logsApiUrl,
127
127
  logFilesJson: this.opts.logFiles ? JSON.stringify(this.opts.logFiles) : void 0,
128
- enableBlockOnError: this.opts.enableBlockOnError,
129
128
  verbose: options.verbose,
130
129
  enableLsp: this.opts.enableLsp,
131
130
  enablePrettier: this.opts.enablePrettier,
@@ -279,7 +278,6 @@ function resolveOpenCodeOptions(options) {
279
278
  settings: po.settings ?? options.settings,
280
279
  logFiles: options.logFiles,
281
280
  enableLsp: po.enableLsp ?? options.enableLsp,
282
- enableBlockOnError: po.enableBlockOnError ?? options.enableBlockOnError,
283
281
  enablePrettier: po.enablePrettier ?? options.enablePrettier
284
282
  };
285
283
  }
package/lib/types.d.ts CHANGED
@@ -79,8 +79,6 @@ export type OpenCodeProviderOptions = {
79
79
  logFiles?: LogFileConfig[];
80
80
  /** 启用 LSP 诊断(TypeScript + ESLint),agent 编辑文件后自动返回错误信息,默认 false */
81
81
  enableLsp?: boolean;
82
- /** 启用 LSP 错误硬阻止:编辑后有错误则回滚文件并拒绝修改,默认 false */
83
- enableBlockOnError?: boolean;
84
82
  /** 启用代码格式化功能(prettier),默认 true */
85
83
  enablePrettier?: boolean;
86
84
  /** 允许 Provider 自定义扩展字段(schema 由具体 Provider 定义) */
@@ -108,8 +106,6 @@ export interface WebOptions {
108
106
  logsApiUrl?: string;
109
107
  /** 日志文件配置(JSON 字符串) */
110
108
  logFilesJson?: string;
111
- /** 启用 LSP 错误硬阻止(环境变量透传给 OpenCode 插件) */
112
- enableBlockOnError?: boolean;
113
109
  /** 启用 verbose 模式(环境变量透传,调试日志输出) */
114
110
  verbose?: boolean;
115
111
  /** 启用 LSP / 质量门禁(环境变量透传,控制 block-on-error 插件运行) */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aipanel/provider-opencode",
3
- "version": "1.2.6",
3
+ "version": "1.2.8",
4
4
  "type": "module",
5
5
  "main": "lib/index.cjs",
6
6
  "module": "es/index.js",
@@ -21,9 +21,9 @@
21
21
  "registry": "https://registry.npmjs.org/"
22
22
  },
23
23
  "dependencies": {
24
+ "@opencode-ai/plugin": "^1.18.0",
24
25
  "execa": "^9.6.1",
25
- "@aipanel/opencode-plugins": "1.2.6",
26
- "@aipanel/core": "1.2.6"
26
+ "@aipanel/core": "1.2.8"
27
27
  },
28
28
  "scripts": {
29
29
  "build": "pagoda-cli build",