feihong-code 7.0.0 → 7.2.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 (236) hide show
  1. package/.github/workflows/release.yml +173 -0
  2. package/CHANGELOG.md +50 -0
  3. package/dist/agent/browser-agent.js +354 -0
  4. package/dist/agent/browser-agent.js.map +1 -0
  5. package/dist/agent/change-manager.js +433 -0
  6. package/dist/agent/change-manager.js.map +1 -0
  7. package/dist/agent/code-rag.js +293 -0
  8. package/dist/agent/code-rag.js.map +1 -0
  9. package/dist/agent/code-writer.js +13 -1
  10. package/dist/agent/code-writer.js.map +1 -1
  11. package/dist/agent/completion-engine.js +522 -0
  12. package/dist/agent/completion-engine.js.map +1 -0
  13. package/dist/agent/context-compactor.js +188 -10
  14. package/dist/agent/context-compactor.js.map +1 -1
  15. package/dist/agent/custom-agent.js +368 -0
  16. package/dist/agent/custom-agent.js.map +1 -0
  17. package/dist/agent/design-to-code.js +218 -0
  18. package/dist/agent/design-to-code.js.map +1 -0
  19. package/dist/agent/event-driven-agent.js +359 -0
  20. package/dist/agent/event-driven-agent.js.map +1 -0
  21. package/dist/agent/git-integration.js +324 -0
  22. package/dist/agent/git-integration.js.map +1 -0
  23. package/dist/agent/layered-memory.js +329 -0
  24. package/dist/agent/layered-memory.js.map +1 -0
  25. package/dist/agent/multi-agent.js +359 -0
  26. package/dist/agent/multi-agent.js.map +1 -0
  27. package/dist/agent/orchestrator.js +127 -16
  28. package/dist/agent/orchestrator.js.map +1 -1
  29. package/dist/agent/quality-gate.js +139 -0
  30. package/dist/agent/quality-gate.js.map +1 -1
  31. package/dist/agent/self-correction.js +274 -0
  32. package/dist/agent/self-correction.js.map +1 -0
  33. package/dist/agent/self-heal.js +40 -10
  34. package/dist/agent/self-heal.js.map +1 -1
  35. package/dist/agent/solo-agent.js +321 -0
  36. package/dist/agent/solo-agent.js.map +1 -0
  37. package/dist/agent/symbol-index.js +258 -28
  38. package/dist/agent/symbol-index.js.map +1 -1
  39. package/dist/agent/team-collaboration.js +252 -0
  40. package/dist/agent/team-collaboration.js.map +1 -0
  41. package/dist/agent/type-checker.js +229 -0
  42. package/dist/agent/type-checker.js.map +1 -0
  43. package/dist/cli/run.js +1 -0
  44. package/dist/cli/run.js.map +1 -1
  45. package/dist/integrations/collaboration.js +219 -0
  46. package/dist/integrations/collaboration.js.map +1 -0
  47. package/dist/integrations/figma.js +307 -0
  48. package/dist/integrations/figma.js.map +1 -0
  49. package/dist/integrations/sso.js +327 -0
  50. package/dist/integrations/sso.js.map +1 -0
  51. package/dist/knowledge/library.js +301 -0
  52. package/dist/knowledge/library.js.map +1 -0
  53. package/dist/plugins/manager.js +332 -0
  54. package/dist/plugins/manager.js.map +1 -0
  55. package/dist/runtime/event-log.js.map +1 -1
  56. package/dist/tools/browser/browser.tool.js +217 -0
  57. package/dist/tools/browser/browser.tool.js.map +1 -0
  58. package/dist/tools/index.js +2 -0
  59. package/dist/tools/index.js.map +1 -1
  60. package/dist/tools/shell/exec.js +50 -10
  61. package/dist/tools/shell/exec.js.map +1 -1
  62. package/dist/tools/shell/run-shell.tool.js +8 -2
  63. package/dist/tools/shell/run-shell.tool.js.map +1 -1
  64. package/dist/training/completion-evaluator.js +213 -0
  65. package/dist/training/completion-evaluator.js.map +1 -0
  66. package/dist/training/fim-data.js +248 -0
  67. package/dist/training/fim-data.js.map +1 -0
  68. package/dist/voice/voice-programming.js +223 -0
  69. package/dist/voice/voice-programming.js.map +1 -0
  70. package/dist/web/public/css/style.css +58 -0
  71. package/dist/web/public/index.html +210 -0
  72. package/dist/web/public/js/api.js +115 -1
  73. package/dist/web/public/js/mock-api.js +234 -0
  74. package/dist/web/public/js/monaco-editor.js +203 -0
  75. package/dist/web/public/js/ui.js +926 -4
  76. package/dist/web/public/manifest.json +59 -0
  77. package/dist/web/public/service-worker.js +173 -0
  78. package/dist/web/public/vendor/monaco/vs/abap-D-t0cyap.js +1 -0
  79. package/dist/web/public/vendor/monaco/vs/apex-CcIm7xu6.js +1 -0
  80. package/dist/web/public/vendor/monaco/vs/assets/css.worker-URu8fCFR.js +97 -0
  81. package/dist/web/public/vendor/monaco/vs/assets/editor.worker-lj3bdIIn.js +30 -0
  82. package/dist/web/public/vendor/monaco/vs/assets/editorWebWorkerMain-CA_vMoUU.js +30 -0
  83. package/dist/web/public/vendor/monaco/vs/assets/html.worker-D1SL3iM8.js +474 -0
  84. package/dist/web/public/vendor/monaco/vs/assets/json.worker-CoJx_OPf.js +62 -0
  85. package/dist/web/public/vendor/monaco/vs/assets/ts.worker-BWKtMYOk.js +67722 -0
  86. package/dist/web/public/vendor/monaco/vs/azcli-BA0tQDCg.js +1 -0
  87. package/dist/web/public/vendor/monaco/vs/basic-languages/monaco.contribution.js +1 -0
  88. package/dist/web/public/vendor/monaco/vs/bat-C397hTD6.js +1 -0
  89. package/dist/web/public/vendor/monaco/vs/bicep-DF5aW17k.js +2 -0
  90. package/dist/web/public/vendor/monaco/vs/cameligo-plsz8qhj.js +1 -0
  91. package/dist/web/public/vendor/monaco/vs/clojure-Y2auQMzK.js +1 -0
  92. package/dist/web/public/vendor/monaco/vs/coffee-Bu45yuWE.js +1 -0
  93. package/dist/web/public/vendor/monaco/vs/cpp-CkKPQIni.js +1 -0
  94. package/dist/web/public/vendor/monaco/vs/csharp-CX28MZyh.js +1 -0
  95. package/dist/web/public/vendor/monaco/vs/csp-D8uWnyxW.js +1 -0
  96. package/dist/web/public/vendor/monaco/vs/css-CaeNmE3S.js +3 -0
  97. package/dist/web/public/vendor/monaco/vs/css.worker-CyhWkhHo.js +1 -0
  98. package/dist/web/public/vendor/monaco/vs/cssMode-CV6Ay48H.js +1 -0
  99. package/dist/web/public/vendor/monaco/vs/cypher-DVThT8BS.js +1 -0
  100. package/dist/web/public/vendor/monaco/vs/dart-CmGfCvrO.js +1 -0
  101. package/dist/web/public/vendor/monaco/vs/dockerfile-CZqqYdch.js +1 -0
  102. package/dist/web/public/vendor/monaco/vs/ecl-30fUercY.js +1 -0
  103. package/dist/web/public/vendor/monaco/vs/editor/editor.main.css +1 -0
  104. package/dist/web/public/vendor/monaco/vs/editor/editor.main.js +1 -0
  105. package/dist/web/public/vendor/monaco/vs/editor/editor.worker.js +1 -0
  106. package/dist/web/public/vendor/monaco/vs/editor-KLE6jdfb.js +831 -0
  107. package/dist/web/public/vendor/monaco/vs/editor.js +1 -0
  108. package/dist/web/public/vendor/monaco/vs/editorWorkerHost-fVE1cjcC.js +35 -0
  109. package/dist/web/public/vendor/monaco/vs/elixir-xjPaIfzF.js +1 -0
  110. package/dist/web/public/vendor/monaco/vs/flow9-DqtmStfK.js +1 -0
  111. package/dist/web/public/vendor/monaco/vs/freemarker2-FWreY7v7.js +3 -0
  112. package/dist/web/public/vendor/monaco/vs/fsharp-BOMdg4U1.js +1 -0
  113. package/dist/web/public/vendor/monaco/vs/go-D_hbi-Jt.js +1 -0
  114. package/dist/web/public/vendor/monaco/vs/graphql-CKUU4kLG.js +1 -0
  115. package/dist/web/public/vendor/monaco/vs/handlebars-CZi4pHCH.js +1 -0
  116. package/dist/web/public/vendor/monaco/vs/hcl-DTaboeZW.js +1 -0
  117. package/dist/web/public/vendor/monaco/vs/html-j_7ZTNRU.js +1 -0
  118. package/dist/web/public/vendor/monaco/vs/html.worker-CA3iAimZ.js +1 -0
  119. package/dist/web/public/vendor/monaco/vs/htmlMode-27by_KM4.js +1 -0
  120. package/dist/web/public/vendor/monaco/vs/index-049xrXrp.js +1 -0
  121. package/dist/web/public/vendor/monaco/vs/index-CBVt3dzv.js +5 -0
  122. package/dist/web/public/vendor/monaco/vs/index.js +1 -0
  123. package/dist/web/public/vendor/monaco/vs/ini-CsNwO04R.js +1 -0
  124. package/dist/web/public/vendor/monaco/vs/initialize-DL0l1TGY.js +1 -0
  125. package/dist/web/public/vendor/monaco/vs/java-CI4ZMsH9.js +1 -0
  126. package/dist/web/public/vendor/monaco/vs/javascript-DTJl4Jn1.js +1 -0
  127. package/dist/web/public/vendor/monaco/vs/json.worker-BizpAl9O.js +1 -0
  128. package/dist/web/public/vendor/monaco/vs/jsonMode-DLUiMUrI.js +1 -0
  129. package/dist/web/public/vendor/monaco/vs/julia-BwzEvaQw.js +1 -0
  130. package/dist/web/public/vendor/monaco/vs/kotlin-IUYPiTV8.js +1 -0
  131. package/dist/web/public/vendor/monaco/vs/language/css/css.worker.js +66 -0
  132. package/dist/web/public/vendor/monaco/vs/language/css/monaco.contribution.js +1 -0
  133. package/dist/web/public/vendor/monaco/vs/language/html/html.worker.js +443 -0
  134. package/dist/web/public/vendor/monaco/vs/language/html/monaco.contribution.js +1 -0
  135. package/dist/web/public/vendor/monaco/vs/language/json/json.worker.js +17 -0
  136. package/dist/web/public/vendor/monaco/vs/language/json/monaco.contribution.js +1 -0
  137. package/dist/web/public/vendor/monaco/vs/language/typescript/monaco.contribution.js +1 -0
  138. package/dist/web/public/vendor/monaco/vs/language/typescript/ts.worker.js +67693 -0
  139. package/dist/web/public/vendor/monaco/vs/less-C0eDYdqa.js +2 -0
  140. package/dist/web/public/vendor/monaco/vs/lexon-iON-Kj97.js +1 -0
  141. package/dist/web/public/vendor/monaco/vs/liquid-CLJYelW6.js +1 -0
  142. package/dist/web/public/vendor/monaco/vs/loader.js +1368 -0
  143. package/dist/web/public/vendor/monaco/vs/lspLanguageFeatures-BIkJOWLw.js +2 -0
  144. package/dist/web/public/vendor/monaco/vs/lua-DtygF91M.js +1 -0
  145. package/dist/web/public/vendor/monaco/vs/m3-CsR4AuFi.js +1 -0
  146. package/dist/web/public/vendor/monaco/vs/main-BEx-Fmlo.js +15 -0
  147. package/dist/web/public/vendor/monaco/vs/main-DsK8pnKg.js +3 -0
  148. package/dist/web/public/vendor/monaco/vs/markdown-C_rD0bIw.js +1 -0
  149. package/dist/web/public/vendor/monaco/vs/mdx-CpzZIPHF.js +1 -0
  150. package/dist/web/public/vendor/monaco/vs/mips-CiYP61RB.js +1 -0
  151. package/dist/web/public/vendor/monaco/vs/monaco.contribution-9cKT3C7t.js +1 -0
  152. package/dist/web/public/vendor/monaco/vs/monaco.contribution-BE88ZNGY.js +1 -0
  153. package/dist/web/public/vendor/monaco/vs/monaco.contribution-BPhsneLd.js +1 -0
  154. package/dist/web/public/vendor/monaco/vs/monaco.contribution-BgRy6xDf.js +1 -0
  155. package/dist/web/public/vendor/monaco/vs/msdax-C38-sJlp.js +1 -0
  156. package/dist/web/public/vendor/monaco/vs/mysql-CdtbpvbG.js +1 -0
  157. package/dist/web/public/vendor/monaco/vs/nls/lang/cs.d.ts +1 -0
  158. package/dist/web/public/vendor/monaco/vs/nls/lang/cs.js +9 -0
  159. package/dist/web/public/vendor/monaco/vs/nls/lang/de.d.ts +1 -0
  160. package/dist/web/public/vendor/monaco/vs/nls/lang/de.js +9 -0
  161. package/dist/web/public/vendor/monaco/vs/nls/lang/es.d.ts +1 -0
  162. package/dist/web/public/vendor/monaco/vs/nls/lang/es.js +9 -0
  163. package/dist/web/public/vendor/monaco/vs/nls/lang/fr.d.ts +1 -0
  164. package/dist/web/public/vendor/monaco/vs/nls/lang/fr.js +9 -0
  165. package/dist/web/public/vendor/monaco/vs/nls/lang/it.d.ts +1 -0
  166. package/dist/web/public/vendor/monaco/vs/nls/lang/it.js +9 -0
  167. package/dist/web/public/vendor/monaco/vs/nls/lang/ja.d.ts +1 -0
  168. package/dist/web/public/vendor/monaco/vs/nls/lang/ja.js +9 -0
  169. package/dist/web/public/vendor/monaco/vs/nls/lang/ko.d.ts +1 -0
  170. package/dist/web/public/vendor/monaco/vs/nls/lang/ko.js +9 -0
  171. package/dist/web/public/vendor/monaco/vs/nls/lang/pl.d.ts +1 -0
  172. package/dist/web/public/vendor/monaco/vs/nls/lang/pl.js +9 -0
  173. package/dist/web/public/vendor/monaco/vs/nls/lang/pt-br.d.ts +1 -0
  174. package/dist/web/public/vendor/monaco/vs/nls/lang/pt-br.js +9 -0
  175. package/dist/web/public/vendor/monaco/vs/nls/lang/ru.d.ts +1 -0
  176. package/dist/web/public/vendor/monaco/vs/nls/lang/ru.js +9 -0
  177. package/dist/web/public/vendor/monaco/vs/nls/lang/tr.d.ts +1 -0
  178. package/dist/web/public/vendor/monaco/vs/nls/lang/tr.js +9 -0
  179. package/dist/web/public/vendor/monaco/vs/nls/lang/zh-cn.d.ts +1 -0
  180. package/dist/web/public/vendor/monaco/vs/nls/lang/zh-cn.js +9 -0
  181. package/dist/web/public/vendor/monaco/vs/nls/lang/zh-tw.d.ts +1 -0
  182. package/dist/web/public/vendor/monaco/vs/nls/lang/zh-tw.js +9 -0
  183. package/dist/web/public/vendor/monaco/vs/nls.messages-loader.js +1 -0
  184. package/dist/web/public/vendor/monaco/vs/objective-c-CntZFaHX.js +1 -0
  185. package/dist/web/public/vendor/monaco/vs/pascal-r6kuqfl_.js +1 -0
  186. package/dist/web/public/vendor/monaco/vs/pascaligo-BiXoTmXh.js +1 -0
  187. package/dist/web/public/vendor/monaco/vs/perl-DABw_TcH.js +1 -0
  188. package/dist/web/public/vendor/monaco/vs/pgsql-me_jFXeX.js +1 -0
  189. package/dist/web/public/vendor/monaco/vs/php-D_kh-9LK.js +1 -0
  190. package/dist/web/public/vendor/monaco/vs/pla-VfZjczW0.js +1 -0
  191. package/dist/web/public/vendor/monaco/vs/postiats-BBSzz8Pk.js +1 -0
  192. package/dist/web/public/vendor/monaco/vs/powerquery-Dt-g_2cc.js +1 -0
  193. package/dist/web/public/vendor/monaco/vs/powershell-B-7ap1zc.js +1 -0
  194. package/dist/web/public/vendor/monaco/vs/protobuf-BmtuEB1A.js +2 -0
  195. package/dist/web/public/vendor/monaco/vs/pug-BRpRNeEb.js +1 -0
  196. package/dist/web/public/vendor/monaco/vs/python-CqWUUgfu.js +1 -0
  197. package/dist/web/public/vendor/monaco/vs/qsharp-BzsFaUU9.js +1 -0
  198. package/dist/web/public/vendor/monaco/vs/r-f8dDdrp4.js +1 -0
  199. package/dist/web/public/vendor/monaco/vs/razor-hok5y9Km.js +1 -0
  200. package/dist/web/public/vendor/monaco/vs/redis-fvZQY4PI.js +1 -0
  201. package/dist/web/public/vendor/monaco/vs/redshift-45Et0LQi.js +1 -0
  202. package/dist/web/public/vendor/monaco/vs/restructuredtext-C7UUFKFD.js +1 -0
  203. package/dist/web/public/vendor/monaco/vs/ruby-CZO8zYTz.js +1 -0
  204. package/dist/web/public/vendor/monaco/vs/rust-Bfetafyc.js +1 -0
  205. package/dist/web/public/vendor/monaco/vs/sb-3GYllVck.js +1 -0
  206. package/dist/web/public/vendor/monaco/vs/scala-foMgrKo1.js +1 -0
  207. package/dist/web/public/vendor/monaco/vs/scheme-CHdMtr7p.js +1 -0
  208. package/dist/web/public/vendor/monaco/vs/scss-C1cmLt9V.js +3 -0
  209. package/dist/web/public/vendor/monaco/vs/shell-ClXCKCEW.js +1 -0
  210. package/dist/web/public/vendor/monaco/vs/solidity-MZ6ExpPy.js +1 -0
  211. package/dist/web/public/vendor/monaco/vs/sophia-DWkuSsPQ.js +1 -0
  212. package/dist/web/public/vendor/monaco/vs/sparql-AUGFYSyk.js +1 -0
  213. package/dist/web/public/vendor/monaco/vs/sql-32GpJSV2.js +1 -0
  214. package/dist/web/public/vendor/monaco/vs/st-CuDFIVZ_.js +1 -0
  215. package/dist/web/public/vendor/monaco/vs/swift-t-PfMj7W.js +1 -0
  216. package/dist/web/public/vendor/monaco/vs/systemverilog-Ch4vA8Yt.js +1 -0
  217. package/dist/web/public/vendor/monaco/vs/tcl-D74tq1nH.js +1 -0
  218. package/dist/web/public/vendor/monaco/vs/toggleHighContrast-qGX7E9o7.js +61 -0
  219. package/dist/web/public/vendor/monaco/vs/ts.worker-2QLmBukE.js +1 -0
  220. package/dist/web/public/vendor/monaco/vs/tsMode-B0S_kp2q.js +11 -0
  221. package/dist/web/public/vendor/monaco/vs/twig-C6taOxMV.js +1 -0
  222. package/dist/web/public/vendor/monaco/vs/typescript-_2t_511t.js +1 -0
  223. package/dist/web/public/vendor/monaco/vs/typespec-8OfoLt6R.js +1 -0
  224. package/dist/web/public/vendor/monaco/vs/vb-Dyb2648j.js +1 -0
  225. package/dist/web/public/vendor/monaco/vs/wgsl-BhLXMOR0.js +298 -0
  226. package/dist/web/public/vendor/monaco/vs/workers-BBttULjf.js +1 -0
  227. package/dist/web/public/vendor/monaco/vs/xml-cZjNDqDT.js +1 -0
  228. package/dist/web/public/vendor/monaco/vs/yaml-A1fOIdH6.js +1 -0
  229. package/dist/web/server.js +1019 -0
  230. package/dist/web/server.js.map +1 -1
  231. package/dist/web/task-queue.js +22 -2
  232. package/dist/web/task-queue.js.map +1 -1
  233. package/docs/DEPLOYMENT.md +382 -0
  234. package/docs/MODEL_FINETUNING.md +405 -0
  235. package/electron/main.js +202 -4
  236. package/package.json +6 -1
