feihong-code 7.0.0 → 7.5.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.
Files changed (149) hide show
  1. package/.github/workflows/ci.yml +180 -140
  2. package/.github/workflows/release.yml +200 -0
  3. package/CHANGELOG.md +94 -65
  4. package/README.md +609 -586
  5. package/dist/agent/browser-agent.js +354 -0
  6. package/dist/agent/browser-agent.js.map +1 -0
  7. package/dist/agent/change-manager.js +433 -0
  8. package/dist/agent/change-manager.js.map +1 -0
  9. package/dist/agent/code-rag.js +293 -0
  10. package/dist/agent/code-rag.js.map +1 -0
  11. package/dist/agent/code-writer.js +13 -1
  12. package/dist/agent/code-writer.js.map +1 -1
  13. package/dist/agent/completion-engine.js +553 -0
  14. package/dist/agent/completion-engine.js.map +1 -0
  15. package/dist/agent/completion-postprocess.js +109 -0
  16. package/dist/agent/completion-postprocess.js.map +1 -0
  17. package/dist/agent/context-budget.js +121 -0
  18. package/dist/agent/context-budget.js.map +1 -0
  19. package/dist/agent/context-compactor.js +235 -10
  20. package/dist/agent/context-compactor.js.map +1 -1
  21. package/dist/agent/custom-agent.js +368 -0
  22. package/dist/agent/custom-agent.js.map +1 -0
  23. package/dist/agent/design-to-code.js +218 -0
  24. package/dist/agent/design-to-code.js.map +1 -0
  25. package/dist/agent/event-driven-agent.js +359 -0
  26. package/dist/agent/event-driven-agent.js.map +1 -0
  27. package/dist/agent/git-integration.js +324 -0
  28. package/dist/agent/git-integration.js.map +1 -0
  29. package/dist/agent/layered-memory.js +329 -0
  30. package/dist/agent/layered-memory.js.map +1 -0
  31. package/dist/agent/lint.js +121 -0
  32. package/dist/agent/lint.js.map +1 -0
  33. package/dist/agent/multi-agent.js +359 -0
  34. package/dist/agent/multi-agent.js.map +1 -0
  35. package/dist/agent/orchestrator.js +136 -25
  36. package/dist/agent/orchestrator.js.map +1 -1
  37. package/dist/agent/prompts.js +54 -54
  38. package/dist/agent/quality-gate.js +139 -0
  39. package/dist/agent/quality-gate.js.map +1 -1
  40. package/dist/agent/self-correction.js +274 -0
  41. package/dist/agent/self-correction.js.map +1 -0
  42. package/dist/agent/self-heal.js +47 -17
  43. package/dist/agent/self-heal.js.map +1 -1
  44. package/dist/agent/solo-agent.js +321 -0
  45. package/dist/agent/solo-agent.js.map +1 -0
  46. package/dist/agent/symbol-index.js +258 -28
  47. package/dist/agent/symbol-index.js.map +1 -1
  48. package/dist/agent/team-collaboration.js +252 -0
  49. package/dist/agent/team-collaboration.js.map +1 -0
  50. package/dist/agent/type-checker.js +229 -0
  51. package/dist/agent/type-checker.js.map +1 -0
  52. package/dist/cli/commands.js +9 -0
  53. package/dist/cli/commands.js.map +1 -1
  54. package/dist/cli/index.js +9 -0
  55. package/dist/cli/index.js.map +1 -1
  56. package/dist/cli/multi-pane-tui.js +217 -0
  57. package/dist/cli/multi-pane-tui.js.map +1 -0
  58. package/dist/cli/run.js +130 -40
  59. package/dist/cli/run.js.map +1 -1
  60. package/dist/cli/tui-run.js +79 -0
  61. package/dist/cli/tui-run.js.map +1 -0
  62. package/dist/cli/tui.js.map +1 -1
  63. package/dist/cli/version.js +1 -1
  64. package/dist/integrations/collaboration.js +219 -0
  65. package/dist/integrations/collaboration.js.map +1 -0
  66. package/dist/integrations/figma.js +307 -0
  67. package/dist/integrations/figma.js.map +1 -0
  68. package/dist/integrations/sso.js +327 -0
  69. package/dist/integrations/sso.js.map +1 -0
  70. package/dist/knowledge/library.js +301 -0
  71. package/dist/knowledge/library.js.map +1 -0
  72. package/dist/lsp/lsp-client.js +183 -0
  73. package/dist/lsp/lsp-client.js.map +1 -0
  74. package/dist/lsp/lsp-service.js +159 -0
  75. package/dist/lsp/lsp-service.js.map +1 -0
  76. package/dist/plugins/manager.js +380 -0
  77. package/dist/plugins/manager.js.map +1 -0
  78. package/dist/runtime/event-log.js.map +1 -1
  79. package/dist/shared/i18n.js +108 -100
  80. package/dist/shared/i18n.js.map +1 -1
  81. package/dist/tools/browser/browser.tool.js +217 -0
  82. package/dist/tools/browser/browser.tool.js.map +1 -0
  83. package/dist/tools/generator/code-generator.js +40 -40
  84. package/dist/tools/generator/test-generator.js +9 -9
  85. package/dist/tools/index.js +2 -0
  86. package/dist/tools/index.js.map +1 -1
  87. package/dist/tools/shell/exec.js +73 -11
  88. package/dist/tools/shell/exec.js.map +1 -1
  89. package/dist/tools/shell/run-shell.tool.js +8 -2
  90. package/dist/tools/shell/run-shell.tool.js.map +1 -1
  91. package/dist/training/completion-evaluator.js +213 -0
  92. package/dist/training/completion-evaluator.js.map +1 -0
  93. package/dist/training/fim-data.js +248 -0
  94. package/dist/training/fim-data.js.map +1 -0
  95. package/dist/voice/voice-programming.js +223 -0
  96. package/dist/voice/voice-programming.js.map +1 -0
  97. package/dist/web/extra-apis.js +440 -0
  98. package/dist/web/extra-apis.js.map +1 -0
  99. package/dist/web/public/css/style.css +1729 -1671
  100. package/dist/web/public/index.html +1242 -990
  101. package/dist/web/public/js/api.js +555 -382
  102. package/dist/web/public/js/app.js +2210 -2210
  103. package/dist/web/public/js/mock-api.js +234 -0
  104. package/dist/web/public/js/monaco-editor.js +363 -0
  105. package/dist/web/public/js/ui.js +2251 -1059
  106. package/dist/web/public/js/utils.js +214 -214
  107. package/dist/web/public/manifest.json +59 -0
  108. package/dist/web/public/service-worker.js +173 -0
  109. package/dist/web/server.js +1116 -45
  110. package/dist/web/server.js.map +1 -1
  111. package/dist/web/task-queue.js +22 -2
  112. package/dist/web/task-queue.js.map +1 -1
  113. package/docs/BENCHMARK_REPORT_en.md +386 -329
  114. package/docs/BENCHMARK_REPORT_zh.md +386 -329
  115. package/docs/DPA.md +63 -0
  116. 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 +67 -0
  117. package/docs/SECURITY_WHITEPAPER.md +74 -0
  118. package/docs/SWE_BENCH_REPORT.md +55 -0
  119. package/docs/TECHNICAL_SPEC_en.md +292 -292
  120. package/docs/TECHNICAL_SPEC_zh.md +292 -292
  121. package/docs/UPDATE_NOTES_en.md +159 -159
  122. package/docs/UPDATE_NOTES_zh.md +159 -159
  123. package/docs/UPGRADE_7.2.0.md +309 -0
  124. package/docs/UPGRADE_GUIDE_7_5.md +86 -0
  125. package/docs/geo-feihong-code.html +391 -0
  126. 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
  127. 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
  128. 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
  129. 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
  130. package/docs//345/256/211/345/205/250/347/231/275/347/232/256/344/271/246_v7.2.0.md +82 -0
  131. package/electron/main.js +404 -404
  132. package/package.json +175 -167
  133. package/templates/skills/api-design/SKILL.md +27 -0
  134. package/templates/skills/code-review/SKILL.md +35 -0
  135. package/templates/skills/dependency-upgrade/SKILL.md +29 -0
  136. package/templates/skills/doc-gen/SKILL.md +29 -0
  137. package/templates/skills/git-flow/SKILL.md +26 -0
  138. package/templates/skills/onboarding/SKILL.md +27 -0
  139. package/templates/skills/performance/SKILL.md +29 -0
  140. package/templates/skills/refactor/SKILL.md +28 -0
  141. package/templates/skills/security-audit/SKILL.md +28 -0
  142. package/templates/skills/test-gen/SKILL.md +30 -0
  143. package/tool-schema.json +197 -197
  144. package/dist/hello.js +0 -14
  145. package/dist/hello.js.map +0 -1
  146. package/dist/self-evolve/hook.ts +0 -80
  147. package/dist/self-evolve/manager.d.ts +0 -8
  148. package/dist/web/web-config.js +0 -143
  149. package/dist/web/web-config.js.map +0 -1
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -24,11 +57,26 @@ const path_1 = require("path");
24
57
  const fs_1 = require("fs");
