@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 +0 -1
- package/es/opencode-web.js +6 -15
- package/es/plugins/edit-diagnostics.d.ts +16 -0
- package/es/plugins/edit-diagnostics.js +113 -0
- package/es/provider.js +0 -2
- package/es/types.d.ts +0 -4
- package/lib/constants.cjs +0 -1
- package/lib/opencode-web.cjs +5 -12
- package/lib/plugins/edit-diagnostics.cjs +135 -0
- package/lib/plugins/edit-diagnostics.d.ts +16 -0
- package/lib/provider.cjs +0 -2
- package/lib/types.d.ts +0 -4
- package/package.json +3 -3
package/es/constants.js
CHANGED
package/es/opencode-web.js
CHANGED
|
@@ -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
|
|
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 = [
|
|
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")
|
|
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,
|
|
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
package/lib/opencode-web.cjs
CHANGED
|
@@ -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
|
|
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 = [
|
|
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")
|
|
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,
|
|
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.
|
|
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/
|
|
26
|
-
"@aipanel/core": "1.2.6"
|
|
26
|
+
"@aipanel/core": "1.2.8"
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"build": "pagoda-cli build",
|