@aipanel/provider-opencode 1.3.4 → 1.3.6
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.d.ts +1 -1
- package/es/opencode-web.js +9 -9
- package/es/plugins/edit-diagnostics.js +63 -59
- package/es/provider.js +12 -10
- package/es/types.d.ts +12 -5
- package/lib/constants.cjs +0 -1
- package/lib/opencode-web.cjs +9 -9
- package/lib/opencode-web.d.ts +1 -1
- package/lib/plugins/edit-diagnostics.cjs +58 -55
- package/lib/provider.cjs +11 -9
- package/lib/types.d.ts +12 -5
- package/package.json +2 -2
package/es/constants.js
CHANGED
package/es/opencode-web.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ResultPromise } from "execa";
|
|
2
2
|
import type { WebOptions } from "./types";
|
|
3
|
-
export declare function prepareOpenCodeRuntime(cwd: string, vitePort: number, viteHost: string,
|
|
3
|
+
export declare function prepareOpenCodeRuntime(cwd: string, vitePort: number, viteHost: string, enablePrettier?: boolean): string;
|
|
4
4
|
export declare function startOpenCodeWeb(options: WebOptions): ResultPromise;
|
package/es/opencode-web.js
CHANGED
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
} from "@aipanel/core/node";
|
|
12
12
|
const pluginsDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "plugins");
|
|
13
13
|
const log = createLogger("OpenCodeWeb");
|
|
14
|
-
function prepareOpenCodeRuntime(cwd, vitePort, viteHost,
|
|
14
|
+
function prepareOpenCodeRuntime(cwd, vitePort, viteHost, enablePrettier) {
|
|
15
15
|
const cacheDir = path.join(cwd, AIPANEL_CACHE_DIR, "opencode");
|
|
16
|
-
log.debug("Setting up OpenCode runtime", { cacheDir,
|
|
16
|
+
log.debug("Setting up OpenCode runtime", { cacheDir, enablePrettier });
|
|
17
17
|
if (!fs.existsSync(cacheDir)) {
|
|
18
18
|
fs.mkdirSync(cacheDir, { recursive: true });
|
|
19
19
|
}
|
|
@@ -50,7 +50,7 @@ function startOpenCodeWeb(options) {
|
|
|
50
50
|
logsApiUrl,
|
|
51
51
|
logFilesJson,
|
|
52
52
|
verbose,
|
|
53
|
-
|
|
53
|
+
diagnosticsJson,
|
|
54
54
|
vueDevtoolsApiUrl
|
|
55
55
|
} = options;
|
|
56
56
|
const stateDir = createStateDirectory(cwd);
|
|
@@ -61,7 +61,7 @@ function startOpenCodeWeb(options) {
|
|
|
61
61
|
logsApiUrl,
|
|
62
62
|
logFilesJson,
|
|
63
63
|
verbose,
|
|
64
|
-
|
|
64
|
+
hasDiagnostics: diagnosticsJson !== void 0
|
|
65
65
|
});
|
|
66
66
|
const env = buildProcessEnv(
|
|
67
67
|
stateDir,
|
|
@@ -70,7 +70,7 @@ function startOpenCodeWeb(options) {
|
|
|
70
70
|
logsApiUrl,
|
|
71
71
|
logFilesJson,
|
|
72
72
|
verbose,
|
|
73
|
-
|
|
73
|
+
diagnosticsJson,
|
|
74
74
|
vueDevtoolsApiUrl,
|
|
75
75
|
cwd
|
|
76
76
|
);
|
|
@@ -142,7 +142,7 @@ function resolvePluginEntries(sourceDir) {
|
|
|
142
142
|
log.debug("Resolved plugin entries", { count: entries.length, entries });
|
|
143
143
|
return entries;
|
|
144
144
|
}
|
|
145
|
-
function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFilesJson, verbose,
|
|
145
|
+
function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFilesJson, verbose, diagnosticsJson, vueDevtoolsApiUrl, workspace) {
|
|
146
146
|
const env = {
|
|
147
147
|
...Object.fromEntries(
|
|
148
148
|
Object.entries(process.env).filter(([, v]) => v !== void 0)
|
|
@@ -171,9 +171,9 @@ function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFile
|
|
|
171
171
|
env[OPENCODE_ENV.VERBOSE] = "1";
|
|
172
172
|
log.debug("Set OPENCODE_VERBOSE=1");
|
|
173
173
|
}
|
|
174
|
-
if (
|
|
175
|
-
env[OPENCODE_ENV.
|
|
176
|
-
log.debug("Set
|
|
174
|
+
if (diagnosticsJson) {
|
|
175
|
+
env[OPENCODE_ENV.DIAGNOSTICS] = diagnosticsJson;
|
|
176
|
+
log.debug("Set OPENCODE_DIAGNOSTICS");
|
|
177
177
|
}
|
|
178
178
|
if (vueDevtoolsApiUrl) {
|
|
179
179
|
env[OPENCODE_ENV.VUE_DEVTOOLS_API_URL] = vueDevtoolsApiUrl;
|
|
@@ -4,10 +4,11 @@ import { tool } from "@opencode-ai/plugin";
|
|
|
4
4
|
import {
|
|
5
5
|
setVerbose,
|
|
6
6
|
createLogger,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
collectDiagnostics,
|
|
8
|
+
renderDiagnostics,
|
|
9
|
+
resolveDiagnosticsPolicy,
|
|
10
|
+
runDiagnostics,
|
|
11
|
+
DEFAULT_DIAGNOSTICS_POLICY,
|
|
11
12
|
MUTATING_TOOLS,
|
|
12
13
|
OPENCODE_ENV,
|
|
13
14
|
DIAGNOSTICS_TOOL_DESCRIPTION
|
|
@@ -17,83 +18,86 @@ if (process.env[OPENCODE_ENV.VERBOSE] === "1") {
|
|
|
17
18
|
}
|
|
18
19
|
const log = createLogger("EditDiagnostics");
|
|
19
20
|
const EDIT_TOOLS = MUTATING_TOOLS;
|
|
20
|
-
|
|
21
|
+
function readPolicy() {
|
|
22
|
+
const raw = process.env[OPENCODE_ENV.DIAGNOSTICS];
|
|
23
|
+
let parsed;
|
|
24
|
+
if (raw) {
|
|
25
|
+
try {
|
|
26
|
+
parsed = JSON.parse(raw);
|
|
27
|
+
} catch {
|
|
28
|
+
log.warn("OPENCODE_DIAGNOSTICS is not valid JSON; falling back to defaults");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return resolveDiagnosticsPolicy(
|
|
32
|
+
[DEFAULT_DIAGNOSTICS_POLICY, parsed],
|
|
33
|
+
(message) => log.warn(message)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
function truncate(text, maxChars) {
|
|
37
|
+
if (text.length <= maxChars) return text;
|
|
38
|
+
return `${text.slice(0, maxChars)}
|
|
39
|
+
|
|
40
|
+
\u2026\uFF08\u8BCA\u65AD\u8F93\u51FA\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF1B\u53EF\u8C03\u7528 run_diagnostics \u67E5\u770B\u5B8C\u6574\u7ED3\u679C\uFF09`;
|
|
41
|
+
}
|
|
21
42
|
var edit_diagnostics_default = {
|
|
22
43
|
id: "vite-plugin-aipanel/edit-diagnostics",
|
|
23
44
|
async server() {
|
|
24
45
|
const workspace = process.env[OPENCODE_ENV.WORKSPACE] || process.cwd();
|
|
46
|
+
const policy = readPolicy();
|
|
25
47
|
const runDiagnosticsTool = tool({
|
|
26
48
|
description: DIAGNOSTICS_TOOL_DESCRIPTION,
|
|
27
49
|
args: {
|
|
28
50
|
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")
|
|
29
51
|
},
|
|
30
52
|
async execute(args, context) {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
const { eslintOutput: eslintOutput2, tscOutput: tscOutput2 } = await runAllChecks(resolved, workspace2);
|
|
44
|
-
return formatDiagnosticsSections(
|
|
45
|
-
`\u8BCA\u65AD\u7ED3\u679C: ${path.relative(workspace2, resolved)}`,
|
|
46
|
-
eslintOutput2,
|
|
47
|
-
tscOutput2
|
|
48
|
-
);
|
|
53
|
+
const cwd = context.directory;
|
|
54
|
+
let target;
|
|
55
|
+
let title;
|
|
56
|
+
if (args.filePath) {
|
|
57
|
+
const resolved = path.resolve(cwd, args.filePath);
|
|
58
|
+
if (!fs.existsSync(resolved)) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${resolved}`);
|
|
59
|
+
target = { kind: "file", file: resolved, cwd };
|
|
60
|
+
title = `\u8BCA\u65AD\u7ED3\u679C: ${path.relative(cwd, resolved)}`;
|
|
61
|
+
} else {
|
|
62
|
+
target = { kind: "project", cwd };
|
|
63
|
+
title = "\u5168\u91CF\u8BCA\u65AD\u7ED3\u679C";
|
|
49
64
|
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const { eslintOutput, tscOutput } = await runProjectDiagnostics(workspace2);
|
|
55
|
-
return formatDiagnosticsSections("\u5168\u91CF\u8BCA\u65AD\u7ED3\u679C", eslintOutput, tscOutput);
|
|
65
|
+
const body = renderDiagnostics(await runDiagnostics(target, policy, "manual"));
|
|
66
|
+
return body ? `${title}
|
|
67
|
+
|
|
68
|
+
${body}` : title;
|
|
56
69
|
}
|
|
57
70
|
});
|
|
58
|
-
|
|
71
|
+
const hooks = {
|
|
59
72
|
"tool.execute.after": async (input, output) => {
|
|
60
73
|
if (!EDIT_TOOLS.has(input.tool)) return;
|
|
61
|
-
if (!
|
|
74
|
+
if (!policy.auto) return;
|
|
62
75
|
const filePath = input.args?.filePath || "";
|
|
63
|
-
if (!filePath
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
if (!filePath) return;
|
|
77
|
+
const resolved = path.resolve(workspace, filePath);
|
|
78
|
+
log.debug("Executing after hook", { tool: input.tool, filePath, processCwd: workspace });
|
|
79
|
+
const result = await runDiagnostics(
|
|
80
|
+
{ kind: "edited", files: [resolved], cwd: workspace },
|
|
81
|
+
policy,
|
|
82
|
+
"edit"
|
|
83
|
+
);
|
|
84
|
+
const text = renderDiagnostics(result, {
|
|
85
|
+
onlyFindings: true,
|
|
86
|
+
maxFindingsPerSection: policy.maxFindingsPerSection
|
|
69
87
|
});
|
|
70
|
-
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
});
|
|
74
|
-
log.debug("Diagnostics result", {
|
|
75
|
-
filePath,
|
|
76
|
-
eslintError: !!eslintOutput.text,
|
|
77
|
-
tscError: tscOutput.exitCode !== 0
|
|
78
|
-
});
|
|
79
|
-
if (diagText) {
|
|
80
|
-
output.output += "\n\n" + diagText;
|
|
81
|
-
}
|
|
82
|
-
const anyError = !!eslintOutput.text || tscOutput.exitCode !== 0;
|
|
83
|
-
if (anyError) {
|
|
88
|
+
if (text) output.output += "\n\n" + truncate(text, policy.maxMessageChars);
|
|
89
|
+
const items = collectDiagnostics(result);
|
|
90
|
+
if (items.length > 0) {
|
|
84
91
|
const meta = output.metadata ?? (output.metadata = {});
|
|
85
92
|
const existing = meta.diagnostics ?? (meta.diagnostics = {});
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
...
|
|
89
|
-
|
|
90
|
-
existing[filePath] = [...existing[filePath] ?? [], ...diags];
|
|
93
|
+
for (const item of items) {
|
|
94
|
+
const key = item.file ?? resolved;
|
|
95
|
+
existing[key] = [...existing[key] ?? [], item];
|
|
96
|
+
}
|
|
91
97
|
}
|
|
92
|
-
},
|
|
93
|
-
tool: {
|
|
94
|
-
run_diagnostics: runDiagnosticsTool
|
|
95
98
|
}
|
|
96
99
|
};
|
|
100
|
+
return policy.exposeTool ? { ...hooks, tool: { run_diagnostics: runDiagnosticsTool } } : hooks;
|
|
97
101
|
}
|
|
98
102
|
};
|
|
99
103
|
export {
|
package/es/provider.js
CHANGED
|
@@ -2,7 +2,7 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
import http from "http";
|
|
5
|
-
import { RETRY_DELAY } from "@aipanel/core";
|
|
5
|
+
import { DEFAULT_DIAGNOSTICS_POLICY, RETRY_DELAY, resolveDiagnosticsPolicy } from "@aipanel/core";
|
|
6
6
|
import { createLogger } from "@aipanel/core/node";
|
|
7
7
|
import { DEFAULT_OPENCODE_PROVIDER_OPTIONS } from "./constants.js";
|
|
8
8
|
import { OpenCodeAPI } from "./api.js";
|
|
@@ -78,7 +78,6 @@ Please install OpenCode first:
|
|
|
78
78
|
options.cwd,
|
|
79
79
|
options.vitePort,
|
|
80
80
|
options.viteHost,
|
|
81
|
-
this.opts.enableLsp,
|
|
82
81
|
this.opts.enablePrettier
|
|
83
82
|
);
|
|
84
83
|
log.debug("Starting OpenCode Web process", {
|
|
@@ -97,7 +96,8 @@ Please install OpenCode first:
|
|
|
97
96
|
logsApiUrl: options.logsApiUrl,
|
|
98
97
|
logFilesJson: this.opts.logFiles ? JSON.stringify(this.opts.logFiles) : void 0,
|
|
99
98
|
verbose: options.verbose,
|
|
100
|
-
|
|
99
|
+
// 诊断策略随 env 下发给 opencode 插件(单一来源;插件侧解析成 DiagnosticsPolicy)
|
|
100
|
+
diagnosticsJson: JSON.stringify(this.opts.diagnostics),
|
|
101
101
|
enablePrettier: this.opts.enablePrettier,
|
|
102
102
|
vueDevtoolsApiUrl: options.vueDevtoolsApiUrl
|
|
103
103
|
});
|
|
@@ -241,15 +241,17 @@ function toChatSession(s) {
|
|
|
241
241
|
};
|
|
242
242
|
}
|
|
243
243
|
function resolveOpenCodeOptions(options) {
|
|
244
|
-
|
|
245
|
-
const po = options.providerOptions ?? {};
|
|
244
|
+
const po = options?.providerOptions ?? {};
|
|
246
245
|
return {
|
|
247
246
|
...DEFAULT_OPENCODE_PROVIDER_OPTIONS,
|
|
248
|
-
language: po.language ?? options
|
|
249
|
-
settings: po.settings ?? options
|
|
250
|
-
logFiles: options
|
|
251
|
-
|
|
252
|
-
|
|
247
|
+
language: po.language ?? options?.language,
|
|
248
|
+
settings: po.settings ?? options?.settings,
|
|
249
|
+
logFiles: options?.logFiles,
|
|
250
|
+
enablePrettier: po.enablePrettier ?? options?.enablePrettier,
|
|
251
|
+
diagnostics: resolveDiagnosticsPolicy(
|
|
252
|
+
[DEFAULT_DIAGNOSTICS_POLICY, po.diagnostics],
|
|
253
|
+
(message) => log.warn(message)
|
|
254
|
+
)
|
|
253
255
|
};
|
|
254
256
|
}
|
|
255
257
|
export {
|
package/es/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* OpenCode Provider 专属类型
|
|
3
3
|
* 所有与 OpenCode Web 绑定的类型自包含于此,核心层不感知。
|
|
4
4
|
*/
|
|
5
|
-
import type { LogFileConfig } from "@aipanel/core";
|
|
5
|
+
import type { DiagnosticsPolicy, LogFileConfig } from "@aipanel/core";
|
|
6
6
|
/**
|
|
7
7
|
* OpenCode 界面语言选项
|
|
8
8
|
*/
|
|
@@ -83,13 +83,20 @@ export type OpenCodeProviderOptions = {
|
|
|
83
83
|
settings?: OpenCodeSettings;
|
|
84
84
|
/** 自定义日志文件配置 */
|
|
85
85
|
logFiles?: LogFileConfig[];
|
|
86
|
-
/**
|
|
87
|
-
|
|
86
|
+
/**
|
|
87
|
+
* 诊断配置(检查来源 + 触发 + 投递;契约见 @aipanel/core 的 DiagnosticsPolicy)。
|
|
88
|
+
* 用户只写要覆盖的字段,provider 归一化成完整策略后随 env 下发。
|
|
89
|
+
*/
|
|
90
|
+
diagnostics?: Partial<DiagnosticsPolicy>;
|
|
88
91
|
/** 启用代码格式化功能(prettier),默认 true */
|
|
89
92
|
enablePrettier?: boolean;
|
|
90
93
|
/** 允许 Provider 自定义扩展字段(schema 由具体 Provider 定义) */
|
|
91
94
|
[key: string]: unknown;
|
|
92
95
|
};
|
|
96
|
+
/** 归一化后的 Provider 选项:诊断策略一定是完整的 */
|
|
97
|
+
export type OpenCodeResolvedOptions = OpenCodeProviderOptions & {
|
|
98
|
+
diagnostics: DiagnosticsPolicy;
|
|
99
|
+
};
|
|
93
100
|
/**
|
|
94
101
|
* OpenCode Web 服务启动选项(进程管理内部类型)
|
|
95
102
|
*/
|
|
@@ -114,8 +121,8 @@ export interface WebOptions {
|
|
|
114
121
|
logFilesJson?: string;
|
|
115
122
|
/** 启用 verbose 模式(环境变量透传,调试日志输出) */
|
|
116
123
|
verbose?: boolean;
|
|
117
|
-
/**
|
|
118
|
-
|
|
124
|
+
/** 诊断策略 JSON(DiagnosticsPolicy):随 env 下发给 opencode 插件 */
|
|
125
|
+
diagnosticsJson?: string;
|
|
119
126
|
/** 启用代码格式化功能(prettier) */
|
|
120
127
|
enablePrettier?: boolean;
|
|
121
128
|
/** Vue DevTools API 地址(环境变量透传给 OpenCode 插件) */
|
package/lib/constants.cjs
CHANGED
package/lib/opencode-web.cjs
CHANGED
|
@@ -39,9 +39,9 @@ var import_node = require("@aipanel/core/node");
|
|
|
39
39
|
const import_meta = {};
|
|
40
40
|
const pluginsDir = import_path.default.join(import_path.default.dirname((0, import_url.fileURLToPath)(import_meta.url)), "plugins");
|
|
41
41
|
const log = (0, import_node.createLogger)("OpenCodeWeb");
|
|
42
|
-
function prepareOpenCodeRuntime(cwd, vitePort, viteHost,
|
|
42
|
+
function prepareOpenCodeRuntime(cwd, vitePort, viteHost, enablePrettier) {
|
|
43
43
|
const cacheDir = import_path.default.join(cwd, import_node.AIPANEL_CACHE_DIR, "opencode");
|
|
44
|
-
log.debug("Setting up OpenCode runtime", { cacheDir,
|
|
44
|
+
log.debug("Setting up OpenCode runtime", { cacheDir, enablePrettier });
|
|
45
45
|
if (!import_fs.default.existsSync(cacheDir)) {
|
|
46
46
|
import_fs.default.mkdirSync(cacheDir, { recursive: true });
|
|
47
47
|
}
|
|
@@ -78,7 +78,7 @@ function startOpenCodeWeb(options) {
|
|
|
78
78
|
logsApiUrl,
|
|
79
79
|
logFilesJson,
|
|
80
80
|
verbose,
|
|
81
|
-
|
|
81
|
+
diagnosticsJson,
|
|
82
82
|
vueDevtoolsApiUrl
|
|
83
83
|
} = options;
|
|
84
84
|
const stateDir = createStateDirectory(cwd);
|
|
@@ -89,7 +89,7 @@ function startOpenCodeWeb(options) {
|
|
|
89
89
|
logsApiUrl,
|
|
90
90
|
logFilesJson,
|
|
91
91
|
verbose,
|
|
92
|
-
|
|
92
|
+
hasDiagnostics: diagnosticsJson !== void 0
|
|
93
93
|
});
|
|
94
94
|
const env = buildProcessEnv(
|
|
95
95
|
stateDir,
|
|
@@ -98,7 +98,7 @@ function startOpenCodeWeb(options) {
|
|
|
98
98
|
logsApiUrl,
|
|
99
99
|
logFilesJson,
|
|
100
100
|
verbose,
|
|
101
|
-
|
|
101
|
+
diagnosticsJson,
|
|
102
102
|
vueDevtoolsApiUrl,
|
|
103
103
|
cwd
|
|
104
104
|
);
|
|
@@ -170,7 +170,7 @@ function resolvePluginEntries(sourceDir) {
|
|
|
170
170
|
log.debug("Resolved plugin entries", { count: entries.length, entries });
|
|
171
171
|
return entries;
|
|
172
172
|
}
|
|
173
|
-
function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFilesJson, verbose,
|
|
173
|
+
function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFilesJson, verbose, diagnosticsJson, vueDevtoolsApiUrl, workspace) {
|
|
174
174
|
const env = {
|
|
175
175
|
...Object.fromEntries(
|
|
176
176
|
Object.entries(process.env).filter(([, v]) => v !== void 0)
|
|
@@ -199,9 +199,9 @@ function buildProcessEnv(stateDir, configDir, contextApiUrl, logsApiUrl, logFile
|
|
|
199
199
|
env[import_node.OPENCODE_ENV.VERBOSE] = "1";
|
|
200
200
|
log.debug("Set OPENCODE_VERBOSE=1");
|
|
201
201
|
}
|
|
202
|
-
if (
|
|
203
|
-
env[import_node.OPENCODE_ENV.
|
|
204
|
-
log.debug("Set
|
|
202
|
+
if (diagnosticsJson) {
|
|
203
|
+
env[import_node.OPENCODE_ENV.DIAGNOSTICS] = diagnosticsJson;
|
|
204
|
+
log.debug("Set OPENCODE_DIAGNOSTICS");
|
|
205
205
|
}
|
|
206
206
|
if (vueDevtoolsApiUrl) {
|
|
207
207
|
env[import_node.OPENCODE_ENV.VUE_DEVTOOLS_API_URL] = vueDevtoolsApiUrl;
|
package/lib/opencode-web.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ResultPromise } from "execa";
|
|
2
2
|
import type { WebOptions } from "./types";
|
|
3
|
-
export declare function prepareOpenCodeRuntime(cwd: string, vitePort: number, viteHost: string,
|
|
3
|
+
export declare function prepareOpenCodeRuntime(cwd: string, vitePort: number, viteHost: string, enablePrettier?: boolean): string;
|
|
4
4
|
export declare function startOpenCodeWeb(options: WebOptions): ResultPromise;
|
|
@@ -39,82 +39,85 @@ if (process.env[import_node.OPENCODE_ENV.VERBOSE] === "1") {
|
|
|
39
39
|
}
|
|
40
40
|
const log = (0, import_node.createLogger)("EditDiagnostics");
|
|
41
41
|
const EDIT_TOOLS = import_node.MUTATING_TOOLS;
|
|
42
|
-
|
|
42
|
+
function readPolicy() {
|
|
43
|
+
const raw = process.env[import_node.OPENCODE_ENV.DIAGNOSTICS];
|
|
44
|
+
let parsed;
|
|
45
|
+
if (raw) {
|
|
46
|
+
try {
|
|
47
|
+
parsed = JSON.parse(raw);
|
|
48
|
+
} catch {
|
|
49
|
+
log.warn("OPENCODE_DIAGNOSTICS is not valid JSON; falling back to defaults");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return (0, import_node.resolveDiagnosticsPolicy)(
|
|
53
|
+
[import_node.DEFAULT_DIAGNOSTICS_POLICY, parsed],
|
|
54
|
+
(message) => log.warn(message)
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
function truncate(text, maxChars) {
|
|
58
|
+
if (text.length <= maxChars) return text;
|
|
59
|
+
return `${text.slice(0, maxChars)}
|
|
60
|
+
|
|
61
|
+
\u2026\uFF08\u8BCA\u65AD\u8F93\u51FA\u8FC7\u957F\uFF0C\u5DF2\u622A\u65AD\uFF1B\u53EF\u8C03\u7528 run_diagnostics \u67E5\u770B\u5B8C\u6574\u7ED3\u679C\uFF09`;
|
|
62
|
+
}
|
|
43
63
|
var edit_diagnostics_default = {
|
|
44
64
|
id: "vite-plugin-aipanel/edit-diagnostics",
|
|
45
65
|
async server() {
|
|
46
66
|
const workspace = process.env[import_node.OPENCODE_ENV.WORKSPACE] || process.cwd();
|
|
67
|
+
const policy = readPolicy();
|
|
47
68
|
const runDiagnosticsTool = (0, import_plugin.tool)({
|
|
48
69
|
description: import_node.DIAGNOSTICS_TOOL_DESCRIPTION,
|
|
49
70
|
args: {
|
|
50
71
|
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")
|
|
51
72
|
},
|
|
52
73
|
async execute(args, context) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
}
|
|
65
|
-
const { eslintOutput: eslintOutput2, tscOutput: tscOutput2 } = await (0, import_node.runAllChecks)(resolved, workspace2);
|
|
66
|
-
return (0, import_node.formatDiagnosticsSections)(
|
|
67
|
-
`\u8BCA\u65AD\u7ED3\u679C: ${import_node_path.default.relative(workspace2, resolved)}`,
|
|
68
|
-
eslintOutput2,
|
|
69
|
-
tscOutput2
|
|
70
|
-
);
|
|
74
|
+
const cwd = context.directory;
|
|
75
|
+
let target;
|
|
76
|
+
let title;
|
|
77
|
+
if (args.filePath) {
|
|
78
|
+
const resolved = import_node_path.default.resolve(cwd, args.filePath);
|
|
79
|
+
if (!import_node_fs.default.existsSync(resolved)) throw new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728: ${resolved}`);
|
|
80
|
+
target = { kind: "file", file: resolved, cwd };
|
|
81
|
+
title = `\u8BCA\u65AD\u7ED3\u679C: ${import_node_path.default.relative(cwd, resolved)}`;
|
|
82
|
+
} else {
|
|
83
|
+
target = { kind: "project", cwd };
|
|
84
|
+
title = "\u5168\u91CF\u8BCA\u65AD\u7ED3\u679C";
|
|
71
85
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const { eslintOutput, tscOutput } = await (0, import_node.runProjectDiagnostics)(workspace2);
|
|
77
|
-
return (0, import_node.formatDiagnosticsSections)("\u5168\u91CF\u8BCA\u65AD\u7ED3\u679C", eslintOutput, tscOutput);
|
|
86
|
+
const body = (0, import_node.renderDiagnostics)(await (0, import_node.runDiagnostics)(target, policy, "manual"));
|
|
87
|
+
return body ? `${title}
|
|
88
|
+
|
|
89
|
+
${body}` : title;
|
|
78
90
|
}
|
|
79
91
|
});
|
|
80
|
-
|
|
92
|
+
const hooks = {
|
|
81
93
|
"tool.execute.after": async (input, output) => {
|
|
82
94
|
if (!EDIT_TOOLS.has(input.tool)) return;
|
|
83
|
-
if (!
|
|
95
|
+
if (!policy.auto) return;
|
|
84
96
|
const filePath = input.args?.filePath || "";
|
|
85
|
-
if (!filePath
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
if (!filePath) return;
|
|
98
|
+
const resolved = import_node_path.default.resolve(workspace, filePath);
|
|
99
|
+
log.debug("Executing after hook", { tool: input.tool, filePath, processCwd: workspace });
|
|
100
|
+
const result = await (0, import_node.runDiagnostics)(
|
|
101
|
+
{ kind: "edited", files: [resolved], cwd: workspace },
|
|
102
|
+
policy,
|
|
103
|
+
"edit"
|
|
104
|
+
);
|
|
105
|
+
const text = (0, import_node.renderDiagnostics)(result, {
|
|
106
|
+
onlyFindings: true,
|
|
107
|
+
maxFindingsPerSection: policy.maxFindingsPerSection
|
|
91
108
|
});
|
|
92
|
-
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
});
|
|
96
|
-
log.debug("Diagnostics result", {
|
|
97
|
-
filePath,
|
|
98
|
-
eslintError: !!eslintOutput.text,
|
|
99
|
-
tscError: tscOutput.exitCode !== 0
|
|
100
|
-
});
|
|
101
|
-
if (diagText) {
|
|
102
|
-
output.output += "\n\n" + diagText;
|
|
103
|
-
}
|
|
104
|
-
const anyError = !!eslintOutput.text || tscOutput.exitCode !== 0;
|
|
105
|
-
if (anyError) {
|
|
109
|
+
if (text) output.output += "\n\n" + truncate(text, policy.maxMessageChars);
|
|
110
|
+
const items = (0, import_node.collectDiagnostics)(result);
|
|
111
|
+
if (items.length > 0) {
|
|
106
112
|
const meta = output.metadata ?? (output.metadata = {});
|
|
107
113
|
const existing = meta.diagnostics ?? (meta.diagnostics = {});
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
...
|
|
111
|
-
|
|
112
|
-
existing[filePath] = [...existing[filePath] ?? [], ...diags];
|
|
114
|
+
for (const item of items) {
|
|
115
|
+
const key = item.file ?? resolved;
|
|
116
|
+
existing[key] = [...existing[key] ?? [], item];
|
|
117
|
+
}
|
|
113
118
|
}
|
|
114
|
-
},
|
|
115
|
-
tool: {
|
|
116
|
-
run_diagnostics: runDiagnosticsTool
|
|
117
119
|
}
|
|
118
120
|
};
|
|
121
|
+
return policy.exposeTool ? { ...hooks, tool: { run_diagnostics: runDiagnosticsTool } } : hooks;
|
|
119
122
|
}
|
|
120
123
|
};
|
package/lib/provider.cjs
CHANGED
|
@@ -110,7 +110,6 @@ Please install OpenCode first:
|
|
|
110
110
|
options.cwd,
|
|
111
111
|
options.vitePort,
|
|
112
112
|
options.viteHost,
|
|
113
|
-
this.opts.enableLsp,
|
|
114
113
|
this.opts.enablePrettier
|
|
115
114
|
);
|
|
116
115
|
log.debug("Starting OpenCode Web process", {
|
|
@@ -129,7 +128,8 @@ Please install OpenCode first:
|
|
|
129
128
|
logsApiUrl: options.logsApiUrl,
|
|
130
129
|
logFilesJson: this.opts.logFiles ? JSON.stringify(this.opts.logFiles) : void 0,
|
|
131
130
|
verbose: options.verbose,
|
|
132
|
-
|
|
131
|
+
// 诊断策略随 env 下发给 opencode 插件(单一来源;插件侧解析成 DiagnosticsPolicy)
|
|
132
|
+
diagnosticsJson: JSON.stringify(this.opts.diagnostics),
|
|
133
133
|
enablePrettier: this.opts.enablePrettier,
|
|
134
134
|
vueDevtoolsApiUrl: options.vueDevtoolsApiUrl
|
|
135
135
|
});
|
|
@@ -273,15 +273,17 @@ function toChatSession(s) {
|
|
|
273
273
|
};
|
|
274
274
|
}
|
|
275
275
|
function resolveOpenCodeOptions(options) {
|
|
276
|
-
|
|
277
|
-
const po = options.providerOptions ?? {};
|
|
276
|
+
const po = options?.providerOptions ?? {};
|
|
278
277
|
return {
|
|
279
278
|
...import_constants.DEFAULT_OPENCODE_PROVIDER_OPTIONS,
|
|
280
|
-
language: po.language ?? options
|
|
281
|
-
settings: po.settings ?? options
|
|
282
|
-
logFiles: options
|
|
283
|
-
|
|
284
|
-
|
|
279
|
+
language: po.language ?? options?.language,
|
|
280
|
+
settings: po.settings ?? options?.settings,
|
|
281
|
+
logFiles: options?.logFiles,
|
|
282
|
+
enablePrettier: po.enablePrettier ?? options?.enablePrettier,
|
|
283
|
+
diagnostics: (0, import_core.resolveDiagnosticsPolicy)(
|
|
284
|
+
[import_core.DEFAULT_DIAGNOSTICS_POLICY, po.diagnostics],
|
|
285
|
+
(message) => log.warn(message)
|
|
286
|
+
)
|
|
285
287
|
};
|
|
286
288
|
}
|
|
287
289
|
// Annotate the CommonJS export names for ESM import in node:
|
package/lib/types.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* OpenCode Provider 专属类型
|
|
3
3
|
* 所有与 OpenCode Web 绑定的类型自包含于此,核心层不感知。
|
|
4
4
|
*/
|
|
5
|
-
import type { LogFileConfig } from "@aipanel/core";
|
|
5
|
+
import type { DiagnosticsPolicy, LogFileConfig } from "@aipanel/core";
|
|
6
6
|
/**
|
|
7
7
|
* OpenCode 界面语言选项
|
|
8
8
|
*/
|
|
@@ -83,13 +83,20 @@ export type OpenCodeProviderOptions = {
|
|
|
83
83
|
settings?: OpenCodeSettings;
|
|
84
84
|
/** 自定义日志文件配置 */
|
|
85
85
|
logFiles?: LogFileConfig[];
|
|
86
|
-
/**
|
|
87
|
-
|
|
86
|
+
/**
|
|
87
|
+
* 诊断配置(检查来源 + 触发 + 投递;契约见 @aipanel/core 的 DiagnosticsPolicy)。
|
|
88
|
+
* 用户只写要覆盖的字段,provider 归一化成完整策略后随 env 下发。
|
|
89
|
+
*/
|
|
90
|
+
diagnostics?: Partial<DiagnosticsPolicy>;
|
|
88
91
|
/** 启用代码格式化功能(prettier),默认 true */
|
|
89
92
|
enablePrettier?: boolean;
|
|
90
93
|
/** 允许 Provider 自定义扩展字段(schema 由具体 Provider 定义) */
|
|
91
94
|
[key: string]: unknown;
|
|
92
95
|
};
|
|
96
|
+
/** 归一化后的 Provider 选项:诊断策略一定是完整的 */
|
|
97
|
+
export type OpenCodeResolvedOptions = OpenCodeProviderOptions & {
|
|
98
|
+
diagnostics: DiagnosticsPolicy;
|
|
99
|
+
};
|
|
93
100
|
/**
|
|
94
101
|
* OpenCode Web 服务启动选项(进程管理内部类型)
|
|
95
102
|
*/
|
|
@@ -114,8 +121,8 @@ export interface WebOptions {
|
|
|
114
121
|
logFilesJson?: string;
|
|
115
122
|
/** 启用 verbose 模式(环境变量透传,调试日志输出) */
|
|
116
123
|
verbose?: boolean;
|
|
117
|
-
/**
|
|
118
|
-
|
|
124
|
+
/** 诊断策略 JSON(DiagnosticsPolicy):随 env 下发给 opencode 插件 */
|
|
125
|
+
diagnosticsJson?: string;
|
|
119
126
|
/** 启用代码格式化功能(prettier) */
|
|
120
127
|
enablePrettier?: boolean;
|
|
121
128
|
/** Vue DevTools API 地址(环境变量透传给 OpenCode 插件) */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aipanel/provider-opencode",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "lib/index.cjs",
|
|
6
6
|
"module": "es/index.js",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"vitest": "^4.1.0"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@aipanel/core": "1.3.
|
|
27
|
+
"@aipanel/core": "1.3.6",
|
|
28
28
|
"@opencode-ai/plugin": "^1.18.0",
|
|
29
29
|
"execa": "^9.6.1"
|
|
30
30
|
},
|