@@ -355,7 +355,16 @@
355
355
  }
356
356
 
357
357
  async function api(path, method = 'GET', body) {
358
- const res = await fetch(path, { method, headers: authHeaders(), body: body ? JSON.stringify(body) : undefined });
358
+ let res;
359
+ try {
360
+ res = await fetch(path, { method, headers: authHeaders(), body: body ? JSON.stringify(body) : undefined });
361
+ } catch (e) {
362
+ // 网络层失败(Failed to fetch / 连接拒绝 / 超时中断):标记后端可能已断开
363
+ if (typeof BackendConn !== 'undefined') BackendConn.markFailure();
364
+ throw e;
365
+ }
366
+ // 请求到达后端并返回响应:若之前处于断连状态,立即恢复
367
+ if (typeof BackendConn !== 'undefined' && !BackendConn.online) BackendConn.setOnline(true);
359
368
  if (!res.ok) {
360
369
  const d = await res.json().catch(() => ({}));
361
370
  // 401:令牌确实被服务端拒绝,清除并回到登录页。
@@ -380,3 +389,108 @@
380
389
  function authHeaders() {
381
390
  return state.token ? { 'Authorization': 'Bearer ' + state.token, 'Content-Type': 'application/json' } : { 'Content-Type': 'application/json' };
382
391
  }
392
+
393
+ /* ========== 后端连接状态监测(心跳 + 断连横幅 + 自动重连) ==========
394
+ * 解决问题:后端进程崩溃后,前端无感知,用户操作均报 "Failed to fetch" 且不知原因。
395
+ * 机制:
396
+ * 1. 每 5s 轮询 /api/health,连续失败 2 次判定后端断开,顶部显示红色横幅。
397
+ * 2. 断连后自动加快到每 2s 重试,恢复后自动切回 5s 并 toast 提示。
398
+ * 3. 任意 api() 调用网络失败也会累计失败计数(双保险,不必等下一轮心跳)。
399
+ * 4. 页面从后台切回 / 网络恢复事件时立即检测一次。
400
+ */
401
+ const BackendConn = {
402
+ online: true,
403
+ heartbeatTimer: null,
404
+ reconnectTimer: null,
405
+ intervalMs: 5000, // 正常心跳间隔
406
+ reconnectMs: 2000, // 断连后重连检测间隔
407
+ consecutiveFailures: 0,
408
+ threshold: 2, // 连续失败 N 次才判定断开(避免瞬时抖动误报)
409
+ healthTimeoutMs: 3000, // 单次健康检查超时
410
+
411
+ init() {
412
+ this.startHeartbeat();
413
+ // 重连按钮
414
+ const btn = document.getElementById('backendReconnectBtn');
415
+ if (btn) btn.addEventListener('click', () => this.reconnect());
416
+ // 页面从后台切回前台时立即检测
417
+ document.addEventListener('visibilitychange', () => {
418
+ if (!document.hidden) this.check();
419
+ });
420
+ // 浏览器网络恢复事件
421
+ window.addEventListener('online', () => this.check());
422
+ },
423
+
424
+ startHeartbeat() {
425
+ if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
426
+ this.heartbeatTimer = setInterval(() => this.check(), this.intervalMs);
427
+ },
428
+
429
+ async check() {
430
+ try {
431
+ const ctrl = new AbortController();
432
+ const timeout = setTimeout(() => ctrl.abort(), this.healthTimeoutMs);
433
+ const res = await fetch('/api/health', {
434
+ method: 'GET',
435
+ signal: ctrl.signal,
436
+ cache: 'no-store',
437
+ headers: { 'Cache-Control': 'no-cache' },
438
+ });
439
+ clearTimeout(timeout);
440
+ if (res.ok) {
441
+ this.consecutiveFailures = 0;
442
+ this.setOnline(true);
443
+ } else {
444
+ this.markFailure();
445
+ }
446
+ } catch (e) {
447
+ this.markFailure();
448
+ }
449
+ },
450
+
451
+ /** 累计失败次数,达到阈值则判定断开 */
452
+ markFailure() {
453
+ this.consecutiveFailures++;
454
+ if (this.consecutiveFailures >= this.threshold) {
455
+ this.setOnline(false);
456
+ }
457
+ },
458
+
459
+ setOnline(online) {
460
+ if (this.online === online) return;
461
+ this.online = online;
462
+ const banner = document.getElementById('backendOfflineBanner');
463
+ const text = document.getElementById('backendOfflineText');
464
+ if (banner) {
465
+ banner.style.display = online ? 'none' : 'flex';
466
+ }
467
+ if (online) {
468
+ // 恢复:切回正常心跳间隔
469
+ if (this.reconnectTimer) { clearInterval(this.reconnectTimer); this.reconnectTimer = null; }
470
+ this.startHeartbeat();
471
+ try { toast('后端服务已恢复连接'); } catch (e) {}
472
+ } else {
473
+ // 断开:停止正常心跳,加快重连检测
474
+ if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; }
475
+ if (!this.reconnectTimer) {
476
+ this.reconnectTimer = setInterval(() => this.check(), this.reconnectMs);
477
+ }
478
+ if (text) text.textContent = '后端服务已断开,正在自动重连…';
479
+ }
480
+ },
481
+
482
+ /** 手动触发重连:重置失败计数并立即检测 */
483
+ reconnect() {
484
+ const text = document.getElementById('backendOfflineText');
485
+ if (text) text.textContent = '正在重连后端服务…';
486
+ this.consecutiveFailures = 0;
487
+ this.check();
488
+ },
489
+ };
490
+
491
+ // 页面加载完成后启动心跳监测
492
+ if (document.readyState === 'loading') {
493
+ document.addEventListener('DOMContentLoaded', () => BackendConn.init());
494
+ } else {
495
+ BackendConn.init();
496
+ }
@@ -0,0 +1,234 @@
1
+ /**
2
+ * 飞虹 Code - 安卓版 Mock API 层 (mock-api.js)
3
+ *
4
+ * 在 Android WebView 中无本地 Node.js 后端时提供离线演示数据。
5
+ * 通过拦截 window.fetch 对 /api/ 请求返回本地模拟数据,保证 UI 完整可用。
6
+ *
7
+ * 模式:
8
+ * - 默认:本地 Mock 模式(离线演示)
9
+ * - 可在设置中填写服务器地址切换为远程模式(真实调用后端)
10
+ *
11
+ * 此文件在 api.js 之前加载,必须在最顶部定义,保证 window.fetch 被优先拦截。
12
+ */
13
+ (function () {
14
+ if (window.__feihongMockInstalled) return;
15
+ window.__feihongMockInstalled = true;
16
+
17
+ // ========== 远程服务器配置 ==========
18
+ var REMOTE_KEY = 'fhcode.remote.base';
19
+ function getRemoteBase() {
20
+ try { return localStorage.getItem(REMOTE_KEY) || ''; } catch (e) { return ''; }
21
+ }
22
+
23
+ // ========== Mock 数据 ==========
24
+ var MODELS = [
25
+ { id: 'deepseek-chat', name: 'DeepSeek Chat', provider: 'deepseek', reasoning: false, enabled: true },
26
+ { id: 'deepseek-reasoner', name: 'DeepSeek Reasoner', provider: 'deepseek', reasoning: true, enabled: true },
27
+ { id: 'qwen-max', name: '通义千问 Max', provider: 'qwen', reasoning: false, enabled: true },
28
+ { id: 'gpt-4o', name: 'GPT-4o', provider: 'openai', reasoning: false, enabled: true }
29
+ ];
30
+
31
+ var SKILLS = [
32
+ { id: 'code-review', name: '代码审查', desc: '自动审查代码质量与安全', enabled: true },
33
+ { id: 'test-gen', name: '测试生成', desc: '自动生成单元测试用例', enabled: true },
34
+ { id: 'doc-gen', name: '文档生成', desc: '自动生成 API 文档', enabled: true },
35
+ { id: 'refactor', name: '重构专家', desc: '代码重构与优化', enabled: true },
36
+ { id: 'bug-hunt', name: 'Bug 猎手', desc: '自动定位与修复 Bug', enabled: true }
37
+ ];
38
+
39
+ var OFFICE_CAPS = [
40
+ { title: '生成周报', icon: '📊', prompt: '帮我生成一份本周工作周报', desc: '自动生成结构化周报' },
41
+ { title: '写邮件', icon: '✉️', prompt: '帮我写一封商务邮件', desc: '专业邮件撰写' },
42
+ { title: 'PPT 大纲', icon: '📑', prompt: '帮我生成一份 PPT 大纲', desc: '结构化演示大纲' },
43
+ { title: '会议纪要', icon: '📝', prompt: '帮我整理会议纪要', desc: '智能会议纪要' },
44
+ { title: '数据分析', icon: '📈', prompt: '帮我分析这份数据', desc: '数据洞察分析' },
45
+ { title: '翻译文档', icon: '🌐', prompt: '帮我翻译这段内容', desc: '中英互译' }
46
+ ];
47
+
48
+ var ROLES = [
49
+ { id: 'architect', name: '架构师', icon: '🏛️', desc: '系统设计、技术选型' },
50
+ { id: 'developer', name: '开发者', icon: '💻', desc: '代码实现' },
51
+ { id: 'tester', name: '测试工程师', icon: '🧪', desc: '测试用例设计' },
52
+ { id: 'reviewer', name: '评审员', icon: '🔍', desc: '代码审查' }
53
+ ];
54
+
55
+ var CUSTOM_AGENTS = [
56
+ { id: 'builtin-代码审查员', name: '代码审查员', category: 'review', desc: '专业代码审查,安全漏洞/性能/最佳实践', useCount: 12 },
57
+ { id: 'builtin-测试工程师', name: '测试工程师', category: 'testing', desc: '自动生成测试用例', useCount: 8 },
58
+ { id: 'builtin-文档生成器', name: '文档生成器', category: 'documentation', desc: '自动生成代码文档', useCount: 6 },
59
+ { id: 'builtin-重构专家', name: '重构专家', category: 'refactoring', desc: '代码重构优化', useCount: 9 },
60
+ { id: 'builtin-Bug猎手', name: 'Bug 猎手', category: 'debugging', desc: 'Bug 分析和修复', useCount: 15 }
61
+ ];
62
+
63
+ // ========== Mock 路由处理 ==========
64
+ function handleMock(path, method, body) {
65
+ var p = path.split('?')[0];
66
+ var lower = p.toLowerCase();
67
+
68
+ // 健康检查
69
+ if (lower === '/api/health') {
70
+ return ok({ product: '飞虹 Code', version: '7.0.0', enterprise: true, signature: '安卓版 · 离线演示模式', mode: 'mock', time: new Date().toISOString() });
71
+ }
72
+
73
+ // 模型
74
+ if (lower === '/api/models' && method === 'GET') return ok({ models: MODELS });
75
+ if (lower === '/api/models' && method === 'POST') {
76
+ var m = body || {};
77
+ MODELS.push({ id: 'custom-' + Date.now(), name: m.name || '自定义模型', provider: 'custom', reasoning: !!m.reasoning, enabled: true });
78
+ return ok({ model: MODELS[MODELS.length - 1] });
79
+ }
80
+
81
+ // 技能
82
+ if (lower === '/api/skills/installed') return ok({ skills: SKILLS });
83
+ if (lower === '/api/skills' ) return ok({ skills: SKILLS });
84
+
85
+ // 办公能力
86
+ if (lower === '/api/office/capabilities') return ok({ capabilities: OFFICE_CAPS });
87
+
88
+ // 多智能体角色
89
+ if (lower === '/api/multi-agent/roles') return ok({ roles: ROLES });
90
+ if (lower === '/api/multi-agent/run') {
91
+ var goal = (body && body.goal) || '完成任务';
92
+ return ok({
93
+ goal: goal,
94
+ rounds: [
95
+ { role: '架构师', output: '设计方案:采用模块化架构,定义清晰接口边界。\n- 技术选型:TypeScript + 分层设计\n- 接口定义:RESTful API\n- 错误处理:统一异常捕获', score: 85 },
96
+ { role: '开发者', output: '代码实现:完成核心模块开发。\n- 实现业务逻辑\n- 编写单元测试\n- 补充类型定义', score: 88 }
97
+ ],
98
+ finalVerdict: '通过',
99
+ qualityScore: 86,
100
+ issues: []
101
+ });
102
+ }
103
+
104
+ // 自定义 Agent
105
+ if (lower === '/api/custom-agents') return ok({ agents: CUSTOM_AGENTS });
106
+ if (lower.indexOf('/api/custom-agents/match') === 0) {
107
+ var inp = (body && body.input) || '';
108
+ var matched = CUSTOM_AGENTS.filter(function (a) {
109
+ return !inp || a.name.indexOf(inp) !== -1 || a.desc.indexOf(inp) !== -1;
110
+ }).slice(0, 3);
111
+ return ok({ agents: matched });
112
+ }
113
+ if (lower.indexOf('/api/custom-agents/') === 0 && method === 'GET') {
114
+ var id = p.split('/').pop();
115
+ var agent = CUSTOM_AGENTS.find(function (a) { return a.id === id; });
116
+ return ok({ agent: agent || CUSTOM_AGENTS[0] });
117
+ }
118
+
119
+ // 事件驱动
120
+ if (lower === '/api/event-driven/config') return ok({ config: { enabled: true, tasks: [] } });
121
+ if (lower === '/api/event-driven/events') return ok({ events: [] });
122
+ if (lower === '/api/event-driven/cron') return ok({ tasks: [] });
123
+
124
+ // 记忆
125
+ if (lower === '/api/memory/history') return ok({ history: [] });
126
+ if (lower === '/api/memory/long') return ok({ content: '这是长期记忆区域。在安卓离线演示模式下,长期记忆功能暂未启用。\n\n连接远程服务器后,将自动同步您的记忆数据。' });
127
+ if (lower === '/api/memory/short') return ok({ content: '当天暂无记录' });
128
+ if (lower === '/api/memory/stats') return ok({ shortTermFiles: 0, longTermNotes: 0, lastSummarize: '暂无' });
129
+
130
+ // 补全
131
+ if (lower.indexOf('/api/completion') === 0) {
132
+ return ok({
133
+ completions: [
134
+ { text: 'async function handleRequest(req, res) {\n try {\n const data = await fetchData(req.params.id);\n res.json({ ok: true, data });\n } catch (e) {\n res.status(500).json({ ok: false, error: e.message });\n }\n}', score: 0.95 },
135
+ { text: 'function validate(input) {\n if (!input) throw new Error("input is required");\n return input.trim();\n}', score: 0.88 }
136
+ ]
137
+ });
138
+ }
139
+
140
+ // 会话 / 统计
141
+ if (lower === '/api/sessions') return ok({ sessions: [] });
142
+ if (lower.indexOf('/api/team') === 0) {
143
+ if (method === 'GET') return ok({ members: [], tasks: [] });
144
+ return ok({ ok: true });
145
+ }
146
+ if (lower.indexOf('/api/git') === 0) {
147
+ if (method === 'GET') return ok({ branches: [], status: { clean: true }, commits: [] });
148
+ return ok({ ok: true });
149
+ }
150
+ if (lower.indexOf('/api/design-to-code') === 0) {
151
+ return ok({ ok: true, html: '<!-- 安卓演示模式:设计稿转代码需连接远程服务器 -->' });
152
+ }
153
+ if (lower.indexOf('/api/solo') === 0) {
154
+ return ok({ ok: true, status: 'idle' });
155
+ }
156
+ if (lower.indexOf('/api/mcp') === 0) {
157
+ return ok({ servers: [] });
158
+ }
159
+
160
+ // 登录/认证
161
+ if (lower === '/api/auth/login' || lower === '/api/login') {
162
+ return ok({ token: 'mock-token-' + Date.now(), user: { name: '演示用户', role: 'admin' } });
163
+ }
164
+
165
+ // 对话 / Agent 执行
166
+ if (lower.indexOf('/api/chat') === 0 || lower.indexOf('/api/agent') === 0 || lower.indexOf('/api/task') === 0) {
167
+ var prompt = (body && (body.prompt || body.message || body.goal || body.input)) || '任务';
168
+ return ok({
169
+ id: 'mock-' + Date.now(),
170
+ answer: '【安卓离线演示】\n\n已收到任务:' + prompt + '\n\n当前处于离线演示模式,AI 生成能力需要连接远程飞虹 Code 服务器。\n\n在「设置」中填写服务器地址后,即可使用完整 AI 能力(多模型路由、Agent 执行、代码补全等)。',
171
+ done: true
172
+ });
173
+ }
174
+
175
+ // 配置
176
+ if (lower === '/api/config' || lower === '/api/settings') {
177
+ return ok({ config: { mode: 'mock', remoteBase: getRemoteBase(), version: '7.0.0' } });
178
+ }
179
+
180
+ // 兜底:GET 返回空结构,其他返回 ok
181
+ if (method === 'GET') return ok({ ok: true, data: [] });
182
+ return ok({ ok: true, data: [] });
183
+ }
184
+
185
+ function ok(data) {
186
+ return {
187
+ ok: true,
188
+ status: 200,
189
+ json: function () { return Promise.resolve(data); },
190
+ text: function () { return Promise.resolve(JSON.stringify(data)); }
191
+ };
192
+ }
193
+
194
+ // ========== 拦截 fetch ==========
195
+ var originalFetch = window.fetch;
196
+ window.fetch = function (input, init) {
197
+ var url = typeof input === 'string' ? input : input.url;
198
+ var method = ((init && init.method) || (input && input.method) || 'GET').toUpperCase();
199
+
200
+ // 远程模式:配置了服务器地址且请求的是 /api/ 相对路径时,转发到远程
201
+ var remoteBase = getRemoteBase();
202
+ if (remoteBase && url.indexOf('/api/') === 0) {
203
+ var remoteUrl = remoteBase.replace(/\/$/, '') + url;
204
+ return originalFetch.call(window, remoteUrl, init).catch(function (e) {
205
+ // 远程失败时降级到 Mock
206
+ return Promise.resolve(handleMock(url, method, parseBody(init)));
207
+ });
208
+ }
209
+
210
+ // Mock 模式:拦截 /api/ 请求
211
+ if (url.indexOf('/api/') === 0) {
212
+ return Promise.resolve(handleMock(url, method, parseBody(init)));
213
+ }
214
+
215
+ // 其他请求(静态资源等)正常处理
216
+ return originalFetch.apply(window, arguments);
217
+ };
218
+
219
+ function parseBody(init) {
220
+ if (!init || !init.body) return null;
221
+ try { return JSON.parse(init.body); } catch (e) { return null; }
222
+ }
223
+
224
+ // ========== 暴露远程模式切换接口 ==========
225
+ window.FeiHongApp = window.FeiHongApp || {};
226
+ window.FeiHongApp.setRemote = function (base) {
227
+ try { localStorage.setItem(REMOTE_KEY, base || ''); } catch (e) {}
228
+ return base;
229
+ };
230
+ window.FeiHongApp.getRemote = function () { return getRemoteBase(); };
231
+ window.FeiHongApp.isMock = function () { return !getRemoteBase(); };
232
+
233
+ console.log('[MockAPI] 飞虹 Code 安卓离线 Mock 层已加载 (mode=' + (getRemoteBase() ? 'remote' : 'mock') + ')');
234
+ })();
@@ -0,0 +1,203 @@
1
+ /**
2
+ * 飞虹 Code - Monaco Editor 封装模块
3
+ * VSCode 同款编辑器内核,提供语法高亮、智能补全、内联 ghost text 等能力
4
+ *
5
+ * 依赖:index.html 中已加载 vendor/monaco/vs/loader.js(AMD loader)
6
+ */
7
+ (function (global) {
8
+ 'use strict';
9
+
10
+ let monacoInstance = null;
11
+ let initPromise = null;
12
+ const editors = new Map(); // containerId -> editor instance
13
+ let inlineCompletionProvider = null;
14
+ let completionItemProvider = null;
15
+
16
+ /** 根据文件扩展名推断 Monaco language id */
17
+ function detectLanguage(filePath) {
18
+ const ext = filePath.split('.').pop().toLowerCase();
19
+ const map = {
20
+ ts: 'typescript', tsx: 'typescript', js: 'javascript', jsx: 'javascript',
21
+ json: 'json', md: 'markdown', html: 'html', css: 'css', scss: 'scss',
22
+ less: 'less', py: 'python', java: 'java', c: 'c', cpp: 'cpp', h: 'cpp',
23
+ cs: 'csharp', go: 'go', rs: 'rust', php: 'php', rb: 'ruby', swift: 'swift',
24
+ kt: 'kotlin', scala: 'scala', sh: 'shell', bash: 'shell', zsh: 'shell',
25
+ sql: 'sql', yaml: 'yaml', yml: 'yaml', xml: 'xml', vue: 'html', svelte: 'html',
26
+ proto: 'protobuf', dockerfile: 'dockerfile', toml: 'ini', ini: 'ini',
27
+ };
28
+ if (filePath.toLowerCase().endsWith('dockerfile')) return 'dockerfile';
29
+ return map[ext] || 'plaintext';
30
+ }
31
+
32
+ /** 初始化 Monaco(只执行一次,返回 Promise) */
33
+ function initMonaco() {
34
+ if (monacoInstance) return Promise.resolve(monacoInstance);
35
+ if (initPromise) return initPromise;
36
+
37
+ initPromise = new Promise((resolve, reject) => {
38
+ if (typeof global.require === 'undefined') {
39
+ reject(new Error('Monaco loader.js 未加载,请检查 vendor/monaco/vs/loader.js'));
40
+ return;
41
+ }
42
+ global.require.config({ paths: { vs: 'vendor/monaco/vs' } });
43
+ global.require(['vs/editor/editor.main'], function () {
44
+ monacoInstance = global.monaco;
45
+ // 配置默认主题(跟随系统深色/浅色)
46
+ const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
47
+ monacoInstance.editor.setTheme(isDark ? 'vs-dark' : 'vs');
48
+ resolve(monacoInstance);
49
+ }, reject);
50
+ });
51
+ return initPromise;
52
+ }
53
+
54
+ /**
55
+ * 在指定容器中创建 Monaco 编辑器
56
+ * @param {HTMLElement|string} container - 容器元素或 ID
57
+ * @param {string} value - 初始内容
58
+ * @param {string} filePath - 文件路径(用于推断语言)
59
+ * @param {object} options - 额外选项
60
+ * @returns {Promise<monaco.editor.IStandaloneCodeEditor>}
61
+ */
62
+ async function createEditor(container, value, filePath, options = {}) {
63
+ const monaco = await initMonaco();
64
+ const el = typeof container === 'string' ? document.getElementById(container) : container;
65
+ if (!el) throw new Error('编辑器容器不存在: ' + container);
66
+
67
+ // 如果该容器已有编辑器,先销毁
68
+ const existing = editors.get(el.id || container);
69
+ if (existing) {
70
+ existing.dispose();
71
+ editors.delete(el.id || container);
72
+ }
73
+
74
+ const language = detectLanguage(filePath);
75
+ const editor = monaco.editor.create(el, {
76
+ value: value || '',
77
+ language: language,
78
+ theme: undefined, // 使用全局主题
79
+ automaticLayout: true,
80
+ fontSize: 13,
81
+ lineNumbers: 'on',
82
+ minimap: { enabled: true },
83
+ scrollBeyondLastLine: false,
84
+ wordWrap: 'on',
85
+ bracketPairColorization: { enabled: true },
86
+ guides: { bracketPairs: true, indentation: true },
87
+ tabSize: 2,
88
+ renderWhitespace: 'selection',
89
+ smoothScrolling: true,
90
+ cursorBlinking: 'smooth',
91
+ // P0-1: 启用内联补全(ghost text,Tab 接受)
92
+ inlineSuggest: { enabled: true, showToolbar: 'always' },
93
+ // 启用快速建议(输入时自动触发补全弹窗)
94
+ quickSuggestions: { other: true, comments: false, strings: true },
95
+ quickSuggestionsDelay: 200,
96
+ ...options,
97
+ });
98
+
99
+ const key = el.id || container;
100
+ editors.set(key, editor);
101
+ return editor;
102
+ }
103
+
104
+ /** 获取编辑器实例 */
105
+ function getEditor(container) {
106
+ const key = typeof container === 'string' ? container : container.id;
107
+ return editors.get(key);
108
+ }
109
+
110
+ /** 销毁编辑器 */
111
+ function disposeEditor(container) {
112
+ const key = typeof container === 'string' ? container : container.id;
113
+ const editor = editors.get(key);
114
+ if (editor) {
115
+ editor.dispose();
116
+ editors.delete(key);
117
+ }
118
+ }
119
+
120
+ /**
121
+ * 注册内联补全 Provider(ghost text,Tab 接受)
122
+ * @param {function} fetchFn - 异步补全函数,参数 {filePath, content, offset},返回 {text: string}
123
+ */
124
+ function registerInlineCompletions(fetchFn) {
125
+ initMonaco().then((monaco) => {
126
+ if (inlineCompletionProvider) {
127
+ inlineCompletionProvider.dispose();
128
+ }
129
+ inlineCompletionProvider = monaco.languages.registerInlineCompletionsProvider('*', {
130
+ provideInlineCompletions: async function (model, position, context, token) {
131
+ try {
132
+ const filePath = model.uri.path || 'untitled';
133
+ const content = model.getValue();
134
+ const offset = model.getOffsetAt(position);
135
+ const result = await fetchFn({ filePath, content, offset, token });
136
+ if (!result || !result.text) return { items: [] };
137
+ const item = {
138
+ range: new monaco.Range(position.lineNumber, position.column, position.lineNumber, position.column),
139
+ insertText: result.text,
140
+ filterText: result.text.slice(0, 20),
141
+ };
142
+ return { items: [item] };
143
+ } catch (e) {
144
+ return { items: [] };
145
+ }
146
+ },
147
+ freeInlineCompletions: function () {},
148
+ });
149
+ });
150
+ }
151
+
152
+ /**
153
+ * 注册补全项 Provider(Ctrl+Space 触发弹窗,↑↓选择,Enter接受)
154
+ * @param {function} fetchFn - 异步补全函数,参数 {filePath, content, offset, prefix},返回数组 [{label, kind, insertText, detail}]
155
+ */
156
+ function registerCompletionItems(fetchFn) {
157
+ initMonaco().then((monaco) => {
158
+ if (completionItemProvider) {
159
+ completionItemProvider.dispose();
160
+ }
161
+ completionItemProvider = monaco.languages.registerCompletionItemProvider('*', {
162
+ triggerCharacters: ['.', '(', ',', ' '],
163
+ provideCompletionItems: async function (model, position, context, token) {
164
+ try {
165
+ const filePath = model.uri.path || 'untitled';
166
+ const content = model.getValue();
167
+ const offset = model.getOffsetAt(position);
168
+ const word = model.getWordUntilPosition(position);
169
+ const prefix = word.word || '';
170
+ const suggestions = await fetchFn({ filePath, content, offset, prefix, token });
171
+ if (!Array.isArray(suggestions) || !suggestions.length) return { suggestions: [] };
172
+ const range = new monaco.Range(
173
+ position.lineNumber, word.startColumn,
174
+ position.lineNumber, position.column
175
+ );
176
+ return {
177
+ suggestions: suggestions.map((s) => ({
178
+ label: s.label || s.insertText || '',
179
+ kind: monaco.languages.CompletionItemKind[s.kind] || monaco.languages.CompletionItemKind.Text,
180
+ insertText: s.insertText || '',
181
+ detail: s.detail || '',
182
+ range: range,
183
+ })),
184
+ };
185
+ } catch (e) {
186
+ return { suggestions: [] };
187
+ }
188
+ },
189
+ });
190
+ });
191
+ }
192
+
193
+ /** 暴露全局 API */
194
+ global.FHMonaco = {
195
+ init: initMonaco,
196
+ createEditor: createEditor,
197
+ getEditor: getEditor,
198
+ disposeEditor: disposeEditor,
199
+ detectLanguage: detectLanguage,
200
+ registerInlineCompletions: registerInlineCompletions,
201
+ registerCompletionItems: registerCompletionItems,
202
+ };
203
+ })(window);