feihong-code 7.2.0 → 7.6.0
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/.github/workflows/ci.yml +54 -1
- package/.github/workflows/release.yml +30 -0
- package/.github/workflows/swebench-real.yml +84 -0
- package/CHANGELOG.md +41 -39
- package/CONTRIBUTING.md +9 -2
- package/README.md +56 -6
- package/README.self-evolve.md +279 -274
- package/dist/agent/change-manager.js +1 -1
- package/dist/agent/code-rag.js +1 -1
- package/dist/agent/code-review.js +1 -1
- package/dist/agent/code-writer.js +1 -1
- package/dist/agent/completion-engine.js +76 -45
- package/dist/agent/completion-engine.js.map +1 -1
- package/dist/agent/completion-postprocess.js +109 -0
- package/dist/agent/completion-postprocess.js.map +1 -0
- package/dist/agent/context-budget.js +121 -0
- package/dist/agent/context-budget.js.map +1 -0
- package/dist/agent/context-compactor.js +58 -0
- package/dist/agent/context-compactor.js.map +1 -1
- package/dist/agent/experience.js +1 -1
- package/dist/agent/layered-memory.js +1 -1
- package/dist/agent/lint.js +121 -0
- package/dist/agent/lint.js.map +1 -0
- package/dist/agent/orchestrator.js +106 -31
- package/dist/agent/orchestrator.js.map +1 -1
- package/dist/agent/parallel-orchestrator.js +1 -1
- package/dist/agent/planner.js +8 -3
- package/dist/agent/planner.js.map +1 -1
- package/dist/agent/prompts.js +55 -55
- package/dist/agent/quality-gate.js +1 -1
- package/dist/agent/repo-context.js +1 -1
- package/dist/agent/repo-reader.js +1 -1
- package/dist/agent/repo-underwriter.js +1 -1
- package/dist/agent/self-correction.js +25 -25
- package/dist/agent/self-heal.js +32 -27
- package/dist/agent/self-heal.js.map +1 -1
- package/dist/agent/self-improver.js +1 -1
- package/dist/agent/subagent-summary.js +1 -1
- package/dist/agent/subagent.js +1 -1
- package/dist/agent/swe-agent.js +1 -1
- package/dist/agent/swe-verifier.js +1 -1
- package/dist/agent/symbol-index.js +1 -1
- package/dist/agent/team.js +1 -1
- package/dist/agent/type-checker.js +1 -1
- package/dist/cli/commands.js +16 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/index.js +66 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/multi-pane-tui.js +217 -0
- package/dist/cli/multi-pane-tui.js.map +1 -0
- package/dist/cli/repl.js +1 -1
- package/dist/cli/run.js +168 -42
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/self-evolve-cli.js +168 -0
- package/dist/cli/tui-run.js +79 -0
- package/dist/cli/tui-run.js.map +1 -0
- package/dist/cli/tui.js +1 -1
- package/dist/cli/tui.js.map +1 -1
- package/dist/cli/version.js +3 -3
- package/dist/cli/version.js.map +1 -1
- package/dist/enterprise/audit.js +1 -1
- package/dist/enterprise/index.js +1 -1
- package/dist/enterprise/policy.js +1 -1
- package/dist/enterprise/quota.js +1 -1
- package/dist/enterprise/tenant.js +1 -1
- package/dist/harness/executor.js +1 -1
- package/dist/harness/index.js +1 -1
- package/dist/harness/loader.js +1 -1
- package/dist/harness/types.js +1 -1
- package/dist/harness/verifier.js +52 -12
- package/dist/harness/verifier.js.map +1 -1
- package/dist/integrations/feishu-bridge.js +348 -0
- package/dist/integrations/feishu-bridge.js.map +1 -0
- package/dist/integrations/github-mcp.js +137 -0
- package/dist/integrations/github-mcp.js.map +1 -0
- package/dist/integrations/wechat-bridge.js +499 -0
- package/dist/integrations/wechat-bridge.js.map +1 -0
- package/dist/integrations/yuanbao-bridge.js +326 -0
- package/dist/integrations/yuanbao-bridge.js.map +1 -0
- package/dist/lsp/lsp-client.js +183 -0
- package/dist/lsp/lsp-client.js.map +1 -0
- package/dist/lsp/lsp-service.js +159 -0
- package/dist/lsp/lsp-service.js.map +1 -0
- package/dist/models/model-router.js +1 -1
- package/dist/models/model.dto.js +1 -1
- package/dist/models/providers/ollama.provider.js +1 -1
- package/dist/models/providers/openai-compatible.provider.js +1 -1
- package/dist/plugins/manager.js +50 -2
- package/dist/plugins/manager.js.map +1 -1
- package/dist/plugins/plugin-loader.js +1 -1
- package/dist/runtime/event-log.js +1 -1
- package/dist/runtime/git.js +1 -1
- package/dist/runtime/hooks.js +71 -4
- package/dist/runtime/hooks.js.map +1 -1
- package/dist/runtime/session-persist.js +1 -1
- package/dist/runtime/worktree.js +1 -1
- package/dist/self-evolve/manager.js +517 -401
- package/dist/self-evolve/self-heal-scheduler.js +160 -0
- package/dist/self-evolve/self-heal-scheduler.js.map +1 -0
- package/dist/shared/concurrency.js +1 -1
- package/dist/shared/config.js +4 -2
- package/dist/shared/config.js.map +1 -1
- package/dist/shared/errors.js +1 -1
- package/dist/shared/i18n.js +109 -101
- package/dist/shared/i18n.js.map +1 -1
- package/dist/shared/secure-store.js +12 -5
- package/dist/shared/secure-store.js.map +1 -1
- package/dist/shared/types.js +1 -1
- package/dist/skills/goal.js +1 -1
- package/dist/skills/grill.js +1 -1
- package/dist/skills/plan.js +1 -1
- package/dist/skills/pua-hooks.js +273 -0
- package/dist/skills/pua-hooks.js.map +1 -0
- package/dist/skills/self-heal.js +9 -3
- package/dist/skills/self-heal.js.map +1 -1
- package/dist/skills/skill-loader.js +1 -1
- package/dist/skills/skill-market.js +84 -8
- package/dist/skills/skill-market.js.map +1 -1
- package/dist/tools/analysis/code-analyzer.js +1 -1
- package/dist/tools/file/edit.tool.js +18 -3
- package/dist/tools/file/edit.tool.js.map +1 -1
- package/dist/tools/file/list.tool.js +1 -1
- package/dist/tools/file/read.tool.js +1 -1
- package/dist/tools/file/write.tool.js +1 -1
- package/dist/tools/generator/code-generator.js +41 -41
- package/dist/tools/generator/test-generator.js +10 -10
- package/dist/tools/index.js +1 -1
- package/dist/tools/mcp/index.js +1 -1
- package/dist/tools/mcp/mcp-client.js +1 -1
- package/dist/tools/safe-path.js +1 -1
- package/dist/tools/search/grep.tool.js +1 -1
- package/dist/tools/shell/exec.js +24 -2
- package/dist/tools/shell/exec.js.map +1 -1
- package/dist/tools/shell/run-shell.tool.js +1 -1
- package/dist/tools/skills/load-skill.tool.js +1 -1
- package/dist/tools/tool.registry.js +10 -1
- package/dist/tools/tool.registry.js.map +1 -1
- package/dist/tools/verify/build-check.tool.js +1 -1
- package/dist/tools/verify/test-run.tool.js +1 -1
- package/dist/tools/web/web.tool.js +1 -1
- package/dist/voice/voice-programming.js +32 -6
- package/dist/voice/voice-programming.js.map +1 -1
- package/dist/web/auth.js +34 -2
- package/dist/web/auth.js.map +1 -1
- package/dist/web/channels.js +1 -1
- package/dist/web/extra-apis.js +440 -0
- package/dist/web/extra-apis.js.map +1 -0
- package/dist/web/public/css/style.css +99 -72
- package/dist/web/public/index.html +44 -1
- package/dist/web/public/js/api.js +64 -2
- package/dist/web/public/js/app.js +151 -128
- package/dist/web/public/js/mock-api.js +25 -0
- package/dist/web/public/js/monaco-editor.js +161 -1
- package/dist/web/public/js/ui.js +292 -56
- package/dist/web/public/service-worker.js +5 -29
- package/dist/web/server.js +178 -61
- package/dist/web/server.js.map +1 -1
- package/dist/web/task-queue.js +6 -21
- package/dist/web/task-queue.js.map +1 -1
- package/docs/App/344/275/277/347/224/250/350/257/264/346/230/216/344/271/246.md +1 -1
- package/docs/App/346/212/200/346/234/257/350/257/264/346/230/216/344/271/246.md +1 -1
- package/docs/BENCHMARK_REPORT_en.md +62 -5
- package/docs/BENCHMARK_REPORT_zh.md +66 -9
- package/docs/DPA.md +65 -0
- package/docs/DPA/346/225/260/346/215/256/345/244/204/347/220/206/345/215/217/350/256/256/346/250/241/346/235/277_v1.0.md +69 -0
- package/docs/Deployment_Guide_EN.md +1 -1
- package/docs/SECURITY_WHITEPAPER.md +74 -0
- package/docs/SELF-EVOLVE-GUIDE.md +1 -1
- package/docs/SWE_BENCH_REPORT.md +73 -0
- package/docs/TECHNICAL_SPEC_en.md +1 -1
- package/docs/TECHNICAL_SPEC_zh.md +1 -1
- package/docs/Technical_Manual_EN.md +1 -1
- package/docs/UPDATE_NOTES_en.md +1 -1
- package/docs/UPDATE_NOTES_zh.md +1 -1
- package/docs/UPGRADE_7.2.0.md +309 -0
- package/docs/UPGRADE_GUIDE_7_5.md +86 -0
- package/docs/UPGRADE_GUIDE_7_6.md +153 -0
- package/docs/User_Manual_EN.md +1 -1
- package/docs/VOLTAGENT_SKILLS_CATALOG.md +95 -0
- package/docs/error-codes.md +1 -1
- package/docs/geo-feihong-code.html +391 -0
- package/docs//344/272/247/345/223/201/345/274/200/345/217/221/346/226/207/346/241/243.md +5 -3
- package/docs//344/275/277/347/224/250/350/257/264/346/230/216/344/271/246.md +140 -231
- package/docs//345/205/250/351/235/242/345/244/215/347/233/230_50/350/275/256/346/267/261/345/272/246/345/256/241/350/256/241_v7.2.0.md +208 -0
- package/docs//345/205/250/351/235/242/345/244/215/347/233/230_6/351/241/271/351/207/215/347/202/271/346/240/270/346/237/245_v7.2.0.md +152 -0
- package/docs//345/205/250/351/235/242/345/244/215/347/233/230_/350/277/220/350/241/214/346/227/266/345/256/236/346/265/213/347/211/210_v7.2.0.md +271 -0
- package/docs//345/220/210/350/247/204/350/256/244/350/257/201/345/207/206/345/244/207/346/270/205/345/215/225_v7.2.0.md +59 -0
- package/docs//345/256/211/345/205/250/347/231/275/347/232/256/344/271/246_v7.2.0.md +84 -0
- package/docs//346/212/200/346/234/257/350/257/264/346/230/216/344/271/246.md +240 -149
- package/docs//351/203/250/347/275/262/350/257/264/346/230/216/344/271/246.md +2 -2
- package/electron/main.js +4 -202
- package/package.json +182 -172
- package/templates/skills/api-design/SKILL.md +27 -0
- package/templates/skills/code-review/SKILL.md +35 -0
- package/templates/skills/dependency-upgrade/SKILL.md +29 -0
- package/templates/skills/doc-gen/SKILL.md +29 -0
- package/templates/skills/git-flow/SKILL.md +26 -0
- package/templates/skills/onboarding/SKILL.md +27 -0
- package/templates/skills/performance/SKILL.md +29 -0
- package/templates/skills/refactor/SKILL.md +28 -0
- package/templates/skills/security-audit/SKILL.md +28 -0
- package/templates/skills/test-gen/SKILL.md +30 -0
- package/docs/DEPLOYMENT.md +0 -382
- package/docs/MODEL_FINETUNING.md +0 -405
package/dist/web/server.js
CHANGED
|
@@ -38,7 +38,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.startWebServer = startWebServer;
|
|
40
40
|
/**
|
|
41
|
-
* 飞虹 Code (Muse Code
|
|
41
|
+
* 飞虹 Code (对标 Muse Code · 自研内核)
|
|
42
42
|
* 晋江市飞虹智科技企业管理有限公司 · 飞扬企源研发中心 · 负责人:吴赐虹
|
|
43
43
|
*
|
|
44
44
|
* M5 Web 控制台:自包含 Express 服务骨架 + 多视图 API。
|
|
@@ -56,7 +56,9 @@ const crypto_1 = require("crypto");
|
|
|
56
56
|
const path_1 = require("path");
|
|
57
57
|
const fs_1 = require("fs");
|
|
58
58
|
const child_process_1 = require("child_process");
|
|
59
|
+
const node_dns_1 = __importDefault(require("node:dns"));
|
|
59
60
|
const auth_1 = require("./auth");
|
|
61
|
+
const extra_apis_1 = require("./extra-apis");
|
|
60
62
|
const task_queue_1 = require("./task-queue");
|
|
61
63
|
const version_1 = require("../cli/version");
|
|
62
64
|
const i18n_1 = require("../shared/i18n");
|
|
@@ -65,6 +67,7 @@ const config_1 = require("../shared/config");
|
|
|
65
67
|
const model_router_1 = require("../models/model-router");
|
|
66
68
|
const openai_compatible_provider_1 = require("../models/providers/openai-compatible.provider");
|
|
67
69
|
const completion_engine_1 = require("../agent/completion-engine");
|
|
70
|
+
const lint_1 = require("../agent/lint");
|
|
68
71
|
const symbol_index_1 = require("../agent/symbol-index");
|
|
69
72
|
const change_manager_1 = require("../agent/change-manager");
|
|
70
73
|
const design_to_code_1 = require("../agent/design-to-code");
|
|
@@ -78,6 +81,10 @@ const mcp_client_1 = require("../tools/mcp/mcp-client");
|
|
|
78
81
|
const memory_1 = require("../memory");
|
|
79
82
|
const auto_summarize_1 = require("../memory/auto-summarize");
|
|
80
83
|
const secure_store_1 = require("../shared/secure-store");
|
|
84
|
+
const wechat_bridge_1 = require("../integrations/wechat-bridge");
|
|
85
|
+
const feishu_bridge_1 = require("../integrations/feishu-bridge");
|
|
86
|
+
const yuanbao_bridge_1 = require("../integrations/yuanbao-bridge");
|
|
87
|
+
const collaboration_1 = require("../integrations/collaboration");
|
|
81
88
|
/**
|
|
82
89
|
* 启动 Web 控制台。
|
|
83
90
|
* - 端口:opts.port > FH_WEB_PORT > 8080
|
|
@@ -97,6 +104,10 @@ function startWebServer(opts = {}) {
|
|
|
97
104
|
const app = (0, express_1.default)();
|
|
98
105
|
// 放宽请求体上限以支持截图/图片 base64 上传(8MB),仍可有效防御内存耗尽
|
|
99
106
|
app.use(express_1.default.json({ limit: '8mb' }));
|
|
107
|
+
// 微信回调使用 XML body,单独路由用 text 解析
|
|
108
|
+
app.use('/api/wechat/callback', express_1.default.text({ type: ['*/xml', 'text/xml', 'application/xml'], limit: '1mb' }));
|
|
109
|
+
// 元宝回调需要原始 body 用于 HMAC 签名校验
|
|
110
|
+
app.use('/api/yuanbao/callback', express_1.default.raw({ type: 'application/json', limit: '1mb' }));
|
|
100
111
|
// 静态仪表盘(开发时直接从 src 读取,避免 dist 被锁)
|
|
101
112
|
const publicDir = process.env.FH_WEB_SRC_PUBLIC || (0, path_1.join)(__dirname, 'public');
|
|
102
113
|
app.use(express_1.default.static(publicDir, { maxAge: '0' }));
|
|
@@ -135,6 +146,18 @@ function startWebServer(opts = {}) {
|
|
|
135
146
|
catch (e) { /* 暂存失败不影响任务执行 */ }
|
|
136
147
|
},
|
|
137
148
|
});
|
|
149
|
+
// 微信桥接:注入 TaskQueue 引用并初始化
|
|
150
|
+
(0, wechat_bridge_1.setWechatTaskQueue)(queue);
|
|
151
|
+
const wechatConfig = (0, wechat_bridge_1.initWechatBridge)();
|
|
152
|
+
// 飞书桥接:注入 TaskQueue 和 FeishuIntegration,初始化
|
|
153
|
+
const feishuConfig = (0, feishu_bridge_1.initFeishuBridge)();
|
|
154
|
+
if ((0, feishu_bridge_1.isFeishuEnabled)(feishuConfig)) {
|
|
155
|
+
const feishuIntegration = new collaboration_1.FeishuIntegration({ appId: feishuConfig.appId, appSecret: feishuConfig.appSecret });
|
|
156
|
+
(0, feishu_bridge_1.setFeishuBridgeDeps)(queue, feishuIntegration);
|
|
157
|
+
}
|
|
158
|
+
// 元宝/豆包桥接:注入 TaskQueue 引用并初始化
|
|
159
|
+
(0, yuanbao_bridge_1.setYuanbaoTaskQueue)(queue);
|
|
160
|
+
const yuanbaoConfig = (0, yuanbao_bridge_1.initYuanbaoBridge)();
|
|
138
161
|
// 公开健康检查(仅暴露版本/状态等观测信息,无敏感数据)
|
|
139
162
|
app.get('/api/health', (_req, res) => {
|
|
140
163
|
res.json({
|
|
@@ -144,9 +167,43 @@ function startWebServer(opts = {}) {
|
|
|
144
167
|
signature: version_1.SIGNATURE,
|
|
145
168
|
enterprise: (0, enterprise_1.isEnterpriseEnabled)(),
|
|
146
169
|
lang: (0, i18n_1.getLang)(),
|
|
170
|
+
wechat: (0, wechat_bridge_1.isWechatEnabled)(wechatConfig) ? wechatConfig.mode : 'disabled',
|
|
171
|
+
feishu: (0, feishu_bridge_1.isFeishuEnabled)(feishuConfig) ? 'enabled' : 'disabled',
|
|
172
|
+
yuanbao: (0, yuanbao_bridge_1.isYuanbaoEnabled)(yuanbaoConfig) ? 'enabled' : 'disabled',
|
|
147
173
|
time: new Date().toISOString(),
|
|
148
174
|
});
|
|
149
175
|
});
|
|
176
|
+
// 微信桥接回调(GET=URL验证,POST=消息接收),无需 Bearer Token(微信服务器调用)
|
|
177
|
+
app.get('/api/wechat/callback', async (req, res) => {
|
|
178
|
+
const result = (await (0, wechat_bridge_1.handleWechatCallback)(wechatConfig, req.query, ''));
|
|
179
|
+
res.status(result.status);
|
|
180
|
+
res.setHeader('Content-Type', result.contentType);
|
|
181
|
+
res.send(result.body);
|
|
182
|
+
});
|
|
183
|
+
app.post('/api/wechat/callback', async (req, res) => {
|
|
184
|
+
const body = typeof req.body === 'string' ? req.body : JSON.stringify(req.body || '');
|
|
185
|
+
const result = (await (0, wechat_bridge_1.handleWechatCallback)(wechatConfig, req.query, body));
|
|
186
|
+
res.status(result.status);
|
|
187
|
+
res.setHeader('Content-Type', result.contentType);
|
|
188
|
+
res.send(result.body);
|
|
189
|
+
});
|
|
190
|
+
// 飞书桥接回调(事件订阅,无需 Bearer Token)
|
|
191
|
+
app.post('/api/feishu/callback', async (req, res) => {
|
|
192
|
+
const body = (req.body || {});
|
|
193
|
+
const result = (await (0, feishu_bridge_1.handleFeishuCallback)(feishuConfig, body));
|
|
194
|
+
res.status(result.status);
|
|
195
|
+
res.setHeader('Content-Type', result.contentType);
|
|
196
|
+
res.send(result.body);
|
|
197
|
+
});
|
|
198
|
+
// 元宝/豆包桥接回调(webhook,无需 Bearer Token,原始 body 用于签名校验)
|
|
199
|
+
app.post('/api/yuanbao/callback', async (req, res) => {
|
|
200
|
+
const rawBody = Buffer.isBuffer(req.body) ? req.body.toString('utf8') : (typeof req.body === 'string' ? req.body : JSON.stringify(req.body || ''));
|
|
201
|
+
const headers = req.headers;
|
|
202
|
+
const result = (await (0, yuanbao_bridge_1.handleYuanbaoCallback)(yuanbaoConfig, rawBody, headers));
|
|
203
|
+
res.status(result.status);
|
|
204
|
+
res.setHeader('Content-Type', result.contentType);
|
|
205
|
+
res.send(result.body);
|
|
206
|
+
});
|
|
150
207
|
// 手机号直登:无短信验证,生成本地会话令牌
|
|
151
208
|
app.post('/api/auth/login', (req, res) => {
|
|
152
209
|
const body = (req.body ?? {});
|
|
@@ -158,23 +215,19 @@ function startWebServer(opts = {}) {
|
|
|
158
215
|
// 使用新的登录方法,支持首次登录检测
|
|
159
216
|
const result = sessions.login(phone);
|
|
160
217
|
const session = sessions.get(result.token);
|
|
161
|
-
//
|
|
218
|
+
// 首次登录:返回引导任务定义(不自动提交执行,由用户在前端点「运行」时再真正发起)
|
|
162
219
|
let welcomeTasks = [];
|
|
163
220
|
if (result.isFirstLogin) {
|
|
164
221
|
// 标记会话为首次登录(用于后续接口识别)
|
|
165
222
|
if (session) {
|
|
166
223
|
session.isFirstLogin = true;
|
|
167
224
|
}
|
|
168
|
-
//
|
|
225
|
+
// 只返回任务定义,taskId 留空,前端点运行时才提交到任务队列
|
|
169
226
|
for (const task of auth_1.WELCOME_TASKS) {
|
|
170
|
-
const record = queue.submit(task.goal, {
|
|
171
|
-
workspaceDir: serverWorkspaceDir,
|
|
172
|
-
agentType: 'general',
|
|
173
|
-
});
|
|
174
227
|
welcomeTasks.push({
|
|
175
228
|
...task,
|
|
176
|
-
taskId:
|
|
177
|
-
status:
|
|
229
|
+
taskId: '',
|
|
230
|
+
status: 'pending',
|
|
178
231
|
});
|
|
179
232
|
}
|
|
180
233
|
}
|
|
@@ -270,7 +323,21 @@ function startWebServer(opts = {}) {
|
|
|
270
323
|
const history = (0, auto_summarize_1.getSummaryHistory)(memoryConfig, limit);
|
|
271
324
|
res.json({ ok: true, history });
|
|
272
325
|
});
|
|
273
|
-
app.
|
|
326
|
+
app.post('/api/auth/clear-first-login', (req, res) => {
|
|
327
|
+
const header = req.header('authorization') || '';
|
|
328
|
+
const m = /^Bearer\s+(.+)$/i.exec(header);
|
|
329
|
+
if (!m) {
|
|
330
|
+
res.status(401).json({ ok: false, error: 'unauthorized' });
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const token = m[1];
|
|
334
|
+
const ok = sessions.clearFirstLogin(token);
|
|
335
|
+
res.json({ ok, cleared: ok });
|
|
336
|
+
});
|
|
337
|
+
app.use('/api', (0, auth_1.requireToken)(token, sessions, ['/api/security/public-key']));
|
|
338
|
+
// ===== v7.2.0 能力接线层:voice / knowledge / figma / sso / collaboration / plugins / self-correction =====
|
|
339
|
+
// 将原"孤立代码"模块接入 Web API(修复:从 404 变为可调用)
|
|
340
|
+
(0, extra_apis_1.registerExtraApis)(app, { dataDir: (0, path_1.join)((0, config_1.resolveHomeDir)(), '.feihong-code') });
|
|
274
341
|
app.get('/api/auth/me', (req, res) => {
|
|
275
342
|
const session = req.user;
|
|
276
343
|
res.json({
|
|
@@ -776,6 +843,7 @@ function startWebServer(opts = {}) {
|
|
|
776
843
|
const cursorOffset = typeof body?.cursorOffset === 'number' ? body.cursorOffset : fileContent.length;
|
|
777
844
|
const mode = body?.mode === 'full' ? 'full' : 'quick';
|
|
778
845
|
const language = typeof body?.language === 'string' ? body.language : undefined;
|
|
846
|
+
const crossFileContext = typeof body?.crossFileContext === 'string' ? body.crossFileContext : undefined;
|
|
779
847
|
if (!filePath || !fileContent) {
|
|
780
848
|
res.status(400).json({ ok: false, error: '缺少 filePath 或 fileContent' });
|
|
781
849
|
return;
|
|
@@ -791,6 +859,7 @@ function startWebServer(opts = {}) {
|
|
|
791
859
|
cursorOffset,
|
|
792
860
|
mode,
|
|
793
861
|
language,
|
|
862
|
+
crossFileContext,
|
|
794
863
|
});
|
|
795
864
|
res.json({ ok: true, ...result });
|
|
796
865
|
}
|
|
@@ -798,6 +867,17 @@ function startWebServer(opts = {}) {
|
|
|
798
867
|
res.status(500).json({ ok: false, error: '补全失败: ' + e.message, suggestions: [] });
|
|
799
868
|
}
|
|
800
869
|
});
|
|
870
|
+
app.post('/api/lint', (req, res) => {
|
|
871
|
+
const body = (req.body ?? {});
|
|
872
|
+
const code = typeof body?.code === 'string' ? body.code : '';
|
|
873
|
+
const language = typeof body?.language === 'string' ? body.language : undefined;
|
|
874
|
+
if (!code) {
|
|
875
|
+
res.status(400).json({ ok: false, error: '缺少 code' });
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
const errors = (0, lint_1.lintSnippet)(code, language);
|
|
879
|
+
res.json({ ok: true, errors, clean: errors.length === 0 });
|
|
880
|
+
});
|
|
801
881
|
/* ========== 阶段一-2:补全 Pro API ========== */
|
|
802
882
|
// 记录用户接受的补全(用于连续推荐)
|
|
803
883
|
app.post('/api/completion/accept', (req, res) => {
|
|
@@ -844,6 +924,15 @@ function startWebServer(opts = {}) {
|
|
|
844
924
|
}
|
|
845
925
|
});
|
|
846
926
|
/* ========== P2-1: 设计稿转代码 API(多模态,图片→代码) ========== */
|
|
927
|
+
// P1-3: 设计稿转代码引擎状态查询
|
|
928
|
+
app.get('/api/design-to-code', (_req, res) => {
|
|
929
|
+
res.json({
|
|
930
|
+
ok: true,
|
|
931
|
+
configured: !!designToCodeEngine,
|
|
932
|
+
framework: designToCodeEngine ? 'html' : null,
|
|
933
|
+
note: designToCodeEngine ? '已配置多模态模型' : '未配置多模态模型(POST /api/design-to-code 需 vision 模型)',
|
|
934
|
+
});
|
|
935
|
+
});
|
|
847
936
|
app.post('/api/design-to-code', async (req, res) => {
|
|
848
937
|
const body = (req.body ?? {});
|
|
849
938
|
const image = typeof body?.image === 'string' ? body.image.trim() : '';
|
|
@@ -1337,6 +1426,15 @@ function startWebServer(opts = {}) {
|
|
|
1337
1426
|
.catch((e) => res.status(500).json({ ok: false, error: e.message }));
|
|
1338
1427
|
});
|
|
1339
1428
|
// Cron 任务 CRUD
|
|
1429
|
+
app.get('/api/event-driven/cron', (_req, res) => {
|
|
1430
|
+
try {
|
|
1431
|
+
const cfg = eventDrivenManager.getConfig();
|
|
1432
|
+
res.json({ ok: true, cronTasks: cfg.cronTasks ?? [] });
|
|
1433
|
+
}
|
|
1434
|
+
catch (e) {
|
|
1435
|
+
res.status(500).json({ ok: false, error: e.message });
|
|
1436
|
+
}
|
|
1437
|
+
});
|
|
1340
1438
|
app.post('/api/event-driven/cron', (req, res) => {
|
|
1341
1439
|
const body = (req.body ?? {});
|
|
1342
1440
|
if (!body.name || !body.cron || !body.task) {
|
|
@@ -1609,18 +1707,18 @@ function startWebServer(opts = {}) {
|
|
|
1609
1707
|
res.status(400).json({ ok: false, error: '仅支持 Windows 系统' });
|
|
1610
1708
|
return;
|
|
1611
1709
|
}
|
|
1612
|
-
const script = `
|
|
1613
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
1614
|
-
Add-Type -AssemblyName System.Drawing
|
|
1615
|
-
$screen = [System.Windows.Forms.Screen]::PrimaryScreen
|
|
1616
|
-
$bounds = $screen.Bounds
|
|
1617
|
-
$bitmap = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height
|
|
1618
|
-
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
|
|
1619
|
-
$graphics.CopyFromScreen($bounds.Location, [System.Drawing.Point]::Empty, $bounds.Size)
|
|
1620
|
-
$ms = New-Object System.IO.MemoryStream
|
|
1621
|
-
$bitmap.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
1622
|
-
$bytes = $ms.ToArray()
|
|
1623
|
-
[Convert]::ToBase64String($bytes)
|
|
1710
|
+
const script = `
|
|
1711
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
1712
|
+
Add-Type -AssemblyName System.Drawing
|
|
1713
|
+
$screen = [System.Windows.Forms.Screen]::PrimaryScreen
|
|
1714
|
+
$bounds = $screen.Bounds
|
|
1715
|
+
$bitmap = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height
|
|
1716
|
+
$graphics = [System.Drawing.Graphics]::FromImage($bitmap)
|
|
1717
|
+
$graphics.CopyFromScreen($bounds.Location, [System.Drawing.Point]::Empty, $bounds.Size)
|
|
1718
|
+
$ms = New-Object System.IO.MemoryStream
|
|
1719
|
+
$bitmap.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
|
|
1720
|
+
$bytes = $ms.ToArray()
|
|
1721
|
+
[Convert]::ToBase64String($bytes)
|
|
1624
1722
|
`;
|
|
1625
1723
|
const base64 = await runPowerShell(script);
|
|
1626
1724
|
res.json({ ok: true, image: 'data:image/png;base64,' + base64, width: 1920, height: 1080 });
|
|
@@ -1639,17 +1737,17 @@ function startWebServer(opts = {}) {
|
|
|
1639
1737
|
res.status(400).json({ ok: false, error: '缺少 x 或 y 坐标' });
|
|
1640
1738
|
return;
|
|
1641
1739
|
}
|
|
1642
|
-
const script = `
|
|
1643
|
-
Add-Type @"
|
|
1644
|
-
using System;
|
|
1645
|
-
using System.Runtime.InteropServices;
|
|
1646
|
-
public class MouseHelper {
|
|
1647
|
-
[DllImport("user32.dll")]
|
|
1648
|
-
public static extern bool SetCursorPos(int X, int Y);
|
|
1649
|
-
}
|
|
1650
|
-
"@
|
|
1651
|
-
[MouseHelper]::SetCursorPos(${x}, ${y}) | Out-Null
|
|
1652
|
-
Write-Output "ok"
|
|
1740
|
+
const script = `
|
|
1741
|
+
Add-Type @"
|
|
1742
|
+
using System;
|
|
1743
|
+
using System.Runtime.InteropServices;
|
|
1744
|
+
public class MouseHelper {
|
|
1745
|
+
[DllImport("user32.dll")]
|
|
1746
|
+
public static extern bool SetCursorPos(int X, int Y);
|
|
1747
|
+
}
|
|
1748
|
+
"@
|
|
1749
|
+
[MouseHelper]::SetCursorPos(${x}, ${y}) | Out-Null
|
|
1750
|
+
Write-Output "ok"
|
|
1653
1751
|
`;
|
|
1654
1752
|
await runPowerShell(script);
|
|
1655
1753
|
res.json({ ok: true, x, y });
|
|
@@ -1676,20 +1774,20 @@ function startWebServer(opts = {}) {
|
|
|
1676
1774
|
const clickPart = doubleClick
|
|
1677
1775
|
? `[MouseHelper]::mouse_event(${clickFlag}, 0, 0, 0, 0); [MouseHelper]::mouse_event(${upFlag}, 0, 0, 0, 0); Start-Sleep -Milliseconds 100; [MouseHelper]::mouse_event(${clickFlag}, 0, 0, 0, 0); [MouseHelper]::mouse_event(${upFlag}, 0, 0, 0, 0);`
|
|
1678
1776
|
: `[MouseHelper]::mouse_event(${clickFlag}, 0, 0, 0, 0); [MouseHelper]::mouse_event(${upFlag}, 0, 0, 0, 0);`;
|
|
1679
|
-
const script = `
|
|
1680
|
-
Add-Type @"
|
|
1681
|
-
using System;
|
|
1682
|
-
using System.Runtime.InteropServices;
|
|
1683
|
-
public class MouseHelper {
|
|
1684
|
-
[DllImport("user32.dll")]
|
|
1685
|
-
public static extern bool SetCursorPos(int X, int Y);
|
|
1686
|
-
[DllImport("user32.dll")]
|
|
1687
|
-
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
|
|
1688
|
-
}
|
|
1689
|
-
"@
|
|
1690
|
-
${movePart}
|
|
1691
|
-
${clickPart}
|
|
1692
|
-
Write-Output "ok"
|
|
1777
|
+
const script = `
|
|
1778
|
+
Add-Type @"
|
|
1779
|
+
using System;
|
|
1780
|
+
using System.Runtime.InteropServices;
|
|
1781
|
+
public class MouseHelper {
|
|
1782
|
+
[DllImport("user32.dll")]
|
|
1783
|
+
public static extern bool SetCursorPos(int X, int Y);
|
|
1784
|
+
[DllImport("user32.dll")]
|
|
1785
|
+
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
|
|
1786
|
+
}
|
|
1787
|
+
"@
|
|
1788
|
+
${movePart}
|
|
1789
|
+
${clickPart}
|
|
1790
|
+
Write-Output "ok"
|
|
1693
1791
|
`;
|
|
1694
1792
|
await runPowerShell(script);
|
|
1695
1793
|
res.json({ ok: true, button, x, y, double: doubleClick });
|
|
@@ -1709,10 +1807,10 @@ function startWebServer(opts = {}) {
|
|
|
1709
1807
|
}
|
|
1710
1808
|
// SendKeys 需要转义特殊字符
|
|
1711
1809
|
const escaped = text.replace(/([+^%~(){}])/g, '{$1}');
|
|
1712
|
-
const script = `
|
|
1713
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
1714
|
-
[System.Windows.Forms.SendKeys]::SendWait('${escaped.replace(/'/g, "''")}')
|
|
1715
|
-
Write-Output "ok"
|
|
1810
|
+
const script = `
|
|
1811
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
1812
|
+
[System.Windows.Forms.SendKeys]::SendWait('${escaped.replace(/'/g, "''")}')
|
|
1813
|
+
Write-Output "ok"
|
|
1716
1814
|
`;
|
|
1717
1815
|
await runPowerShell(script);
|
|
1718
1816
|
res.json({ ok: true, text });
|
|
@@ -1730,10 +1828,10 @@ function startWebServer(opts = {}) {
|
|
|
1730
1828
|
res.status(400).json({ ok: false, error: '缺少 key 字段' });
|
|
1731
1829
|
return;
|
|
1732
1830
|
}
|
|
1733
|
-
const script = `
|
|
1734
|
-
Add-Type -AssemblyName System.Windows.Forms
|
|
1735
|
-
[System.Windows.Forms.SendKeys]::SendWait('${key.replace(/'/g, "''")}')
|
|
1736
|
-
Write-Output "ok"
|
|
1831
|
+
const script = `
|
|
1832
|
+
Add-Type -AssemblyName System.Windows.Forms
|
|
1833
|
+
[System.Windows.Forms.SendKeys]::SendWait('${key.replace(/'/g, "''")}')
|
|
1834
|
+
Write-Output "ok"
|
|
1737
1835
|
`;
|
|
1738
1836
|
await runPowerShell(script);
|
|
1739
1837
|
res.json({ ok: true, key });
|
|
@@ -1791,7 +1889,7 @@ function startWebServer(opts = {}) {
|
|
|
1791
1889
|
const headers = { 'User-Agent': 'fhcode-node/1.0' };
|
|
1792
1890
|
if (node.apiKey)
|
|
1793
1891
|
headers['Authorization'] = 'Bearer ' + node.apiKey;
|
|
1794
|
-
const r = await fetchWithRetry(url,
|
|
1892
|
+
const r = await fetchWithRetry(url, 25000, 0);
|
|
1795
1893
|
if (!r.ok)
|
|
1796
1894
|
return { ok: false, error: `HTTP ${r.status}` };
|
|
1797
1895
|
if (node.type === 'http') {
|
|
@@ -2068,7 +2166,10 @@ function startWebServer(opts = {}) {
|
|
|
2068
2166
|
/* ========== 技能市场(插件市场):聚合 ClawHub + Agent-Foundry + 自定义节点 + 自定义来源 ========== */
|
|
2069
2167
|
const CLAWHUB_API = 'https://clawhub.ai/api/v1/skills';
|
|
2070
2168
|
const AGENT_FOUNDRY_CATALOG = 'https://raw.githubusercontent.com/hebertzhu/agent-foundry/main/catalog/skills-catalog.json';
|
|
2071
|
-
async function fetchWithRetry(url, timeoutMs =
|
|
2169
|
+
async function fetchWithRetry(url, timeoutMs = 25000, retries = 1) {
|
|
2170
|
+
// 强制 IPv4 优先:规避 IPv6 链路故障(部分网络 IPv6 路由不可达时,
|
|
2171
|
+
// 默认 verbatim 解析会优先走 IPv6 导致连接超时/失败)。幂等,重复调用无副作用。
|
|
2172
|
+
node_dns_1.default.setDefaultResultOrder('ipv4first');
|
|
2072
2173
|
let lastErr;
|
|
2073
2174
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
2074
2175
|
const ctrl = new AbortController();
|
|
@@ -2118,7 +2219,7 @@ function startWebServer(opts = {}) {
|
|
|
2118
2219
|
}
|
|
2119
2220
|
async function fetchAgentFoundrySkills(keyword, limit) {
|
|
2120
2221
|
try {
|
|
2121
|
-
const r = await fetchWithRetry(AGENT_FOUNDRY_CATALOG);
|
|
2222
|
+
const r = await fetchWithRetry(AGENT_FOUNDRY_CATALOG, 30000);
|
|
2122
2223
|
if (!r.ok)
|
|
2123
2224
|
return [];
|
|
2124
2225
|
const data = (await r.json());
|
|
@@ -2189,7 +2290,7 @@ function startWebServer(opts = {}) {
|
|
|
2189
2290
|
if (!src.enabled || src.type !== 'skills')
|
|
2190
2291
|
continue;
|
|
2191
2292
|
try {
|
|
2192
|
-
const r = await fetchWithRetry(src.url,
|
|
2293
|
+
const r = await fetchWithRetry(src.url, 25000, 0);
|
|
2193
2294
|
if (r.ok) {
|
|
2194
2295
|
const data = await r.json();
|
|
2195
2296
|
const list = Array.isArray(data) ? data : (data.items || data.skills || []);
|
|
@@ -2495,7 +2596,7 @@ function startWebServer(opts = {}) {
|
|
|
2495
2596
|
if (!src.enabled || src.type !== 'templates')
|
|
2496
2597
|
continue;
|
|
2497
2598
|
try {
|
|
2498
|
-
const r = await fetchWithRetry(src.url,
|
|
2599
|
+
const r = await fetchWithRetry(src.url, 25000, 0);
|
|
2499
2600
|
if (r.ok) {
|
|
2500
2601
|
const data = await r.json();
|
|
2501
2602
|
const list = Array.isArray(data) ? data : (data.items || data.templates || []);
|
|
@@ -2619,7 +2720,7 @@ function startWebServer(opts = {}) {
|
|
|
2619
2720
|
if (!src.enabled || src.type !== 'office')
|
|
2620
2721
|
continue;
|
|
2621
2722
|
try {
|
|
2622
|
-
const r = await fetchWithRetry(src.url,
|
|
2723
|
+
const r = await fetchWithRetry(src.url, 25000, 0);
|
|
2623
2724
|
if (r.ok) {
|
|
2624
2725
|
const data = await r.json();
|
|
2625
2726
|
const list = Array.isArray(data) ? data : (data.items || data.capabilities || []);
|
|
@@ -2738,6 +2839,22 @@ function startWebServer(opts = {}) {
|
|
|
2738
2839
|
const list = loadModels().map(publicModel);
|
|
2739
2840
|
res.json({ ok: true, models: list, defaultId: (list.find((m) => m.default) || {}).id || null });
|
|
2740
2841
|
});
|
|
2842
|
+
// P1-3: 模型提供商聚合视图(按 apiBase 分组)
|
|
2843
|
+
app.get('/api/models/providers', (_req, res) => {
|
|
2844
|
+
const list = loadModels().map(publicModel);
|
|
2845
|
+
const byKey = new Map();
|
|
2846
|
+
for (const m of list) {
|
|
2847
|
+
const key = m.apiBase || 'custom';
|
|
2848
|
+
let entry = byKey.get(key);
|
|
2849
|
+
if (!entry) {
|
|
2850
|
+
entry = { providerId: key, apiBase: key, models: [], count: 0 };
|
|
2851
|
+
byKey.set(key, entry);
|
|
2852
|
+
}
|
|
2853
|
+
entry.models.push(m);
|
|
2854
|
+
entry.count++;
|
|
2855
|
+
}
|
|
2856
|
+
res.json({ ok: true, providers: Array.from(byKey.values()), total: list.length });
|
|
2857
|
+
});
|
|
2741
2858
|
app.post('/api/models', (req, res) => {
|
|
2742
2859
|
const body = req.body;
|
|
2743
2860
|
const name = typeof body?.name === 'string' ? body.name.trim() : '';
|