25
58
  const child_process_1 = require("child_process");
26
59
  const auth_1 = require("./auth");
60
+ const extra_apis_1 = require("./extra-apis");
27
61
  const task_queue_1 = require("./task-queue");
28
62
  const version_1 = require("../cli/version");
29
63
  const i18n_1 = require("../shared/i18n");
30
64
  const enterprise_1 = require("../enterprise");
31
65
  const config_1 = require("../shared/config");
66
+ const model_router_1 = require("../models/model-router");
67
+ const openai_compatible_provider_1 = require("../models/providers/openai-compatible.provider");
68
+ const completion_engine_1 = require("../agent/completion-engine");
69
+ const lint_1 = require("../agent/lint");
70
+ const symbol_index_1 = require("../agent/symbol-index");
71
+ const change_manager_1 = require("../agent/change-manager");
72
+ const design_to_code_1 = require("../agent/design-to-code");
73
+ const git_integration_1 = require("../agent/git-integration");
74
+ const team_collaboration_1 = require("../agent/team-collaboration");
75
+ const solo_agent_1 = require("../agent/solo-agent");
76
+ const multi_agent_1 = require("../agent/multi-agent");
77
+ const event_driven_agent_1 = require("../agent/event-driven-agent");
78
+ const custom_agent_1 = require("../agent/custom-agent");
79
+ const mcp_client_1 = require("../tools/mcp/mcp-client");
32
80
  const memory_1 = require("../memory");
33
81
  const auto_summarize_1 = require("../memory/auto-summarize");
34
82
  const secure_store_1 = require("../shared/secure-store");
@@ -58,6 +106,22 @@ function startWebServer(opts = {}) {
58
106
  const sessions = new auth_1.SessionStore();
59
107
  // 当前 Web 控制台工作区,任务提交缺省时使用
60
108
  let serverWorkspaceDir = (0, path_1.resolve)(process.cwd());
109
+ // P3: 多文件变更管理器(AI 生成的修改先暂存,用户审批后才写入磁盘)
110
+ const changeManager = new change_manager_1.ChangeManager({ cwd: serverWorkspaceDir });
111
+ // P2-2: Git 集成管理器
112
+ let gitIntegration = (0, git_integration_1.createGitIntegration)(serverWorkspaceDir);
113
+ // P2-3: 团队协作管理器
114
+ const teamManager = (0, team_collaboration_1.createTeamCollaborationManager)();
115
+ // 阶段二-2: 事件驱动 Agent 管理器
116
+ const eventDrivenManager = (0, event_driven_agent_1.createEventDrivenAgentManager)(serverWorkspaceDir, async (event) => {
117
+ console.log('[event-driven] processing:', { eventId: event.id, type: event.type });
118
+ // 实际处理逻辑:根据事件类型触发对应的 Agent 任务
119
+ // 这里简化处理,实际应集成到 orchestrator
120
+ });
121
+ // 阶段二-3: 自定义 Agent 管理器
122
+ const customAgentManager = (0, custom_agent_1.createCustomAgentManager)(serverWorkspaceDir);
123
+ // P3-2: SOLO 全自主编程任务存储
124
+ const soloTasks = new Map();
61
125
  // 任务队列(进程内;服务端静默执行)— 需在登录接口前初始化
62
126
  const persistDir = process.env.FH_TASK_PERSIST_DIR?.trim() ||
63
127
  (0, path_1.join)(process.env.FH_HOME?.trim() || (0, path_1.join)(require('os').homedir(), '.feihong-code'), 'tasks');
@@ -65,6 +129,13 @@ function startWebServer(opts = {}) {
65
129
  concurrency: Number(process.env.FH_TASK_CONCURRENCY ?? 2),
66
130
  webhookUrl: process.env.FH_TASK_WEBHOOK_URL,
67
131
  persistDir,
132
+ // P3-1: AI 生成的文件修改自动暂存到变更面板
133
+ stageChange: (path, content) => {
134
+ try {
135
+ changeManager.stageChange(path, content);
136
+ }
137
+ catch (e) { /* 暂存失败不影响任务执行 */ }
138
+ },
68
139
  });
69
140
  // 公开健康检查(仅暴露版本/状态等观测信息,无敏感数据)
70
141
  app.get('/api/health', (_req, res) => {
@@ -202,6 +273,9 @@ function startWebServer(opts = {}) {
202
273
  res.json({ ok: true, history });
203
274
  });
204
275
  app.use('/api', (0, auth_1.requireToken)(token, sessions));
276
+ // ===== v7.2.0 能力接线层:voice / knowledge / figma / sso / collaboration / plugins / self-correction =====
277
+ // 将原"孤立代码"模块接入 Web API(修复:从 404 变为可调用)
278
+ (0, extra_apis_1.registerExtraApis)(app, { dataDir: (0, path_1.join)((0, config_1.resolveHomeDir)(), '.feihong-code') });
205
279
  app.get('/api/auth/me', (req, res) => {
206
280
  const session = req.user;
207
281
  res.json({
@@ -536,6 +610,939 @@ function startWebServer(opts = {}) {
536
610
  res.status(500).json({ ok: false, error: '读取失败: ' + e.message });
537
611
  }
538
612
  });
613
+ /* ========== P3: 多文件变更管理(暂存/审批/原子提交) ========== */
614
+ // 暂存变更(AI 生成的文件内容先暂存,不直接写入)
615
+ app.post('/api/changes/stage', (req, res) => {
616
+ const body = (req.body ?? {});
617
+ const path = typeof body?.path === 'string' ? body.path.trim() : '';
618
+ const content = typeof body?.content === 'string' ? body.content : '';
619
+ if (!path) {
620
+ res.status(400).json({ ok: false, error: '缺少 path 字段' });
621
+ return;
622
+ }
623
+ const change = changeManager.stageChange(path, content);
624
+ if (!change) {
625
+ res.status(400).json({ ok: false, error: '内容未变化或暂存区已满' });
626
+ return;
627
+ }
628
+ res.json({ ok: true, change: { path: change.path, type: change.type, additions: change.additions, deletions: change.deletions, status: change.status, hunks: change.hunks } });
629
+ });
630
+ // 获取所有暂存变更(变更面板数据)
631
+ app.get('/api/changes', (_req, res) => {
632
+ res.json({ ok: true, ...changeManager.toPanelData() });
633
+ });
634
+ // 接受整个文件的变更
635
+ app.post('/api/changes/accept', (req, res) => {
636
+ const body = (req.body ?? {});
637
+ const path = typeof body?.path === 'string' ? body.path.trim() : '';
638
+ if (!path) {
639
+ res.status(400).json({ ok: false, error: '缺少 path 字段' });
640
+ return;
641
+ }
642
+ const ok = changeManager.acceptFile(path);
643
+ res.json({ ok, ...(ok ? {} : { error: '文件不存在' }) });
644
+ });
645
+ // 拒绝整个文件的变更
646
+ app.post('/api/changes/reject', (req, res) => {
647
+ const body = (req.body ?? {});
648
+ const path = typeof body?.path === 'string' ? body.path.trim() : '';
649
+ if (!path) {
650
+ res.status(400).json({ ok: false, error: '缺少 path 字段' });
651
+ return;
652
+ }
653
+ const ok = changeManager.rejectFile(path);
654
+ res.json({ ok, ...(ok ? {} : { error: '文件不存在' }) });
655
+ });
656
+ // 接受单个 Hunk
657
+ app.post('/api/changes/hunk/accept', (req, res) => {
658
+ const body = (req.body ?? {});
659
+ const path = typeof body?.path === 'string' ? body.path.trim() : '';
660
+ const hunkIndex = typeof body?.hunkIndex === 'number' ? body.hunkIndex : -1;
661
+ if (!path || hunkIndex < 0) {
662
+ res.status(400).json({ ok: false, error: '缺少 path 或 hunkIndex' });
663
+ return;
664
+ }
665
+ const ok = changeManager.acceptHunk(path, hunkIndex);
666
+ res.json({ ok, ...(ok ? {} : { error: '文件或 Hunk 不存在' }) });
667
+ });
668
+ // 拒绝单个 Hunk
669
+ app.post('/api/changes/hunk/reject', (req, res) => {
670
+ const body = (req.body ?? {});
671
+ const path = typeof body?.path === 'string' ? body.path.trim() : '';
672
+ const hunkIndex = typeof body?.hunkIndex === 'number' ? body.hunkIndex : -1;
673
+ if (!path || hunkIndex < 0) {
674
+ res.status(400).json({ ok: false, error: '缺少 path 或 hunkIndex' });
675
+ return;
676
+ }
677
+ const ok = changeManager.rejectHunk(path, hunkIndex);
678
+ res.json({ ok, ...(ok ? {} : { error: '文件或 Hunk 不存在' }) });
679
+ });
680
+ // 原子化提交(写入所有已接受的变更,失败自动回滚)
681
+ app.post('/api/changes/commit', (_req, res) => {
682
+ const result = changeManager.commit();
683
+ res.json({ ok: result.success, ...result });
684
+ });
685
+ // 丢弃所有暂存变更
686
+ app.post('/api/changes/discard', (_req, res) => {
687
+ changeManager.discardAll();
688
+ res.json({ ok: true });
689
+ });
690
+ // 检测冲突
691
+ app.post('/api/changes/detect-conflicts', (_req, res) => {
692
+ const conflicts = changeManager.detectConflicts();
693
+ res.json({ ok: true, conflicts });
694
+ });
695
+ /* ========== P1-2: MCP 协议管理 API ========== */
696
+ // 获取当前 MCP 服务器配置
697
+ app.get('/api/mcp/config', (_req, res) => {
698
+ try {
699
+ const cfg = (0, config_1.loadConfig)();
700
+ const servers = (cfg.mcp?.servers ?? []).map((s) => ({
701
+ name: s.name,
702
+ command: s.command,
703
+ args: s.args ?? [],
704
+ env: s.env ? Object.keys(s.env) : [], // 只暴露环境变量名,不暴露值
705
+ }));
706
+ res.json({ ok: true, servers, count: servers.length });
707
+ }
708
+ catch (e) {
709
+ res.status(500).json({ ok: false, error: `读取配置失败: ${e instanceof Error ? e.message : String(e)}` });
710
+ }
711
+ });
712
+ // 测试指定 MCP 服务器连接(临时连接,列出工具后关闭)
713
+ app.post('/api/mcp/test', async (req, res) => {
714
+ const body = (req.body ?? {});
715
+ const name = typeof body?.name === 'string' ? body.name.trim() : '';
716
+ if (!name) {
717
+ res.status(400).json({ ok: false, error: '缺少 name 字段' });
718
+ return;
719
+ }
720
+ try {
721
+ const cfg = (0, config_1.loadConfig)();
722
+ const serverCfg = (cfg.mcp?.servers ?? []).find((s) => s.name === name);
723
+ if (!serverCfg) {
724
+ res.status(404).json({ ok: false, error: `未找到 MCP 服务器: ${name}` });
725
+ return;
726
+ }
727
+ const startTime = Date.now();
728
+ const client = await (0, mcp_client_1.connectMcp)(serverCfg);
729
+ const tools = await client.listTools();
730
+ const latencyMs = Date.now() - startTime;
731
+ await client.close().catch(() => undefined);
732
+ res.json({
733
+ ok: true,
734
+ name,
735
+ connected: true,
736
+ latencyMs,
737
+ toolCount: tools.length,
738
+ tools: tools.map((t) => ({
739
+ name: t.name,
740
+ description: (t.description || '').slice(0, 100),
741
+ })),
742
+ });
743
+ }
744
+ catch (e) {
745
+ res.json({
746
+ ok: false,
747
+ name,
748
+ connected: false,
749
+ error: `连接失败: ${e instanceof Error ? e.message : String(e)}`,
750
+ });
751
+ }
752
+ });
753
+ // 列出所有 MCP 服务器的工具(逐个连接测试,返回汇总)
754
+ app.get('/api/mcp/tools', async (_req, res) => {
755
+ try {
756
+ const cfg = (0, config_1.loadConfig)();
757
+ const servers = cfg.mcp?.servers ?? [];
758
+ const results = [];
759
+ for (const s of servers) {
760
+ try {
761
+ const client = await (0, mcp_client_1.connectMcp)(s);
762
+ const tools = await client.listTools();
763
+ await client.close().catch(() => undefined);
764
+ results.push({ name: s.name, ok: true, toolCount: tools.length });
765
+ }
766
+ catch (e) {
767
+ results.push({ name: s.name, ok: false, toolCount: 0, error: e instanceof Error ? e.message : String(e) });
768
+ }
769
+ }
770
+ res.json({ ok: true, servers: results, totalServers: servers.length });
771
+ }
772
+ catch (e) {
773
+ res.status(500).json({ ok: false, error: `获取 MCP 工具失败: ${e instanceof Error ? e.message : String(e)}` });
774
+ }
775
+ });
776
+ /* ========== P2: 代码补全 API(真实接入 CompletionEngine) ========== */
777
+ app.post('/api/completion', async (req, res) => {
778
+ const body = (req.body ?? {});
779
+ const filePath = typeof body?.filePath === 'string' ? body.filePath.trim() : '';
780
+ const fileContent = typeof body?.fileContent === 'string' ? body.fileContent : '';
781
+ const cursorOffset = typeof body?.cursorOffset === 'number' ? body.cursorOffset : fileContent.length;
782
+ const mode = body?.mode === 'full' ? 'full' : 'quick';
783
+ const language = typeof body?.language === 'string' ? body.language : undefined;
784
+ const crossFileContext = typeof body?.crossFileContext === 'string' ? body.crossFileContext : undefined;
785
+ if (!filePath || !fileContent) {
786
+ res.status(400).json({ ok: false, error: '缺少 filePath 或 fileContent' });
787
+ return;
788
+ }
789
+ if (!completionEngine) {
790
+ res.json({ ok: true, suggestions: [], latencyMs: 0, model: '', cached: false, note: '未配置模型,补全不可用' });
791
+ return;
792
+ }
793
+ try {
794
+ const result = await completionEngine.complete({
795
+ filePath,
796
+ fileContent,
797
+ cursorOffset,
798
+ mode,
799
+ language,
800
+ crossFileContext,
801
+ });
802
+ res.json({ ok: true, ...result });
803
+ }
804
+ catch (e) {
805
+ res.status(500).json({ ok: false, error: '补全失败: ' + e.message, suggestions: [] });
806
+ }
807
+ });
808
+ app.post('/api/lint', (req, res) => {
809
+ const body = (req.body ?? {});
810
+ const code = typeof body?.code === 'string' ? body.code : '';
811
+ const language = typeof body?.language === 'string' ? body.language : undefined;
812
+ if (!code) {
813
+ res.status(400).json({ ok: false, error: '缺少 code' });
814
+ return;
815
+ }
816
+ const errors = (0, lint_1.lintSnippet)(code, language);
817
+ res.json({ ok: true, errors, clean: errors.length === 0 });
818
+ });
819
+ /* ========== 阶段一-2:补全 Pro API ========== */
820
+ // 记录用户接受的补全(用于连续推荐)
821
+ app.post('/api/completion/accept', (req, res) => {
822
+ const body = (req.body ?? {});
823
+ const filePath = typeof body?.filePath === 'string' ? body.filePath.trim() : '';
824
+ const cursorOffset = typeof body?.cursorOffset === 'number' ? body.cursorOffset : 0;
825
+ const text = typeof body?.text === 'string' ? body.text : '';
826
+ if (!filePath || !text) {
827
+ res.status(400).json({ ok: false, error: '缺少 filePath 或 text' });
828
+ return;
829
+ }
830
+ if (!completionEngine) {
831
+ res.json({ ok: false, error: '补全引擎未初始化' });
832
+ return;
833
+ }
834
+ try {
835
+ completionEngine.recordAcceptance(filePath, cursorOffset, text);
836
+ res.json({ ok: true });
837
+ }
838
+ catch (e) {
839
+ res.status(500).json({ ok: false, error: e.message });
840
+ }
841
+ });
842
+ // 推荐下一个改动点(补全 Pro 连续推荐)
843
+ app.post('/api/completion/suggest-next', (req, res) => {
844
+ const body = (req.body ?? {});
845
+ const filePath = typeof body?.filePath === 'string' ? body.filePath.trim() : '';
846
+ const fileContent = typeof body?.fileContent === 'string' ? body.fileContent : '';
847
+ const cursorOffset = typeof body?.cursorOffset === 'number' ? body.cursorOffset : fileContent.length;
848
+ if (!filePath || !fileContent) {
849
+ res.status(400).json({ ok: false, error: '缺少 filePath 或 fileContent' });
850
+ return;
851
+ }
852
+ if (!completionEngine) {
853
+ res.json({ ok: true, suggested: false });
854
+ return;
855
+ }
856
+ try {
857
+ const suggestion = completionEngine.suggestNext(filePath, fileContent, cursorOffset);
858
+ res.json({ ok: true, ...suggestion });
859
+ }
860
+ catch (e) {
861
+ res.status(500).json({ ok: false, error: e.message });
862
+ }
863
+ });
864
+ /* ========== P2-1: 设计稿转代码 API(多模态,图片→代码) ========== */
865
+ // P1-3: 设计稿转代码引擎状态查询
866
+ app.get('/api/design-to-code', (_req, res) => {
867
+ res.json({
868
+ ok: true,
869
+ configured: !!designToCodeEngine,
870
+ framework: designToCodeEngine ? 'html' : null,
871
+ note: designToCodeEngine ? '已配置多模态模型' : '未配置多模态模型(POST /api/design-to-code 需 vision 模型)',
872
+ });
873
+ });
874
+ app.post('/api/design-to-code', async (req, res) => {
875
+ const body = (req.body ?? {});
876
+ const image = typeof body?.image === 'string' ? body.image.trim() : '';
877
+ const framework = body?.framework || 'html';
878
+ const instructions = typeof body?.instructions === 'string' ? body.instructions : undefined;
879
+ const previousCode = typeof body?.previousCode === 'string' ? body.previousCode : undefined;
880
+ const feedback = typeof body?.feedback === 'string' ? body.feedback : undefined;
881
+ if (!image) {
882
+ res.status(400).json({ ok: false, error: '缺少 image 字段(base64 或 URL)' });
883
+ return;
884
+ }
885
+ if (!designToCodeEngine) {
886
+ res.status(503).json({ ok: false, error: '未配置多模态模型。请在模型设置中添加支持 vision 能力的模型(如 GPT-4V、Claude 3、通义千问 VL 等)。' });
887
+ return;
888
+ }
889
+ try {
890
+ const result = await designToCodeEngine.generate({
891
+ image,
892
+ framework,
893
+ instructions,
894
+ previousCode,
895
+ feedback,
896
+ });
897
+ res.json({ ok: true, ...result });
898
+ }
899
+ catch (e) {
900
+ res.status(500).json({ ok: false, error: e.message });
901
+ }
902
+ });
903
+ /* ========== P2-2: Git 集成 API ========== */
904
+ // 获取 Git 状态
905
+ app.get('/api/git/status', async (_req, res) => {
906
+ try {
907
+ const status = await gitIntegration.status();
908
+ res.json({ ok: true, ...status });
909
+ }
910
+ catch (e) {
911
+ res.status(500).json({ ok: false, error: e.message });
912
+ }
913
+ });
914
+ // 获取文件 diff
915
+ app.post('/api/git/diff', async (req, res) => {
916
+ const body = (req.body ?? {});
917
+ const path = typeof body?.path === 'string' ? body.path : undefined;
918
+ const staged = body?.staged === true;
919
+ try {
920
+ const diffs = await gitIntegration.diff(path, staged);
921
+ res.json({ ok: true, diffs });
922
+ }
923
+ catch (e) {
924
+ res.status(500).json({ ok: false, error: e.message });
925
+ }
926
+ });
927
+ // 暂存文件
928
+ app.post('/api/git/add', async (req, res) => {
929
+ const body = (req.body ?? {});
930
+ const paths = Array.isArray(body?.paths) ? body.paths.filter((p) => typeof p === 'string') : [];
931
+ if (!paths.length) {
932
+ res.status(400).json({ ok: false, error: '缺少 paths 字段' });
933
+ return;
934
+ }
935
+ try {
936
+ const result = await gitIntegration.add(paths);
937
+ res.json({ ok: result.success, message: result.message });
938
+ }
939
+ catch (e) {
940
+ res.status(500).json({ ok: false, error: e.message });
941
+ }
942
+ });
943
+ // 取消暂存
944
+ app.post('/api/git/reset', async (req, res) => {
945
+ const body = (req.body ?? {});
946
+ const paths = Array.isArray(body?.paths) ? body.paths.filter((p) => typeof p === 'string') : [];
947
+ if (!paths.length) {
948
+ res.status(400).json({ ok: false, error: '缺少 paths 字段' });
949
+ return;
950
+ }
951
+ try {
952
+ const result = await gitIntegration.reset(paths);
953
+ res.json({ ok: result.success, message: result.message });
954
+ }
955
+ catch (e) {
956
+ res.status(500).json({ ok: false, error: e.message });
957
+ }
958
+ });
959
+ // 提交
960
+ app.post('/api/git/commit', async (req, res) => {
961
+ const body = (req.body ?? {});
962
+ const message = typeof body?.message === 'string' ? body.message.trim() : '';
963
+ const paths = Array.isArray(body?.paths) ? body.paths.filter((p) => typeof p === 'string') : undefined;
964
+ if (!message) {
965
+ res.status(400).json({ ok: false, error: '提交信息不能为空' });
966
+ return;
967
+ }
968
+ try {
969
+ const result = await gitIntegration.commit(message, paths);
970
+ res.json({ ok: result.success, hash: result.hash, message: result.message });
971
+ }
972
+ catch (e) {
973
+ res.status(500).json({ ok: false, error: e.message });
974
+ }
975
+ });
976
+ // 获取分支列表
977
+ app.get('/api/git/branches', async (_req, res) => {
978
+ try {
979
+ const branches = await gitIntegration.branches();
980
+ res.json({ ok: true, branches });
981
+ }
982
+ catch (e) {
983
+ res.status(500).json({ ok: false, error: e.message });
984
+ }
985
+ });
986
+ // 切换分支
987
+ app.post('/api/git/checkout', async (req, res) => {
988
+ const body = (req.body ?? {});
989
+ const branch = typeof body?.branch === 'string' ? body.branch.trim() : '';
990
+ const createNew = body?.createNew === true;
991
+ if (!branch) {
992
+ res.status(400).json({ ok: false, error: '缺少 branch 字段' });
993
+ return;
994
+ }
995
+ try {
996
+ const result = await gitIntegration.checkout(branch, createNew);
997
+ res.json({ ok: result.success, message: result.message });
998
+ }
999
+ catch (e) {
1000
+ res.status(500).json({ ok: false, error: e.message });
1001
+ }
1002
+ });
1003
+ // 获取提交历史
1004
+ app.get('/api/git/log', async (req, res) => {
1005
+ const count = typeof req.query?.count === 'string' ? parseInt(req.query.count) : 20;
1006
+ try {
1007
+ const commits = await gitIntegration.log(Math.min(Math.max(count, 1), 100));
1008
+ res.json({ ok: true, commits });
1009
+ }
1010
+ catch (e) {
1011
+ res.status(500).json({ ok: false, error: e.message });
1012
+ }
1013
+ });
1014
+ // 推送
1015
+ app.post('/api/git/push', async (req, res) => {
1016
+ const body = (req.body ?? {});
1017
+ const remote = typeof body?.remote === 'string' ? body.remote : 'origin';
1018
+ const branch = typeof body?.branch === 'string' ? body.branch : undefined;
1019
+ const force = body?.force === true;
1020
+ try {
1021
+ const result = await gitIntegration.push(remote, branch, force);
1022
+ res.json({ ok: result.success, message: result.message });
1023
+ }
1024
+ catch (e) {
1025
+ res.status(500).json({ ok: false, error: e.message });
1026
+ }
1027
+ });
1028
+ // 拉取
1029
+ app.post('/api/git/pull', async (req, res) => {
1030
+ const body = (req.body ?? {});
1031
+ const remote = typeof body?.remote === 'string' ? body.remote : 'origin';
1032
+ const branch = typeof body?.branch === 'string' ? body.branch : undefined;
1033
+ try {
1034
+ const result = await gitIntegration.pull(remote, branch);
1035
+ res.json({ ok: result.success, message: result.message });
1036
+ }
1037
+ catch (e) {
1038
+ res.status(500).json({ ok: false, error: e.message });
1039
+ }
1040
+ });
1041
+ // 初始化仓库
1042
+ app.post('/api/git/init', async (_req, res) => {
1043
+ try {
1044
+ const result = await gitIntegration.init();
1045
+ res.json({ ok: result.success, message: result.message });
1046
+ }
1047
+ catch (e) {
1048
+ res.status(500).json({ ok: false, error: e.message });
1049
+ }
1050
+ });
1051
+ /* ========== P2-3: 团队协作 API ========== */
1052
+ app.get('/api/team', (_req, res) => {
1053
+ try {
1054
+ const team = teamManager.getTeam();
1055
+ const stats = teamManager.getStats();
1056
+ res.json({ ok: true, team, stats });
1057
+ }
1058
+ catch (e) {
1059
+ res.status(500).json({ ok: false, error: e.message });
1060
+ }
1061
+ });
1062
+ app.put('/api/team/config', (req, res) => {
1063
+ try {
1064
+ const config = teamManager.updateConfig(req.body || {});
1065
+ res.json({ ok: true, config });
1066
+ }
1067
+ catch (e) {
1068
+ res.status(500).json({ ok: false, error: e.message });
1069
+ }
1070
+ });
1071
+ app.get('/api/team/members', (_req, res) => {
1072
+ try {
1073
+ res.json({ ok: true, members: teamManager.getMembers() });
1074
+ }
1075
+ catch (e) {
1076
+ res.status(500).json({ ok: false, error: e.message });
1077
+ }
1078
+ });
1079
+ app.post('/api/team/members/invite', (req, res) => {
1080
+ const body = (req.body ?? {});
1081
+ const name = typeof body?.name === 'string' ? body.name.trim() : '';
1082
+ const email = typeof body?.email === 'string' ? body.email.trim() : '';
1083
+ const role = body?.role || 'developer';
1084
+ if (!name || !email) {
1085
+ res.status(400).json({ ok: false, error: '缺少 name 或 email' });
1086
+ return;
1087
+ }
1088
+ try {
1089
+ const member = teamManager.inviteMember(name, email, role);
1090
+ res.json({ ok: true, member });
1091
+ }
1092
+ catch (e) {
1093
+ res.status(400).json({ ok: false, error: e.message });
1094
+ }
1095
+ });
1096
+ app.put('/api/team/members/:id/role', (req, res) => {
1097
+ try {
1098
+ const role = (req.body ?? {}).role;
1099
+ const member = teamManager.updateMemberRole(req.params.id, role);
1100
+ res.json({ ok: true, member });
1101
+ }
1102
+ catch (e) {
1103
+ res.status(400).json({ ok: false, error: e.message });
1104
+ }
1105
+ });
1106
+ app.delete('/api/team/members/:id', (req, res) => {
1107
+ try {
1108
+ teamManager.removeMember(req.params.id);
1109
+ res.json({ ok: true });
1110
+ }
1111
+ catch (e) {
1112
+ res.status(400).json({ ok: false, error: e.message });
1113
+ }
1114
+ });
1115
+ app.get('/api/team/tasks', (req, res) => {
1116
+ try {
1117
+ const status = typeof req.query?.status === 'string' ? req.query.status : undefined;
1118
+ res.json({ ok: true, tasks: teamManager.getTasks(status) });
1119
+ }
1120
+ catch (e) {
1121
+ res.status(500).json({ ok: false, error: e.message });
1122
+ }
1123
+ });
1124
+ app.post('/api/team/tasks', (req, res) => {
1125
+ const body = (req.body ?? {});
1126
+ const title = typeof body?.title === 'string' ? body.title.trim() : '';
1127
+ if (!title) {
1128
+ res.status(400).json({ ok: false, error: '缺少 title' });
1129
+ return;
1130
+ }
1131
+ try {
1132
+ const task = teamManager.createTask(title, body.createdBy || 'system', body);
1133
+ res.json({ ok: true, task });
1134
+ }
1135
+ catch (e) {
1136
+ res.status(500).json({ ok: false, error: e.message });
1137
+ }
1138
+ });
1139
+ app.put('/api/team/tasks/:id', (req, res) => {
1140
+ try {
1141
+ res.json({ ok: true, task: teamManager.updateTask(req.params.id, req.body || {}) });
1142
+ }
1143
+ catch (e) {
1144
+ res.status(400).json({ ok: false, error: e.message });
1145
+ }
1146
+ });
1147
+ app.delete('/api/team/tasks/:id', (req, res) => {
1148
+ try {
1149
+ teamManager.deleteTask(req.params.id);
1150
+ res.json({ ok: true });
1151
+ }
1152
+ catch (e) {
1153
+ res.status(400).json({ ok: false, error: e.message });
1154
+ }
1155
+ });
1156
+ app.post('/api/team/tasks/:id/comments', (req, res) => {
1157
+ const body = (req.body ?? {});
1158
+ const content = typeof body?.content === 'string' ? body.content.trim() : '';
1159
+ if (!content) {
1160
+ res.status(400).json({ ok: false, error: '缺少 content' });
1161
+ return;
1162
+ }
1163
+ try {
1164
+ const task = teamManager.addComment(req.params.id, body.author || 'anonymous', content);
1165
+ res.json({ ok: true, task });
1166
+ }
1167
+ catch (e) {
1168
+ res.status(400).json({ ok: false, error: e.message });
1169
+ }
1170
+ });
1171
+ app.get('/api/team/stats', (_req, res) => {
1172
+ try {
1173
+ res.json({ ok: true, stats: teamManager.getStats() });
1174
+ }
1175
+ catch (e) {
1176
+ res.status(500).json({ ok: false, error: e.message });
1177
+ }
1178
+ });
1179
+ /* ========== P3-2: SOLO 全自主编程 API ========== */
1180
+ // 启动 SOLO 任务
1181
+ app.post('/api/solo/run', async (req, res) => {
1182
+ const body = (req.body ?? {});
1183
+ const goal = typeof body?.goal === 'string' ? body.goal.trim() : '';
1184
+ if (!goal) {
1185
+ res.status(400).json({ ok: false, error: '缺少 goal' });
1186
+ return;
1187
+ }
1188
+ const taskId = `solo-${Date.now()}`;
1189
+ const config = {
1190
+ cwd: serverWorkspaceDir,
1191
+ goal,
1192
+ maxTasks: body.maxTasks || 8,
1193
+ maxRetries: body.maxRetries || 3,
1194
+ parallel: body.parallel || false,
1195
+ maxParallel: body.maxParallel || 2,
1196
+ planOnly: body.planOnly || false,
1197
+ verifyOnly: body.verifyOnly || false,
1198
+ autoCorrect: body.autoCorrect !== false,
1199
+ };
1200
+ // 创建子任务执行器(简化版,实际应委托给 orchestrator)
1201
+ const executor = async (focusedGoal) => {
1202
+ try {
1203
+ // 简化执行:返回成功,实际应调用 orchestrator
1204
+ return { ok: true, output: `已执行: ${focusedGoal}`, touchedFiles: [], iterations: 1 };
1205
+ }
1206
+ catch (e) {
1207
+ return { ok: false, output: e.message, touchedFiles: [], iterations: 0 };
1208
+ }
1209
+ };
1210
+ const agent = new solo_agent_1.SoloAgent(config, executor);
1211
+ soloTasks.set(taskId, { agent, status: 'running' });
1212
+ // 异步执行
1213
+ (async () => {
1214
+ try {
1215
+ const report = await agent.run();
1216
+ const task = soloTasks.get(taskId);
1217
+ if (task) {
1218
+ task.report = report;
1219
+ task.status = report.overall === 'failed' ? 'failed' : 'completed';
1220
+ }
1221
+ }
1222
+ catch (e) {
1223
+ const task = soloTasks.get(taskId);
1224
+ if (task) {
1225
+ task.status = 'failed';
1226
+ task.report = {
1227
+ goal, cwd: serverWorkspaceDir, overall: 'failed', phase: 'failed',
1228
+ startedAt: new Date().toISOString(), completedAt: new Date().toISOString(),
1229
+ totalTasks: 0, completedTasks: 0, failedTasks: 0, skippedTasks: 0,
1230
+ tasks: [], summary: `执行失败: ${e.message}`, events: [],
1231
+ };
1232
+ }
1233
+ }
1234
+ })();
1235
+ res.json({ ok: true, taskId, message: 'SOLO 任务已启动' });
1236
+ });
1237
+ // 获取 SOLO 任务状态
1238
+ app.get('/api/solo/status/:id', (req, res) => {
1239
+ const task = soloTasks.get(req.params.id);
1240
+ if (!task) {
1241
+ res.status(404).json({ ok: false, error: '任务不存在' });
1242
+ return;
1243
+ }
1244
+ const status = task.agent.getStatus();
1245
+ res.json({ ok: true, taskId: req.params.id, status: task.status, ...status });
1246
+ });
1247
+ // 获取 SOLO 任务报告
1248
+ app.get('/api/solo/report/:id', (req, res) => {
1249
+ const task = soloTasks.get(req.params.id);
1250
+ if (!task) {
1251
+ res.status(404).json({ ok: false, error: '任务不存在' });
1252
+ return;
1253
+ }
1254
+ if (!task.report) {
1255
+ res.status(202).json({ ok: false, error: '任务尚未完成', status: task.status });
1256
+ return;
1257
+ }
1258
+ res.json({ ok: true, report: task.report });
1259
+ });
1260
+ // 取消 SOLO 任务
1261
+ app.post('/api/solo/cancel/:id', (req, res) => {
1262
+ const task = soloTasks.get(req.params.id);
1263
+ if (!task) {
1264
+ res.status(404).json({ ok: false, error: '任务不存在' });
1265
+ return;
1266
+ }
1267
+ if (task.status === 'running') {
1268
+ task.status = 'failed';
1269
+ res.json({ ok: true, message: '任务已取消' });
1270
+ }
1271
+ else {
1272
+ res.json({ ok: false, error: '任务已结束,无法取消' });
1273
+ }
1274
+ });
1275
+ // 列出所有 SOLO 任务
1276
+ app.get('/api/solo/list', (_req, res) => {
1277
+ const tasks = Array.from(soloTasks.entries()).map(([id, task]) => ({
1278
+ id,
1279
+ status: task.status,
1280
+ goal: task.report?.goal || '',
1281
+ startedAt: task.report?.startedAt,
1282
+ completedAt: task.report?.completedAt,
1283
+ }));
1284
+ res.json({ ok: true, tasks });
1285
+ });
1286
+ /* ========== 阶段二-1:多智能体协同 API ========== */
1287
+ // 运行多智能体协同(架构师/开发/测试/评审)
1288
+ app.post('/api/multi-agent/run', async (req, res) => {
1289
+ const body = (req.body ?? {});
1290
+ const goal = typeof body?.goal === 'string' ? body.goal.trim() : '';
1291
+ if (!goal) {
1292
+ res.status(400).json({ ok: false, error: '缺少 goal' });
1293
+ return;
1294
+ }
1295
+ if (!sharedModelRouter) {
1296
+ res.status(500).json({ ok: false, error: '模型未配置' });
1297
+ return;
1298
+ }
1299
+ const config = {
1300
+ roles: body.roles || ['architect', 'developer', 'tester', 'reviewer'],
1301
+ maxRounds: body.maxRounds || 3,
1302
+ enableReviewLoop: body.enableReviewLoop !== false,
1303
+ };
1304
+ try {
1305
+ const result = await (0, multi_agent_1.runMultiAgent)(sharedModelRouter, goal, config, body.context);
1306
+ res.json({ ok: true, ...result });
1307
+ }
1308
+ catch (e) {
1309
+ res.status(500).json({ ok: false, error: '多智能体协同失败: ' + e.message });
1310
+ }
1311
+ });
1312
+ // 获取角色配置
1313
+ app.get('/api/multi-agent/roles', (_req, res) => {
1314
+ try {
1315
+ Promise.resolve().then(() => __importStar(require('../agent/multi-agent'))).then(({ AGENT_ROLES }) => {
1316
+ const roles = Object.values(AGENT_ROLES).map((r) => ({
1317
+ role: r.role,
1318
+ name: r.name,
1319
+ description: r.description,
1320
+ capabilities: r.capabilities,
1321
+ }));
1322
+ res.json({ ok: true, roles });
1323
+ });
1324
+ }
1325
+ catch (e) {
1326
+ res.status(500).json({ ok: false, error: e.message });
1327
+ }
1328
+ });
1329
+ /* ========== 阶段二-2:事件驱动 Agent API ========== */
1330
+ // 获取事件驱动配置
1331
+ app.get('/api/event-driven/config', (_req, res) => {
1332
+ try {
1333
+ res.json({ ok: true, config: eventDrivenManager.getConfig() });
1334
+ }
1335
+ catch (e) {
1336
+ res.status(500).json({ ok: false, error: e.message });
1337
+ }
1338
+ });
1339
+ // 获取事件历史
1340
+ app.get('/api/event-driven/events', (req, res) => {
1341
+ try {
1342
+ const limit = typeof req.query?.limit === 'string' ? parseInt(req.query.limit, 10) : 50;
1343
+ res.json({ ok: true, events: eventDrivenManager.getEvents(limit) });
1344
+ }
1345
+ catch (e) {
1346
+ res.status(500).json({ ok: false, error: e.message });
1347
+ }
1348
+ });
1349
+ // 手动触发事件
1350
+ app.post('/api/event-driven/trigger', (req, res) => {
1351
+ const body = (req.body ?? {});
1352
+ if (!body.type || !body.title) {
1353
+ res.status(400).json({ ok: false, error: '缺少 type 或 title' });
1354
+ return;
1355
+ }
1356
+ eventDrivenManager.triggerEvent({
1357
+ type: body.type,
1358
+ severity: body.severity || 'info',
1359
+ title: body.title,
1360
+ description: body.description || '',
1361
+ source: body.source || 'manual',
1362
+ payload: body.payload,
1363
+ }).then((event) => res.json({ ok: true, event }))
1364
+ .catch((e) => res.status(500).json({ ok: false, error: e.message }));
1365
+ });
1366
+ // Cron 任务 CRUD
1367
+ app.get('/api/event-driven/cron', (_req, res) => {
1368
+ try {
1369
+ const cfg = eventDrivenManager.getConfig();
1370
+ res.json({ ok: true, cronTasks: cfg.cronTasks ?? [] });
1371
+ }
1372
+ catch (e) {
1373
+ res.status(500).json({ ok: false, error: e.message });
1374
+ }
1375
+ });
1376
+ app.post('/api/event-driven/cron', (req, res) => {
1377
+ const body = (req.body ?? {});
1378
+ if (!body.name || !body.cron || !body.task) {
1379
+ res.status(400).json({ ok: false, error: '缺少 name/cron/task' });
1380
+ return;
1381
+ }
1382
+ try {
1383
+ const task = eventDrivenManager.addCronTask({
1384
+ name: body.name,
1385
+ cron: body.cron,
1386
+ task: body.task,
1387
+ enabled: body.enabled !== false,
1388
+ debounceMs: body.debounceMs || 0,
1389
+ });
1390
+ res.json({ ok: true, task });
1391
+ }
1392
+ catch (e) {
1393
+ res.status(500).json({ ok: false, error: e.message });
1394
+ }
1395
+ });
1396
+ app.put('/api/event-driven/cron/:id', (req, res) => {
1397
+ try {
1398
+ const task = eventDrivenManager.updateCronTask(req.params.id, (req.body ?? {}));
1399
+ if (!task) {
1400
+ res.status(404).json({ ok: false, error: '任务不存在' });
1401
+ return;
1402
+ }
1403
+ res.json({ ok: true, task });
1404
+ }
1405
+ catch (e) {
1406
+ res.status(500).json({ ok: false, error: e.message });
1407
+ }
1408
+ });
1409
+ app.delete('/api/event-driven/cron/:id', (req, res) => {
1410
+ try {
1411
+ const ok = eventDrivenManager.removeCronTask(req.params.id);
1412
+ res.json({ ok, message: ok ? '已删除' : '任务不存在' });
1413
+ }
1414
+ catch (e) {
1415
+ res.status(500).json({ ok: false, error: e.message });
1416
+ }
1417
+ });
1418
+ // Webhook 接收
1419
+ app.post('/api/event-driven/webhook/:path', (req, res) => {
1420
+ const path = `/webhook/${req.params.path}`;
1421
+ eventDrivenManager.handleWebhook(path, (req.body || {}), req.headers)
1422
+ .then((result) => res.json(result))
1423
+ .catch((e) => res.status(500).json({ ok: false, error: e.message }));
1424
+ });
1425
+ /* ========== 阶段二-3:自定义 Agent API ========== */
1426
+ // 获取所有自定义 Agent
1427
+ app.get('/api/custom-agents', (req, res) => {
1428
+ try {
1429
+ const category = typeof req.query?.category === 'string' ? req.query.category : undefined;
1430
+ res.json({ ok: true, agents: customAgentManager.getAllAgents(category) });
1431
+ }
1432
+ catch (e) {
1433
+ res.status(500).json({ ok: false, error: e.message });
1434
+ }
1435
+ });
1436
+ // 获取 Agent 分类
1437
+ app.get('/api/custom-agents/categories', (_req, res) => {
1438
+ try {
1439
+ res.json({ ok: true, categories: customAgentManager.getCategories() });
1440
+ }
1441
+ catch (e) {
1442
+ res.status(500).json({ ok: false, error: e.message });
1443
+ }
1444
+ });
1445
+ // 匹配推荐 Agent
1446
+ app.post('/api/custom-agents/match', (req, res) => {
1447
+ const body = (req.body ?? {});
1448
+ const input = typeof body?.input === 'string' ? body.input : '';
1449
+ if (!input) {
1450
+ res.status(400).json({ ok: false, error: '缺少 input' });
1451
+ return;
1452
+ }
1453
+ try {
1454
+ const limit = typeof body?.limit === 'number' ? body.limit : 3;
1455
+ res.json({ ok: true, agents: customAgentManager.matchAgents(input, limit) });
1456
+ }
1457
+ catch (e) {
1458
+ res.status(500).json({ ok: false, error: e.message });
1459
+ }
1460
+ });
1461
+ // 获取单个 Agent
1462
+ app.get('/api/custom-agents/:id', (req, res) => {
1463
+ try {
1464
+ const agent = customAgentManager.getAgent(req.params.id);
1465
+ if (!agent) {
1466
+ res.status(404).json({ ok: false, error: 'Agent 不存在' });
1467
+ return;
1468
+ }
1469
+ res.json({ ok: true, agent });
1470
+ }
1471
+ catch (e) {
1472
+ res.status(500).json({ ok: false, error: e.message });
1473
+ }
1474
+ });
1475
+ // 创建自定义 Agent
1476
+ app.post('/api/custom-agents', (req, res) => {
1477
+ const body = (req.body ?? {});
1478
+ if (!body.name || !body.systemPrompt) {
1479
+ res.status(400).json({ ok: false, error: '缺少 name 或 systemPrompt' });
1480
+ return;
1481
+ }
1482
+ try {
1483
+ const agent = customAgentManager.createAgent({
1484
+ name: body.name,
1485
+ description: body.description || '',
1486
+ systemPrompt: body.systemPrompt,
1487
+ tools: body.tools || [],
1488
+ modelConfig: body.modelConfig || { temperature: 0.3, maxTokens: 2000, timeoutMs: 30000 },
1489
+ triggers: body.triggers || [],
1490
+ icon: body.icon || '🤖',
1491
+ category: body.category || 'custom',
1492
+ enabled: body.enabled !== false,
1493
+ author: body.author,
1494
+ version: body.version || '1.0.0',
1495
+ });
1496
+ res.json({ ok: true, agent });
1497
+ }
1498
+ catch (e) {
1499
+ res.status(500).json({ ok: false, error: e.message });
1500
+ }
1501
+ });
1502
+ // 更新自定义 Agent
1503
+ app.put('/api/custom-agents/:id', (req, res) => {
1504
+ try {
1505
+ const agent = customAgentManager.updateAgent(req.params.id, (req.body ?? {}));
1506
+ if (!agent) {
1507
+ res.status(404).json({ ok: false, error: 'Agent 不存在' });
1508
+ return;
1509
+ }
1510
+ res.json({ ok: true, agent });
1511
+ }
1512
+ catch (e) {
1513
+ res.status(500).json({ ok: false, error: e.message });
1514
+ }
1515
+ });
1516
+ // 删除自定义 Agent
1517
+ app.delete('/api/custom-agents/:id', (req, res) => {
1518
+ try {
1519
+ const ok = customAgentManager.deleteAgent(req.params.id);
1520
+ res.json({ ok, message: ok ? '已删除' : 'Agent 不存在或为内置 Agent' });
1521
+ }
1522
+ catch (e) {
1523
+ res.status(500).json({ ok: false, error: e.message });
1524
+ }
1525
+ });
1526
+ // 执行自定义 Agent
1527
+ app.post('/api/custom-agents/:id/execute', async (req, res) => {
1528
+ const body = (req.body ?? {});
1529
+ const input = typeof body?.input === 'string' ? body.input : '';
1530
+ if (!input) {
1531
+ res.status(400).json({ ok: false, error: '缺少 input' });
1532
+ return;
1533
+ }
1534
+ if (!sharedModelRouter) {
1535
+ res.status(500).json({ ok: false, error: '模型未配置' });
1536
+ return;
1537
+ }
1538
+ try {
1539
+ const result = await customAgentManager.executeAgent(req.params.id, input, sharedModelRouter, body?.context);
1540
+ res.json({ ok: result.success, ...result });
1541
+ }
1542
+ catch (e) {
1543
+ res.status(500).json({ ok: false, error: e.message });
1544
+ }
1545
+ });
539
1546
  /* ========== 打开本地文件夹/浏览器 ========== */
540
1547
  app.post('/api/open/folder', (req, res) => {
541
1548
  const body = (req.body ?? {});
@@ -638,18 +1645,18 @@ function startWebServer(opts = {}) {
638
1645
  res.status(400).json({ ok: false, error: '仅支持 Windows 系统' });
639
1646
  return;
640
1647
  }
641
- const script = `
642
- Add-Type -AssemblyName System.Windows.Forms
643
- Add-Type -AssemblyName System.Drawing
644
- $screen = [System.Windows.Forms.Screen]::PrimaryScreen
645
- $bounds = $screen.Bounds
646
- $bitmap = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height
647
- $graphics = [System.Drawing.Graphics]::FromImage($bitmap)
648
- $graphics.CopyFromScreen($bounds.Location, [System.Drawing.Point]::Empty, $bounds.Size)
649
- $ms = New-Object System.IO.MemoryStream
650
- $bitmap.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
651
- $bytes = $ms.ToArray()
652
- [Convert]::ToBase64String($bytes)
1648
+ const script = `
1649
+ Add-Type -AssemblyName System.Windows.Forms
1650
+ Add-Type -AssemblyName System.Drawing
1651
+ $screen = [System.Windows.Forms.Screen]::PrimaryScreen
1652
+ $bounds = $screen.Bounds
1653
+ $bitmap = New-Object System.Drawing.Bitmap $bounds.Width, $bounds.Height
1654
+ $graphics = [System.Drawing.Graphics]::FromImage($bitmap)
1655
+ $graphics.CopyFromScreen($bounds.Location, [System.Drawing.Point]::Empty, $bounds.Size)
1656
+ $ms = New-Object System.IO.MemoryStream
1657
+ $bitmap.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png)
1658
+ $bytes = $ms.ToArray()
1659
+ [Convert]::ToBase64String($bytes)
653
1660
  `;
654
1661
  const base64 = await runPowerShell(script);
655
1662
  res.json({ ok: true, image: 'data:image/png;base64,' + base64, width: 1920, height: 1080 });
@@ -668,17 +1675,17 @@ function startWebServer(opts = {}) {
668
1675
  res.status(400).json({ ok: false, error: '缺少 x 或 y 坐标' });
669
1676
  return;
670
1677
  }
671
- const script = `
672
- Add-Type @"
673
- using System;
674
- using System.Runtime.InteropServices;
675
- public class MouseHelper {
676
- [DllImport("user32.dll")]
677
- public static extern bool SetCursorPos(int X, int Y);
678
- }
679
- "@
680
- [MouseHelper]::SetCursorPos(${x}, ${y}) | Out-Null
681
- Write-Output "ok"
1678
+ const script = `
1679
+ Add-Type @"
1680
+ using System;
1681
+ using System.Runtime.InteropServices;
1682
+ public class MouseHelper {
1683
+ [DllImport("user32.dll")]
1684
+ public static extern bool SetCursorPos(int X, int Y);
1685
+ }
1686
+ "@
1687
+ [MouseHelper]::SetCursorPos(${x}, ${y}) | Out-Null
1688
+ Write-Output "ok"
682
1689
  `;
683
1690
  await runPowerShell(script);
684
1691
  res.json({ ok: true, x, y });
@@ -705,20 +1712,20 @@ function startWebServer(opts = {}) {
705
1712
  const clickPart = doubleClick
706
1713
  ? `[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);`
707
1714
  : `[MouseHelper]::mouse_event(${clickFlag}, 0, 0, 0, 0); [MouseHelper]::mouse_event(${upFlag}, 0, 0, 0, 0);`;
708
- const script = `
709
- Add-Type @"
710
- using System;
711
- using System.Runtime.InteropServices;
712
- public class MouseHelper {
713
- [DllImport("user32.dll")]
714
- public static extern bool SetCursorPos(int X, int Y);
715
- [DllImport("user32.dll")]
716
- public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
717
- }
718
- "@
719
- ${movePart}
720
- ${clickPart}
721
- Write-Output "ok"
1715
+ const script = `
1716
+ Add-Type @"
1717
+ using System;
1718
+ using System.Runtime.InteropServices;
1719
+ public class MouseHelper {
1720
+ [DllImport("user32.dll")]
1721
+ public static extern bool SetCursorPos(int X, int Y);
1722
+ [DllImport("user32.dll")]
1723
+ public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
1724
+ }
1725
+ "@
1726
+ ${movePart}
1727
+ ${clickPart}
1728
+ Write-Output "ok"
722
1729
  `;
723
1730
  await runPowerShell(script);
724
1731
  res.json({ ok: true, button, x, y, double: doubleClick });
@@ -738,10 +1745,10 @@ function startWebServer(opts = {}) {
738
1745
  }
739
1746
  // SendKeys 需要转义特殊字符
740
1747
  const escaped = text.replace(/([+^%~(){}])/g, '{$1}');
741
- const script = `
742
- Add-Type -AssemblyName System.Windows.Forms
743
- [System.Windows.Forms.SendKeys]::SendWait('${escaped.replace(/'/g, "''")}')
744
- Write-Output "ok"
1748
+ const script = `
1749
+ Add-Type -AssemblyName System.Windows.Forms
1750
+ [System.Windows.Forms.SendKeys]::SendWait('${escaped.replace(/'/g, "''")}')
1751
+ Write-Output "ok"
745
1752
  `;
746
1753
  await runPowerShell(script);
747
1754
  res.json({ ok: true, text });
@@ -759,10 +1766,10 @@ function startWebServer(opts = {}) {
759
1766
  res.status(400).json({ ok: false, error: '缺少 key 字段' });
760
1767
  return;
761
1768
  }
762
- const script = `
763
- Add-Type -AssemblyName System.Windows.Forms
764
- [System.Windows.Forms.SendKeys]::SendWait('${key.replace(/'/g, "''")}')
765
- Write-Output "ok"
1769
+ const script = `
1770
+ Add-Type -AssemblyName System.Windows.Forms
1771
+ [System.Windows.Forms.SendKeys]::SendWait('${key.replace(/'/g, "''")}')
1772
+ Write-Output "ok"
766
1773
  `;
767
1774
  await runPowerShell(script);
768
1775
  res.json({ ok: true, key });
@@ -1700,6 +2707,8 @@ function startWebServer(opts = {}) {
1700
2707
  apiKey: resolveApiKey(m),
1701
2708
  model: m.name,
1702
2709
  })));
2710
+ // P2: 同步更新补全引擎
2711
+ rebuildCompletionEngine(encList);
1703
2712
  return ok;
1704
2713
  }
1705
2714
  // 初始化模型提供列表,并注册到任务队列
@@ -1711,6 +2720,52 @@ function startWebServer(opts = {}) {
1711
2720
  apiKey: resolveApiKey(m),
1712
2721
  model: m.name,
1713
2722
  })));
2723
+ /* ========== P2: 共享 ModelRouter + CompletionEngine(供 /api/completion 使用) ========== */
2724
+ let sharedModelRouter = null;
2725
+ let completionEngine = null;
2726
+ // P2-1: 设计稿转代码引擎(多模态)
2727
+ let designToCodeEngine = null;
2728
+ function rebuildCompletionEngine(models) {
2729
+ try {
2730
+ const providers = models
2731
+ .filter((m) => m.apiBase && m.name)
2732
+ .map((m) => new openai_compatible_provider_1.OpenAICompatibleProvider({
2733
+ id: m.id,
2734
+ type: 'openai-compatible',
2735
+ baseURL: m.apiBase,
2736
+ apiKey: resolveApiKey(m),
2737
+ model: m.name,
2738
+ tags: ['code-gen', 'cheap'],
2739
+ }));
2740
+ if (!providers.length) {
2741
+ sharedModelRouter = null;
2742
+ completionEngine = null;
2743
+ designToCodeEngine = null;
2744
+ return;
2745
+ }
2746
+ sharedModelRouter = new model_router_1.ModelRouter(providers, 'capability', 1.0);
2747
+ // 阶段一-1:构建代码图谱,用于跨文件上下文注入
2748
+ const codeGraph = (0, symbol_index_1.buildCodeGraph)(serverWorkspaceDir, { maxFiles: 500 });
2749
+ completionEngine = (0, completion_engine_1.createCompletionEngine)(sharedModelRouter, {
2750
+ maxPrefixChars: 1500,
2751
+ maxSuffixChars: 500,
2752
+ quickMaxTokens: 64,
2753
+ fullMaxTokens: 256,
2754
+ cacheTtlMs: 10000,
2755
+ enableSyntaxCheck: true,
2756
+ }, codeGraph);
2757
+ // P2-1: 更新设计稿转代码引擎的模型提供者
2758
+ designToCodeEngine = (0, design_to_code_1.createDesignToCodeEngine)(providers);
2759
+ }
2760
+ catch (e) {
2761
+ console.error('[completion] rebuild failed:', e);
2762
+ sharedModelRouter = null;
2763
+ completionEngine = null;
2764
+ designToCodeEngine = null;
2765
+ }
2766
+ }
2767
+ // 初始化时构建
2768
+ rebuildCompletionEngine(initialModels);
1714
2769
  // 第二重(通信层):向客户端下发 RSA 公钥,用于加密敏感参数(如模型 API Key)传输
1715
2770
  app.get('/api/security/public-key', (_req, res) => {
1716
2771
  res.json({ ok: true, publicKey: rsaKeys.publicKey, algorithm: 'RSA-OAEP-2048-SHA256' });
@@ -1719,6 +2774,22 @@ function startWebServer(opts = {}) {
1719
2774
  const list = loadModels().map(publicModel);
1720
2775
  res.json({ ok: true, models: list, defaultId: (list.find((m) => m.default) || {}).id || null });
1721
2776
  });
2777
+ // P1-3: 模型提供商聚合视图(按 apiBase 分组)
2778
+ app.get('/api/models/providers', (_req, res) => {
2779
+ const list = loadModels().map(publicModel);
2780
+ const byKey = new Map();
2781
+ for (const m of list) {
2782
+ const key = m.apiBase || 'custom';
2783
+ let entry = byKey.get(key);
2784
+ if (!entry) {
2785
+ entry = { providerId: key, apiBase: key, models: [], count: 0 };
2786
+ byKey.set(key, entry);
2787
+ }
2788
+ entry.models.push(m);
2789
+ entry.count++;
2790
+ }
2791
+ res.json({ ok: true, providers: Array.from(byKey.values()), total: list.length });
2792
+ });
1722
2793
  app.post('/api/models', (req, res) => {
1723
2794
  const body = req.body;
1724
2795
  const name = typeof body?.name === 'string' ? body.name.trim() : '';