autosnippet 3.3.5 → 3.3.7

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 (279) hide show
  1. package/dashboard/dist/assets/icons-FHns2ypa.js +1 -0
  2. package/dashboard/dist/assets/index-BRJv5Y3r.js +135 -0
  3. package/dashboard/dist/assets/index-DzoB7kxK.css +1 -0
  4. package/dashboard/dist/index.html +3 -3
  5. package/dist/bin/api-server.js +1 -0
  6. package/dist/bin/cli.d.ts +1 -0
  7. package/dist/bin/cli.js +137 -9
  8. package/dist/lib/agent/AgentFactory.d.ts +0 -17
  9. package/dist/lib/agent/AgentFactory.js +1 -25
  10. package/dist/lib/agent/AgentRuntime.d.ts +2 -2
  11. package/dist/lib/agent/AgentRuntime.js +26 -18
  12. package/dist/lib/agent/capabilities.d.ts +11 -0
  13. package/dist/lib/agent/capabilities.js +29 -5
  14. package/dist/lib/agent/context/ExplorationTracker.js +10 -1
  15. package/dist/lib/agent/context/exploration/ExplorationStrategies.d.ts +2 -0
  16. package/dist/lib/agent/context/exploration/ExplorationStrategies.js +2 -2
  17. package/dist/lib/agent/domain/ChatAgentTasks.js +4 -0
  18. package/dist/lib/agent/domain/insight-analyst.d.ts +47 -3
  19. package/dist/lib/agent/domain/insight-analyst.js +111 -11
  20. package/dist/lib/agent/domain/insight-evolver.d.ts +69 -0
  21. package/dist/lib/agent/domain/insight-evolver.js +230 -0
  22. package/dist/lib/agent/domain/insight-gate.d.ts +42 -0
  23. package/dist/lib/agent/domain/insight-gate.js +41 -0
  24. package/dist/lib/agent/domain/insight-producer.d.ts +27 -2
  25. package/dist/lib/agent/domain/insight-producer.js +60 -5
  26. package/dist/lib/agent/domain/scan-prompts.js +10 -7
  27. package/dist/lib/agent/forced-summary.js +7 -2
  28. package/dist/lib/agent/memory/ActiveContext.d.ts +2 -28
  29. package/dist/lib/agent/memory/MemoryCoordinator.d.ts +2 -2
  30. package/dist/lib/agent/memory/SessionStore.d.ts +6 -12
  31. package/dist/lib/agent/memory/SessionStore.js +9 -15
  32. package/dist/lib/agent/memory/memory-flush-contract.d.ts +49 -0
  33. package/dist/lib/agent/memory/memory-flush-contract.js +16 -0
  34. package/dist/lib/agent/memory/session-store-schema.d.ts +20 -0
  35. package/dist/lib/agent/memory/session-store-schema.js +41 -0
  36. package/dist/lib/agent/presets.d.ts +89 -1
  37. package/dist/lib/agent/presets.js +53 -5
  38. package/dist/lib/agent/tools/_shared.d.ts +7 -15
  39. package/dist/lib/agent/tools/_shared.js +20 -21
  40. package/dist/lib/agent/tools/composite.d.ts +25 -22
  41. package/dist/lib/agent/tools/composite.js +108 -109
  42. package/dist/lib/agent/tools/evolution-tools.d.ts +145 -0
  43. package/dist/lib/agent/tools/evolution-tools.js +161 -0
  44. package/dist/lib/agent/tools/index.d.ts +163 -92
  45. package/dist/lib/agent/tools/index.js +9 -1
  46. package/dist/lib/agent/tools/lifecycle.d.ts +7 -1
  47. package/dist/lib/agent/tools/lifecycle.js +59 -75
  48. package/dist/lib/cli/AiScanService.js +5 -5
  49. package/dist/lib/cli/KnowledgeSyncService.js +1 -1
  50. package/dist/lib/core/AstAnalyzer.d.ts +1 -0
  51. package/dist/lib/core/discovery/ConfigWatcher.d.ts +64 -0
  52. package/dist/lib/core/discovery/ConfigWatcher.js +336 -0
  53. package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +30 -0
  54. package/dist/lib/core/discovery/CustomConfigDiscoverer.js +1305 -0
  55. package/dist/lib/core/discovery/DiscovererPreference.d.ts +44 -0
  56. package/dist/lib/core/discovery/DiscovererPreference.js +141 -0
  57. package/dist/lib/core/discovery/DiscovererRegistry.d.ts +10 -1
  58. package/dist/lib/core/discovery/DiscovererRegistry.js +42 -2
  59. package/dist/lib/core/discovery/ProjectDiscoverer.d.ts +19 -0
  60. package/dist/lib/core/discovery/index.d.ts +2 -0
  61. package/dist/lib/core/discovery/index.js +4 -0
  62. package/dist/lib/core/discovery/parsers/CMakeParser.d.ts +32 -0
  63. package/dist/lib/core/discovery/parsers/CMakeParser.js +148 -0
  64. package/dist/lib/core/discovery/parsers/GradleDslParser.d.ts +43 -0
  65. package/dist/lib/core/discovery/parsers/GradleDslParser.js +171 -0
  66. package/dist/lib/core/discovery/parsers/JsonConfigParser.d.ts +45 -0
  67. package/dist/lib/core/discovery/parsers/JsonConfigParser.js +122 -0
  68. package/dist/lib/core/discovery/parsers/RubyDslParser.d.ts +49 -0
  69. package/dist/lib/core/discovery/parsers/RubyDslParser.js +282 -0
  70. package/dist/lib/core/discovery/parsers/StarlarkParser.d.ts +33 -0
  71. package/dist/lib/core/discovery/parsers/StarlarkParser.js +229 -0
  72. package/dist/lib/core/discovery/parsers/YamlConfigParser.d.ts +37 -0
  73. package/dist/lib/core/discovery/parsers/YamlConfigParser.js +212 -0
  74. package/dist/lib/{service/bootstrap/DimensionCopyRegistry.d.ts → domain/dimension/DimensionCopy.d.ts} +2 -2
  75. package/dist/lib/{service/bootstrap/DimensionCopyRegistry.js → domain/dimension/DimensionCopy.js} +22 -72
  76. package/dist/lib/domain/dimension/DimensionRegistry.d.ts +54 -0
  77. package/dist/lib/domain/dimension/DimensionRegistry.js +620 -0
  78. package/dist/lib/domain/dimension/DimensionSop.d.ts +55 -0
  79. package/dist/lib/domain/dimension/DimensionSop.js +1604 -0
  80. package/dist/lib/domain/dimension/UnifiedDimension.d.ts +61 -0
  81. package/dist/lib/domain/dimension/UnifiedDimension.js +53 -0
  82. package/dist/lib/domain/dimension/index.d.ts +10 -0
  83. package/dist/lib/domain/dimension/index.js +9 -0
  84. package/dist/lib/domain/knowledge/FieldSpec.d.ts +1 -1
  85. package/dist/lib/domain/knowledge/FieldSpec.js +29 -16
  86. package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +40 -112
  87. package/dist/lib/domain/knowledge/KnowledgeEntry.js +44 -9
  88. package/dist/lib/domain/knowledge/KnowledgeRepository.d.ts +1 -0
  89. package/dist/lib/domain/knowledge/KnowledgeRepository.js +3 -0
  90. package/dist/lib/domain/knowledge/Lifecycle.js +1 -1
  91. package/dist/lib/domain/knowledge/StyleGuide.d.ts +1 -1
  92. package/dist/lib/domain/knowledge/StyleGuide.js +1 -1
  93. package/dist/lib/domain/knowledge/UnifiedValidator.js +15 -0
  94. package/dist/lib/external/ai/AiProvider.d.ts +12 -0
  95. package/dist/lib/external/ai/AiProvider.js +24 -0
  96. package/dist/lib/external/ai/AiProviderManager.d.ts +101 -0
  97. package/dist/lib/external/ai/AiProviderManager.js +193 -0
  98. package/dist/lib/external/ai/providers/ClaudeProvider.js +11 -0
  99. package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +18 -0
  100. package/dist/lib/external/ai/providers/MockProvider.d.ts +21 -3
  101. package/dist/lib/external/ai/providers/MockProvider.js +290 -14
  102. package/dist/lib/external/ai/providers/OpenAiProvider.js +16 -0
  103. package/dist/lib/external/lark/LarkTransport.d.ts +5 -1
  104. package/dist/lib/external/lark/LarkTransport.js +10 -2
  105. package/dist/lib/external/mcp/McpServer.js +4 -0
  106. package/dist/lib/external/mcp/handlers/TargetClassifier.d.ts +1 -1
  107. package/dist/lib/external/mcp/handlers/bootstrap/BootstrapSession.d.ts +8 -16
  108. package/dist/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +10 -10
  109. package/dist/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.d.ts +7 -0
  110. package/dist/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +20 -0
  111. package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.d.ts +52 -132
  112. package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +204 -17
  113. package/dist/lib/external/mcp/handlers/bootstrap/base-dimensions.d.ts +11 -75
  114. package/dist/lib/external/mcp/handlers/bootstrap/base-dimensions.js +40 -191
  115. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.d.ts +13 -78
  116. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +30 -52
  117. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.d.ts +0 -1
  118. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.d.ts +20 -0
  119. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +432 -0
  120. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.d.ts +99 -12
  121. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +188 -169
  122. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +7 -17
  123. package/dist/lib/external/mcp/handlers/bootstrap/refine.js +8 -0
  124. package/dist/lib/external/mcp/handlers/bootstrap/shared/async-fill-helpers.d.ts +46 -0
  125. package/dist/lib/external/mcp/handlers/bootstrap/shared/async-fill-helpers.js +58 -0
  126. package/dist/lib/external/mcp/handlers/bootstrap/shared/audit-helpers.d.ts +25 -0
  127. package/dist/lib/external/mcp/handlers/bootstrap/shared/audit-helpers.js +47 -0
  128. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +50 -12
  129. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +30 -10
  130. package/dist/lib/external/mcp/handlers/bootstrap/shared/dimension-text.js +1 -1
  131. package/dist/lib/external/mcp/handlers/bootstrap/shared/handler-types.d.ts +24 -0
  132. package/dist/lib/external/mcp/handlers/bootstrap/shared/handler-types.js +14 -0
  133. package/dist/lib/external/mcp/handlers/bootstrap/shared/panorama-utils.d.ts +14 -0
  134. package/dist/lib/external/mcp/handlers/bootstrap/shared/panorama-utils.js +48 -0
  135. package/dist/lib/external/mcp/handlers/bootstrap/shared/session-helpers.d.ts +21 -0
  136. package/dist/lib/external/mcp/handlers/bootstrap/shared/session-helpers.js +45 -0
  137. package/dist/lib/external/mcp/handlers/bootstrap/shared/skill-generator.d.ts +1 -1
  138. package/dist/lib/external/mcp/handlers/bootstrap/shared/target-file-map.d.ts +27 -0
  139. package/dist/lib/external/mcp/handlers/bootstrap/shared/target-file-map.js +44 -0
  140. package/dist/lib/external/mcp/handlers/bootstrap-external.d.ts +23 -10
  141. package/dist/lib/external/mcp/handlers/bootstrap-external.js +41 -51
  142. package/dist/lib/external/mcp/handlers/bootstrap-internal.d.ts +2 -0
  143. package/dist/lib/external/mcp/handlers/bootstrap-internal.js +117 -82
  144. package/dist/lib/external/mcp/handlers/consolidated.d.ts +4 -4
  145. package/dist/lib/external/mcp/handlers/consolidated.js +108 -332
  146. package/dist/lib/external/mcp/handlers/dimension-complete-external.js +71 -2
  147. package/dist/lib/external/mcp/handlers/evolve-external.d.ts +54 -0
  148. package/dist/lib/external/mcp/handlers/evolve-external.js +229 -0
  149. package/dist/lib/external/mcp/handlers/knowledge.js +30 -5
  150. package/dist/lib/external/mcp/handlers/rescan-external.d.ts +76 -0
  151. package/dist/lib/external/mcp/handlers/rescan-external.js +335 -0
  152. package/dist/lib/external/mcp/handlers/rescan-internal.d.ts +120 -0
  153. package/dist/lib/external/mcp/handlers/rescan-internal.js +359 -0
  154. package/dist/lib/external/mcp/handlers/search.d.ts +6 -5
  155. package/dist/lib/external/mcp/handlers/search.js +6 -5
  156. package/dist/lib/external/mcp/handlers/types.d.ts +2 -1
  157. package/dist/lib/external/mcp/handlers/wiki-external.js +2 -2
  158. package/dist/lib/external/mcp/tools.d.ts +8 -18
  159. package/dist/lib/external/mcp/tools.js +58 -2
  160. package/dist/lib/http/routes/ai.js +111 -30
  161. package/dist/lib/http/routes/candidates.js +11 -4
  162. package/dist/lib/http/routes/commands.js +10 -1
  163. package/dist/lib/http/routes/health.js +11 -0
  164. package/dist/lib/http/routes/knowledge.js +122 -1
  165. package/dist/lib/http/routes/modules.js +52 -3
  166. package/dist/lib/http/routes/panorama.js +16 -4
  167. package/dist/lib/http/routes/recipes.js +7 -0
  168. package/dist/lib/http/utils/routeHelpers.js +2 -1
  169. package/dist/lib/infrastructure/cache/CacheCoordinator.d.ts +41 -0
  170. package/dist/lib/infrastructure/cache/CacheCoordinator.js +105 -0
  171. package/dist/lib/infrastructure/database/migrations/006_lifecycle_transition_events.d.ts +7 -0
  172. package/dist/lib/infrastructure/database/migrations/006_lifecycle_transition_events.js +28 -0
  173. package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +1 -1
  174. package/dist/lib/injection/ServiceContainer.d.ts +6 -5
  175. package/dist/lib/injection/ServiceContainer.js +64 -25
  176. package/dist/lib/injection/ServiceMap.d.ts +10 -1
  177. package/dist/lib/injection/modules/AiModule.d.ts +6 -9
  178. package/dist/lib/injection/modules/AiModule.js +82 -39
  179. package/dist/lib/injection/modules/KnowledgeModule.js +15 -1
  180. package/dist/lib/injection/modules/PanoramaModule.js +1 -1
  181. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +4 -0
  182. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +16 -1
  183. package/dist/lib/service/bootstrap/BootstrapEventEmitter.d.ts +3 -2
  184. package/dist/lib/service/bootstrap/BootstrapEventEmitter.js +1 -1
  185. package/dist/lib/service/bootstrap/DeliveryVerifier.d.ts +51 -0
  186. package/dist/lib/service/bootstrap/DeliveryVerifier.js +163 -0
  187. package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +5 -0
  188. package/dist/lib/service/bootstrap/UiStartupTasks.js +20 -0
  189. package/dist/lib/service/bootstrap/bootstrap-event-types.d.ts +54 -0
  190. package/dist/lib/service/bootstrap/bootstrap-event-types.js +10 -0
  191. package/dist/lib/service/cleanup/CleanupService.d.ts +132 -0
  192. package/dist/lib/service/cleanup/CleanupService.js +571 -0
  193. package/dist/lib/service/delivery/AgentInstructionsGenerator.js +39 -43
  194. package/dist/lib/service/delivery/FileProtection.d.ts +20 -0
  195. package/dist/lib/service/delivery/FileProtection.js +54 -0
  196. package/dist/lib/service/delivery/SkillsSyncer.js +16 -21
  197. package/dist/lib/service/evolution/ContentPatcher.d.ts +44 -0
  198. package/dist/lib/service/evolution/ContentPatcher.js +310 -0
  199. package/dist/lib/service/evolution/ProposalExecutor.d.ts +4 -0
  200. package/dist/lib/service/evolution/ProposalExecutor.js +77 -13
  201. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.d.ts +64 -0
  202. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.js +458 -0
  203. package/dist/lib/service/evolution/RecipeRelevanceAuditor.d.ts +89 -0
  204. package/dist/lib/service/evolution/RecipeRelevanceAuditor.js +492 -0
  205. package/dist/lib/service/evolution/createSupersedeProposal.d.ts +44 -0
  206. package/dist/lib/service/evolution/createSupersedeProposal.js +81 -0
  207. package/dist/lib/service/guard/ComplianceReporter.d.ts +4 -0
  208. package/dist/lib/service/guard/ComplianceReporter.js +51 -0
  209. package/dist/lib/service/guard/GuardCheckEngine.js +5 -4
  210. package/dist/lib/service/knowledge/CodeEntityGraph.d.ts +6 -0
  211. package/dist/lib/service/knowledge/CodeEntityGraph.js +16 -0
  212. package/dist/lib/service/knowledge/ConfidenceRouter.js +1 -1
  213. package/dist/lib/service/knowledge/KnowledgeService.d.ts +11 -1
  214. package/dist/lib/service/knowledge/KnowledgeService.js +67 -14
  215. package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +225 -0
  216. package/dist/lib/service/knowledge/RecipeProductionGateway.js +384 -0
  217. package/dist/lib/service/module/ModuleService.js +10 -19
  218. package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +10 -1
  219. package/dist/lib/service/panorama/CouplingAnalyzer.js +44 -2
  220. package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +4 -3
  221. package/dist/lib/service/panorama/DimensionAnalyzer.js +40 -151
  222. package/dist/lib/service/panorama/LayerInferrer.d.ts +16 -1
  223. package/dist/lib/service/panorama/LayerInferrer.js +118 -1
  224. package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +9 -0
  225. package/dist/lib/service/panorama/ModuleDiscoverer.js +58 -2
  226. package/dist/lib/service/panorama/PanoramaAggregator.d.ts +6 -2
  227. package/dist/lib/service/panorama/PanoramaAggregator.js +84 -6
  228. package/dist/lib/service/panorama/PanoramaScanner.js +28 -0
  229. package/dist/lib/service/panorama/PanoramaService.js +10 -5
  230. package/dist/lib/service/panorama/PanoramaTypes.d.ts +38 -0
  231. package/dist/lib/service/panorama/RoleRefiner.d.ts +2 -0
  232. package/dist/lib/service/panorama/RoleRefiner.js +41 -0
  233. package/dist/lib/service/panorama/TechStackProfiler.d.ts +13 -0
  234. package/dist/lib/service/panorama/TechStackProfiler.js +191 -0
  235. package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
  236. package/dist/lib/service/search/SearchEngine.d.ts +11 -10
  237. package/dist/lib/service/search/SearchEngine.js +38 -36
  238. package/dist/lib/service/search/SearchTypes.d.ts +14 -8
  239. package/dist/lib/service/search/SearchTypes.js +1 -1
  240. package/dist/lib/service/search/tokenizer.d.ts +1 -1
  241. package/dist/lib/service/search/tokenizer.js +2 -2
  242. package/dist/lib/service/skills/SignalCollector.d.ts +1 -0
  243. package/dist/lib/service/skills/SignalCollector.js +6 -5
  244. package/dist/lib/service/vector/ContextualEnricher.d.ts +1 -0
  245. package/dist/lib/service/vector/ContextualEnricher.js +4 -0
  246. package/dist/lib/shared/LanguageService.js +3 -0
  247. package/dist/lib/shared/developer-identity.d.ts +18 -0
  248. package/dist/lib/shared/developer-identity.js +62 -0
  249. package/dist/lib/shared/schemas/common.d.ts +4 -4
  250. package/dist/lib/shared/schemas/http-requests.d.ts +20 -18
  251. package/dist/lib/shared/schemas/http-requests.js +17 -6
  252. package/dist/lib/shared/schemas/mcp-tools.d.ts +32 -2
  253. package/dist/lib/shared/schemas/mcp-tools.js +38 -0
  254. package/dist/lib/types/evolution.d.ts +135 -0
  255. package/dist/lib/types/evolution.js +6 -0
  256. package/dist/lib/types/graph-shared.d.ts +25 -0
  257. package/dist/lib/types/graph-shared.js +7 -0
  258. package/dist/lib/types/knowledge-wire.d.ts +132 -0
  259. package/dist/lib/types/knowledge-wire.js +7 -0
  260. package/dist/lib/types/project-snapshot-builder.d.ts +19 -0
  261. package/dist/lib/types/project-snapshot-builder.js +189 -0
  262. package/dist/lib/types/project-snapshot.d.ts +399 -0
  263. package/dist/lib/types/project-snapshot.js +17 -0
  264. package/dist/lib/types/search-wire.d.ts +46 -0
  265. package/dist/lib/types/search-wire.js +7 -0
  266. package/dist/lib/types/snapshot-views.d.ts +58 -0
  267. package/dist/lib/types/snapshot-views.js +103 -0
  268. package/package.json +1 -1
  269. package/skills/autosnippet-recipes/SKILL.md +1 -1
  270. package/templates/instructions/agent-static.md +2 -0
  271. package/templates/instructions/conventions.md +3 -1
  272. package/templates/recipes-setup/README.md +2 -2
  273. package/dashboard/dist/assets/icons-BJ2mUBi8.js +0 -1
  274. package/dashboard/dist/assets/index-B659K9t5.js +0 -128
  275. package/dashboard/dist/assets/index-NCm40PMD.css +0 -1
  276. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.d.ts +0 -169
  277. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +0 -727
  278. package/dist/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.d.ts +0 -370
  279. package/dist/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +0 -821
@@ -25,6 +25,15 @@ const logger = Logger.getInstance();
25
25
  function getContainer() {
26
26
  return getServiceContainer();
27
27
  }
28
+ /** 检查 AI Provider 是否可用(非 mock),不可用则抛 ValidationError */
29
+ function requireAiReady() {
30
+ const container = getContainer();
31
+ const manager = container.singletons?._aiProviderManager;
32
+ if (manager?.isMock) {
33
+ throw new ValidationError('AI Provider 未配置,当前为 Mock 模式。请先在 .env 中配置 API Key。');
34
+ }
35
+ return container;
36
+ }
28
37
  // ═══════════════════════════════════════════════════════
29
38
  // UI 语言偏好 — 前端 ↔ 服务端同步
30
39
  // ═══════════════════════════════════════════════════════
@@ -76,22 +85,19 @@ router.get('/providers', async (req, res) => {
76
85
  });
77
86
  /**
78
87
  * GET /api/v1/ai/config
79
- * 获取当前 AI 配置
88
+ * 获取当前 AI 配置(优先从 AiProviderManager 读取)
80
89
  */
81
90
  router.get('/config', async (req, res) => {
82
91
  const container = getServiceContainer();
83
- const p = container.singletons?.aiProvider;
92
+ const manager = container.singletons?._aiProviderManager;
84
93
  res.json({
85
94
  success: true,
86
- data: {
87
- provider: p?.name || '',
88
- model: p?.model || '',
89
- },
95
+ data: { provider: manager.name, model: manager.model, isMock: manager.isMock },
90
96
  });
91
97
  });
92
98
  /**
93
99
  * POST /api/v1/ai/config
94
- * 更新 AI 配置(切换提供商/模型)
100
+ * 更新 AI 配置(切换提供商/模型)— 通过 AiProviderManager 统一热切换
95
101
  */
96
102
  router.post('/config', validate(AiConfigBody), async (req, res) => {
97
103
  const { provider, model } = req.body;
@@ -106,18 +112,13 @@ router.post('/config', validate(AiConfigBody), async (req, res) => {
106
112
  catch (error) {
107
113
  throw new ValidationError(`Invalid provider: ${error.message}`);
108
114
  }
109
- // 同步到 DI 容器,使 SearchEngine / Agent / IndexingPipeline 等也使用新 provider
110
- try {
111
- const container = getServiceContainer();
112
- container.reloadAiProvider(newProvider);
113
- logger.info('AI provider synced to DI container', {
114
- provider: provider.toLowerCase(),
115
- model: newProvider.model,
116
- });
117
- }
118
- catch (err) {
119
- logger.debug('DI container 同步 AI provider 失败', { error: err.message });
120
- }
115
+ // 通过 reloadAiProvider AiProviderManager.switchProvider() 统一热切换
116
+ const container = getServiceContainer();
117
+ container.reloadAiProvider(newProvider);
118
+ logger.info('AI provider switched via AiProviderManager', {
119
+ provider: provider.toLowerCase(),
120
+ model: newProvider.model,
121
+ });
121
122
  res.json({
122
123
  success: true,
123
124
  data: {
@@ -127,13 +128,58 @@ router.post('/config', validate(AiConfigBody), async (req, res) => {
127
128
  },
128
129
  });
129
130
  });
131
+ /**
132
+ * POST /api/v1/ai/mock/cleanup
133
+ * 清理 Mock 模式产生的候选数据
134
+ */
135
+ router.post('/mock/cleanup', async (_req, res) => {
136
+ const container = getContainer();
137
+ const knowledgeService = container.get('knowledgeService');
138
+ const dbConn = container.get('database');
139
+ const rawDb = dbConn.getDb();
140
+ // 查找所有 mock 来源的候选
141
+ const mockSources = ['mock-bootstrap', 'mock-pipeline'];
142
+ let totalDeleted = 0;
143
+ for (const source of mockSources) {
144
+ const rows = rawDb
145
+ .prepare('SELECT id FROM knowledge_entries WHERE source = ?')
146
+ .all(source);
147
+ for (const row of rows) {
148
+ try {
149
+ await knowledgeService.delete(row.id, { userId: 'system:mock-cleanup' });
150
+ totalDeleted++;
151
+ }
152
+ catch {
153
+ logger.debug(`Mock cleanup: failed to delete ${row.id}`);
154
+ }
155
+ }
156
+ }
157
+ // 清理 mock 相关的 semantic_memories
158
+ try {
159
+ rawDb
160
+ .prepare("DELETE FROM semantic_memories WHERE source = 'bootstrap' AND metadata LIKE '%mock%'")
161
+ .run();
162
+ }
163
+ catch {
164
+ // 表可能不存在
165
+ }
166
+ logger.info(`Mock cleanup completed: ${totalDeleted} entries deleted`);
167
+ const rt = getRealtimeService();
168
+ if (rt) {
169
+ rt.broadcastEvent('mock-cleanup-completed', { deleted: totalDeleted });
170
+ }
171
+ res.json({
172
+ success: true,
173
+ data: { deleted: totalDeleted },
174
+ });
175
+ });
130
176
  /**
131
177
  * POST /api/v1/ai/summarize
132
178
  * AI 摘要生成
133
179
  */
134
180
  router.post('/summarize', validate(AiSummarizeBody), async (req, res) => {
135
181
  const { code, language } = req.body;
136
- const container = getContainer();
182
+ const container = requireAiReady();
137
183
  const factory = container.get('agentFactory');
138
184
  const result = await factory.scanKnowledge({
139
185
  label: 'code',
@@ -158,7 +204,7 @@ router.post('/translate', validate(AiTranslateBody), async (req, res) => {
158
204
  });
159
205
  }
160
206
  try {
161
- const container = getContainer();
207
+ const container = requireAiReady();
162
208
  const factory = container.get('agentFactory');
163
209
  const result = await factory.translateToEnglish(summary, usageGuide);
164
210
  if (result?.error) {
@@ -197,7 +243,7 @@ router.post('/translate', validate(AiTranslateBody), async (req, res) => {
197
243
  */
198
244
  router.post('/chat', validate(AiChatBody), async (req, res) => {
199
245
  const { prompt, history, lang, conversationId } = req.body;
200
- const container = getContainer();
246
+ const container = requireAiReady();
201
247
  const factory = container.get('agentFactory');
202
248
  // ── 对话持久化: 从 ConversationStore 加载历史 ──
203
249
  let convStore = null;
@@ -309,7 +355,7 @@ router.post('/chat', validate(AiChatBody), async (req, res) => {
309
355
  */
310
356
  router.post('/agent/tool', validate(AiToolBody), async (req, res) => {
311
357
  const { tool, params } = req.body;
312
- const container = getContainer();
358
+ const container = requireAiReady();
313
359
  const factory = container.get('agentFactory');
314
360
  const result = await factory.invokeAgent(tool, params);
315
361
  res.json({ success: true, data: result });
@@ -332,7 +378,7 @@ const DAG_TASK_HANDLERS = {
332
378
  };
333
379
  router.post('/agent/task', validate(AiTaskBody), async (req, res) => {
334
380
  const { task, params } = req.body;
335
- const container = getContainer();
381
+ const container = requireAiReady();
336
382
  const factory = container.get('agentFactory');
337
383
  // 优先尝试 DAG 任务
338
384
  const dagHandler = DAG_TASK_HANDLERS[task];
@@ -530,7 +576,7 @@ router.post('/env-config', validate(AiEnvConfigBody), async (req, res) => {
530
576
  for (const [k, v] of Object.entries(updates)) {
531
577
  process.env[k] = String(v);
532
578
  }
533
- // 尝试热切换 AI Provider(包括依赖 AI 的所有服务)
579
+ // 尝试热切换 AI Provider(通过 AiProviderManager 统一处理)
534
580
  try {
535
581
  const newProvider = createProvider({
536
582
  provider: provider.toLowerCase(),
@@ -538,7 +584,7 @@ router.post('/env-config', validate(AiEnvConfigBody), async (req, res) => {
538
584
  });
539
585
  const container = getServiceContainer();
540
586
  container.reloadAiProvider(newProvider);
541
- logger.info('AI provider hot-swapped after env update', {
587
+ logger.info('AI provider hot-swapped via AiProviderManager after env update', {
542
588
  provider,
543
589
  model: newProvider.model,
544
590
  });
@@ -576,7 +622,7 @@ router.post('/env-config', validate(AiEnvConfigBody), async (req, res) => {
576
622
  */
577
623
  router.post('/chat/stream', validate(AiStreamBody), async (req, res) => {
578
624
  const { prompt, history, lang } = req.body;
579
- const container = getContainer();
625
+ const container = requireAiReady();
580
626
  const factory = container.get('agentFactory');
581
627
  const session = createStreamSession('chat');
582
628
  logger.debug('SSE session created', { sessionId: session.sessionId });
@@ -626,18 +672,53 @@ router.post('/chat/stream', validate(AiStreamBody), async (req, res) => {
626
672
  runtime
627
673
  .execute(message)
628
674
  .then((result) => {
675
+ const replyText = result.reply || '';
629
676
  // 发送最终文本
630
- if (result.reply) {
677
+ if (replyText) {
631
678
  const textId = `text_${Date.now()}`;
632
679
  session.send({ type: 'text:start', id: textId, role: 'assistant' });
633
- session.send({ type: 'text:delta', id: textId, delta: result.reply });
680
+ session.send({ type: 'text:delta', id: textId, delta: replyText });
634
681
  session.send({ type: 'text:end', id: textId });
635
682
  }
683
+ else {
684
+ logger.warn('SSE session: empty reply from AgentRuntime', {
685
+ sessionId: session.sessionId,
686
+ iterations: result.iterations,
687
+ toolCalls: result.toolCalls?.length || 0,
688
+ });
689
+ }
636
690
  session.end({
637
- text: result.reply,
691
+ text: replyText || '抱歉,AI 未能生成有效回复。请重试或换个问题。',
638
692
  toolCalls: result.toolCalls || [],
639
693
  iterations: result.iterations || 0,
640
694
  });
695
+ // ── Token 用量持久化(streaming) ──
696
+ try {
697
+ const tokenUsage = result.tokenUsage;
698
+ if (tokenUsage) {
699
+ const tokenStore = container.get('tokenUsageStore');
700
+ const aiProvider = container.singletons?.aiProvider;
701
+ tokenStore.record({
702
+ source: 'user',
703
+ provider: aiProvider?.name ?? undefined,
704
+ model: aiProvider?.model ?? undefined,
705
+ inputTokens: tokenUsage.input || 0,
706
+ outputTokens: tokenUsage.output || 0,
707
+ durationMs: result.durationMs || 0,
708
+ toolCalls: result.toolCalls?.length || 0,
709
+ });
710
+ try {
711
+ const realtime = getRealtimeService();
712
+ realtime?.broadcastTokenUsageUpdated?.();
713
+ }
714
+ catch {
715
+ /* ignore */
716
+ }
717
+ }
718
+ }
719
+ catch {
720
+ /* token tracking should never break streaming */
721
+ }
641
722
  logger.debug('SSE session completed', {
642
723
  sessionId: session.sessionId,
643
724
  events: session.buffer.length,
@@ -55,6 +55,13 @@ router.post('/enrich', validate(EnrichBody), async (req, res) => {
55
55
  let enrichedCount = 0;
56
56
  const results = [];
57
57
  if (aiProvider) {
58
+ // Mock 模式下跳过 AI enrichment
59
+ if (aiProvider.name === 'mock') {
60
+ return void res.json({
61
+ success: true,
62
+ data: { enriched: 0, total: candidates.length, results: [], mock: true },
63
+ });
64
+ }
58
65
  let enriched = [];
59
66
  try {
60
67
  // 获取用户语言偏好
@@ -418,8 +425,8 @@ router.post('/refine-preview', validate(RefinePreviewBody), async (req, res) =>
418
425
  const container = getServiceContainer();
419
426
  const knowledgeService = container.get('knowledgeService');
420
427
  const aiProvider = container.get('aiProvider');
421
- if (!aiProvider) {
422
- throw new ValidationError('AI provider not configured');
428
+ if (!aiProvider || aiProvider.name === 'mock') {
429
+ throw new ValidationError('AI Provider 未配置,当前为 Mock 模式。请先配置 API Key。');
423
430
  }
424
431
  const entry = await knowledgeService.get(candidateId);
425
432
  if (!entry) {
@@ -459,8 +466,8 @@ router.post('/refine-preview-stream', validate(RefinePreviewBody), async (req, r
459
466
  const container = getServiceContainer();
460
467
  const knowledgeService = container.get('knowledgeService');
461
468
  const aiProvider = container.get('aiProvider');
462
- if (!aiProvider) {
463
- throw new ValidationError('AI provider not configured');
469
+ if (!aiProvider || aiProvider.name === 'mock') {
470
+ throw new ValidationError('AI Provider 未配置,当前为 Mock 模式。请先配置 API Key。');
464
471
  }
465
472
  const entry = await knowledgeService.get(candidateId);
466
473
  if (!entry) {
@@ -29,6 +29,15 @@ router.post('/spm-map', async (req, res) => {
29
29
  */
30
30
  router.post('/embed', async (req, res) => {
31
31
  const container = getServiceContainer();
32
+ // Mock 模式下向量构建需要 embedding — 拒绝执行
33
+ const manager = container.singletons?._aiProviderManager;
34
+ if (manager?.isMock) {
35
+ res.status(400).json({
36
+ success: false,
37
+ message: 'AI Provider 未配置,当前为 Mock 模式。Embedding 不可用。',
38
+ });
39
+ return;
40
+ }
32
41
  // 优先使用 VectorService (新架构), 降级到 indexingPipeline (旧架构)
33
42
  const vectorService = container.services.vectorService ? container.get('vectorService') : null;
34
43
  let result;
@@ -80,7 +89,7 @@ router.get('/status', async (req, res) => {
80
89
  spmMap: { available: false },
81
90
  };
82
91
  try {
83
- const indexingPipeline = container.get('indexingPipeline');
92
+ const _indexingPipeline = container.get('indexingPipeline');
84
93
  status.index.ready = true; // IndexingPipeline is available
85
94
  }
86
95
  catch {
@@ -1,5 +1,6 @@
1
1
  /** 健康检查端点 */
2
2
  import express from 'express';
3
+ import { getDeveloperIdentity } from '../../shared/developer-identity.js';
3
4
  const router = express.Router();
4
5
  /**
5
6
  * GET /api/v1/health
@@ -25,4 +26,14 @@ router.get('/ready', (req, res) => {
25
26
  timestamp: Math.floor(Date.now() / 1000),
26
27
  });
27
28
  });
29
+ /**
30
+ * GET /api/v1/health/identity
31
+ * 当前开发者身份(从 git config / 环境变量解析)
32
+ */
33
+ router.get('/identity', (_req, res) => {
34
+ res.json({
35
+ success: true,
36
+ developer: getDeveloperIdentity(),
37
+ });
38
+ });
28
39
  export default router;
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import express from 'express';
7
7
  import { ioLimit } from '#shared/concurrency.js';
8
- import { BatchPublishBody, CreateKnowledgeBody, DeprecateKnowledgeBody, KnowledgeUsageBody, UpdateKnowledgeBody, } from '#shared/schemas/http-requests.js';
8
+ import { BatchDeleteBody, BatchDeprecateBody, BatchPublishBody, CreateKnowledgeBody, DeprecateKnowledgeBody, KnowledgeUsageBody, UpdateKnowledgeBody, } from '#shared/schemas/http-requests.js';
9
9
  import Logger from '../../infrastructure/logging/Logger.js';
10
10
  import { getServiceContainer } from '../../injection/ServiceContainer.js';
11
11
  import { validate } from '../middleware/validate.js';
@@ -140,6 +140,28 @@ router.get('/lifecycle', async (req, res) => {
140
140
  }
141
141
  res.json({ success: true, data: lifecycle });
142
142
  });
143
+ /**
144
+ * POST /api/v1/knowledge/quality/refresh-all
145
+ * 批量重新计算所有条目的质量评分
146
+ */
147
+ router.post('/quality/refresh-all', async (_req, res) => {
148
+ const container = getServiceContainer();
149
+ const knowledgeService = container.get('knowledgeService');
150
+ const result = await knowledgeService.list({}, { page: 1, pageSize: 10000 });
151
+ const all = result.data;
152
+ let updated = 0;
153
+ let failed = 0;
154
+ for (const entry of all) {
155
+ try {
156
+ await knowledgeService.updateQuality(entry.id);
157
+ updated++;
158
+ }
159
+ catch {
160
+ failed++;
161
+ }
162
+ }
163
+ res.json({ success: true, data: { updated, failed, total: all.length } });
164
+ });
143
165
  /**
144
166
  * GET /api/v1/knowledge/:id
145
167
  * 获取知识条目详情
@@ -229,6 +251,54 @@ router.patch('/:id/reactivate', requirePermission('knowledge', 'update'), async
229
251
  const entry = await knowledgeService.reactivate(id, context);
230
252
  res.json({ success: true, data: sanitizeForAPI(entry) });
231
253
  });
254
+ /**
255
+ * PATCH /api/v1/knowledge/:id/stage
256
+ * 暂存 (pending → staging)
257
+ */
258
+ router.patch('/:id/stage', requirePermission('knowledge', 'update'), async (req, res) => {
259
+ const id = String(req.params.id);
260
+ const container = getServiceContainer();
261
+ const knowledgeService = container.get('knowledgeService');
262
+ const context = getContext(req);
263
+ const entry = await knowledgeService.stage(id, context);
264
+ res.json({ success: true, data: sanitizeForAPI(entry) });
265
+ });
266
+ /**
267
+ * PATCH /api/v1/knowledge/:id/evolve
268
+ * 进化 (active → evolving)
269
+ */
270
+ router.patch('/:id/evolve', requirePermission('knowledge', 'update'), async (req, res) => {
271
+ const id = String(req.params.id);
272
+ const container = getServiceContainer();
273
+ const knowledgeService = container.get('knowledgeService');
274
+ const context = getContext(req);
275
+ const entry = await knowledgeService.evolve(id, context);
276
+ res.json({ success: true, data: sanitizeForAPI(entry) });
277
+ });
278
+ /**
279
+ * PATCH /api/v1/knowledge/:id/decay
280
+ * 衰退 (active|evolving → decaying)
281
+ */
282
+ router.patch('/:id/decay', requirePermission('knowledge', 'update'), async (req, res) => {
283
+ const id = String(req.params.id);
284
+ const container = getServiceContainer();
285
+ const knowledgeService = container.get('knowledgeService');
286
+ const context = getContext(req);
287
+ const entry = await knowledgeService.decay(id, context);
288
+ res.json({ success: true, data: sanitizeForAPI(entry) });
289
+ });
290
+ /**
291
+ * PATCH /api/v1/knowledge/:id/restore
292
+ * 恢复为已发布 (decaying|evolving → active)
293
+ */
294
+ router.patch('/:id/restore', requirePermission('knowledge', 'update'), async (req, res) => {
295
+ const id = String(req.params.id);
296
+ const container = getServiceContainer();
297
+ const knowledgeService = container.get('knowledgeService');
298
+ const context = getContext(req);
299
+ const entry = await knowledgeService.restore(id, context);
300
+ res.json({ success: true, data: sanitizeForAPI(entry) });
301
+ });
232
302
  /* ═══ 批量操作 ═══════════════════════════════════════════ */
233
303
  /**
234
304
  * POST /api/v1/knowledge/batch-publish
@@ -258,6 +328,57 @@ router.post('/batch-publish', requirePermission('knowledge', 'publish'), validat
258
328
  },
259
329
  });
260
330
  });
331
+ /**
332
+ * POST /api/v1/knowledge/batch-delete
333
+ * 批量删除知识条目
334
+ */
335
+ router.post('/batch-delete', requirePermission('knowledge', 'delete'), validate(BatchDeleteBody), async (req, res) => {
336
+ const { ids } = req.body;
337
+ const container = getServiceContainer();
338
+ const knowledgeService = container.get('knowledgeService');
339
+ const context = getContext(req);
340
+ const results = await Promise.allSettled(ids.map((id) => ioLimit(() => knowledgeService.delete(id, context))));
341
+ const deleted = results.filter((r) => r.status === 'fulfilled').length;
342
+ const failed = results
343
+ .map((r, i) => (r.status === 'rejected' ? { id: ids[i], error: r.reason?.message } : null))
344
+ .filter(Boolean);
345
+ res.json({
346
+ success: true,
347
+ data: {
348
+ total: ids.length,
349
+ deletedCount: deleted,
350
+ failureCount: failed.length,
351
+ failed,
352
+ },
353
+ });
354
+ });
355
+ /**
356
+ * POST /api/v1/knowledge/batch-deprecate
357
+ * 批量废弃知识条目 (active → deprecated)
358
+ */
359
+ router.post('/batch-deprecate', requirePermission('knowledge', 'publish'), validate(BatchDeprecateBody), async (req, res) => {
360
+ const { ids, reason } = req.body;
361
+ const container = getServiceContainer();
362
+ const knowledgeService = container.get('knowledgeService');
363
+ const context = getContext(req);
364
+ const results = await Promise.allSettled(ids.map((id) => ioLimit(() => knowledgeService.deprecate(id, reason || 'batch deprecate', context))));
365
+ const deprecated = results
366
+ .filter((r) => r.status === 'fulfilled')
367
+ .map((r) => sanitizeForAPI(r.value));
368
+ const failed = results
369
+ .map((r, i) => (r.status === 'rejected' ? { id: ids[i], error: r.reason?.message } : null))
370
+ .filter(Boolean);
371
+ res.json({
372
+ success: true,
373
+ data: {
374
+ deprecated,
375
+ failed,
376
+ total: ids.length,
377
+ successCount: deprecated.length,
378
+ failureCount: failed.length,
379
+ },
380
+ });
381
+ });
261
382
  /* ═══ 使用 / 质量 ═══════════════════════════════════════ */
262
383
  /**
263
384
  * POST /api/v1/knowledge/:id/usage
@@ -5,7 +5,7 @@
5
5
  * 所有端点通过 container.get('moduleService') 获取 ModuleService 实例
6
6
  */
7
7
  import express from 'express';
8
- import { ModuleBootstrapBody, ScanFolderBody, ScanProjectBody, ScanTargetBody, } from '#shared/schemas/http-requests.js';
8
+ import { ModuleBootstrapBody, ModuleRescanBody, ScanFolderBody, ScanProjectBody, ScanTargetBody, } from '#shared/schemas/http-requests.js';
9
9
  import Logger from '../../infrastructure/logging/Logger.js';
10
10
  import { getServiceContainer } from '../../injection/ServiceContainer.js';
11
11
  import { validate } from '../middleware/validate.js';
@@ -416,14 +416,17 @@ router.get('/project-info', async (req, res) => {
416
416
  router.post('/bootstrap', validate(ModuleBootstrapBody), async (req, res) => {
417
417
  const { maxFiles, skipGuard, contentMaxLines } = req.body || {};
418
418
  const container = getServiceContainer();
419
- const agentFactory = container.get('agentFactory');
420
419
  logger.info('Bootstrap cold start initiated (ModuleService path)');
421
- const bootstrapResult = await agentFactory.bootstrapKnowledge({
420
+ // 直接调用 bootstrap-internal handler(统一编排管线)
421
+ const { bootstrapKnowledge } = await import('#external/mcp/handlers/bootstrap-internal.js');
422
+ const raw = await bootstrapKnowledge({ container, logger }, {
422
423
  maxFiles: maxFiles || 500,
423
424
  skipGuard: skipGuard || false,
424
425
  contentMaxLines: contentMaxLines || 120,
425
426
  loadSkills: true,
426
427
  });
428
+ const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
429
+ const bootstrapResult = parsed?.data || parsed;
427
430
  res.json({
428
431
  success: true,
429
432
  data: {
@@ -456,4 +459,50 @@ router.get('/bootstrap/status', async (req, res) => {
456
459
  data: taskManager.getSessionStatus(),
457
460
  });
458
461
  });
462
+ /**
463
+ * POST /api/v1/modules/bootstrap/cancel
464
+ * 取消正在运行的 bootstrap / rescan 异步填充会话
465
+ */
466
+ router.post('/bootstrap/cancel', async (req, res) => {
467
+ const container = getServiceContainer();
468
+ let taskManager = null;
469
+ try {
470
+ taskManager = container.get('bootstrapTaskManager');
471
+ }
472
+ catch {
473
+ /* not registered */
474
+ }
475
+ if (!taskManager) {
476
+ return void res.json({ success: true, message: 'No bootstrap task manager initialized' });
477
+ }
478
+ if (!taskManager.isRunning) {
479
+ return void res.json({ success: true, message: 'No active bootstrap session' });
480
+ }
481
+ const reason = req.body?.reason || 'Cancelled by user via Dashboard';
482
+ taskManager.abortSession(reason);
483
+ logger.info('Bootstrap session cancelled via HTTP', { reason });
484
+ res.json({
485
+ success: true,
486
+ data: taskManager.getSessionStatus(),
487
+ });
488
+ });
489
+ /**
490
+ * POST /api/v1/modules/rescan
491
+ * 增量扫描:保留已有 Recipe,重新分析项目,补齐缺失知识
492
+ * 使用内部 Agent pipeline 自动完成知识补齐
493
+ */
494
+ router.post('/rescan', validate(ModuleRescanBody), async (req, res) => {
495
+ const { reason, dimensions } = req.body || {};
496
+ const container = getServiceContainer();
497
+ logger.info('Rescan (internal) initiated from Dashboard', { reason, dimensions });
498
+ // 直接调用 rescan-internal handler(统一编排管线)
499
+ const { rescanInternal } = await import('#external/mcp/handlers/rescan-internal.js');
500
+ const raw = await rescanInternal({ container, logger }, { reason: reason || 'dashboard-rescan', dimensions });
501
+ const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
502
+ const result = parsed?.data || parsed;
503
+ res.json({
504
+ success: true,
505
+ data: result,
506
+ });
507
+ });
459
508
  export default router;
@@ -14,7 +14,7 @@ const router = express.Router();
14
14
  * GET /api/v1/panorama
15
15
  * 返回项目全景概览(层级、模块、覆盖率)
16
16
  */
17
- router.get('/', async (_req, res) => {
17
+ router.get('/', async (req, res) => {
18
18
  try {
19
19
  const container = getServiceContainer();
20
20
  const panoramaService = container.get('panoramaService');
@@ -25,6 +25,9 @@ router.get('/', async (_req, res) => {
25
25
  });
26
26
  return;
27
27
  }
28
+ if (req.query.refresh === 'true' && typeof panoramaService.invalidate === 'function') {
29
+ panoramaService.invalidate();
30
+ }
28
31
  if (typeof panoramaService.ensureData === 'function') {
29
32
  await panoramaService.ensureData();
30
33
  }
@@ -42,7 +45,7 @@ router.get('/', async (_req, res) => {
42
45
  * GET /api/v1/panorama/health
43
46
  * 返回全景健康度评分
44
47
  */
45
- router.get('/health', async (_req, res) => {
48
+ router.get('/health', async (req, res) => {
46
49
  try {
47
50
  const container = getServiceContainer();
48
51
  const panoramaService = container.get('panoramaService');
@@ -53,6 +56,9 @@ router.get('/health', async (_req, res) => {
53
56
  });
54
57
  return;
55
58
  }
59
+ if (req.query.refresh === 'true' && typeof panoramaService.invalidate === 'function') {
60
+ panoramaService.invalidate();
61
+ }
56
62
  if (typeof panoramaService.ensureData === 'function') {
57
63
  await panoramaService.ensureData();
58
64
  }
@@ -70,7 +76,7 @@ router.get('/health', async (_req, res) => {
70
76
  * GET /api/v1/panorama/gaps
71
77
  * 返回知识空白区列表
72
78
  */
73
- router.get('/gaps', async (_req, res) => {
79
+ router.get('/gaps', async (req, res) => {
74
80
  try {
75
81
  const container = getServiceContainer();
76
82
  const panoramaService = container.get('panoramaService');
@@ -81,6 +87,9 @@ router.get('/gaps', async (_req, res) => {
81
87
  });
82
88
  return;
83
89
  }
90
+ if (req.query.refresh === 'true' && typeof panoramaService.invalidate === 'function') {
91
+ panoramaService.invalidate();
92
+ }
84
93
  if (typeof panoramaService.ensureData === 'function') {
85
94
  await panoramaService.ensureData();
86
95
  }
@@ -98,7 +107,7 @@ router.get('/gaps', async (_req, res) => {
98
107
  * GET /api/v1/panorama/coverage
99
108
  * 返回各模块知识覆盖率热力图数据
100
109
  */
101
- router.get('/coverage', async (_req, res) => {
110
+ router.get('/coverage', async (req, res) => {
102
111
  try {
103
112
  const container = getServiceContainer();
104
113
  const panoramaService = container.get('panoramaService');
@@ -109,6 +118,9 @@ router.get('/coverage', async (_req, res) => {
109
118
  });
110
119
  return;
111
120
  }
121
+ if (req.query.refresh === 'true' && typeof panoramaService.invalidate === 'function') {
122
+ panoramaService.invalidate();
123
+ }
112
124
  if (typeof panoramaService.ensureData === 'function') {
113
125
  await panoramaService.ensureData();
114
126
  }
@@ -70,6 +70,13 @@ router.post('/discover-relations', async (req, res) => {
70
70
  data: { status: 'error', error: 'AgentFactory 不可用,请检查 AI Provider 配置' },
71
71
  });
72
72
  }
73
+ // Mock 模式下跳过 AI 关系发现
74
+ if (agentFactory.getAiProviderInfo?.()?.name === 'mock') {
75
+ return void res.json({
76
+ success: true,
77
+ data: { status: 'error', error: 'AI Provider 未配置,当前为 Mock 模式。请先配置 API Key。' },
78
+ });
79
+ }
73
80
  // 快速检查:至少需要 2 条活跃 Recipe
74
81
  try {
75
82
  const knowledgeService = container.get('knowledgeService');
@@ -3,13 +3,14 @@
3
3
  * 提取自各路由文件中的重复实现
4
4
  */
5
5
  import { KnowledgeEntry } from '../../domain/knowledge/KnowledgeEntry.js';
6
+ import { getDeveloperIdentity } from '../../shared/developer-identity.js';
6
7
  /**
7
8
  * 从请求中提取操作上下文(用户身份、IP、UA)
8
9
  * @returns }
9
10
  */
10
11
  export function getContext(req) {
11
12
  return {
12
- userId: String(req.headers['x-user-id'] || 'anonymous'),
13
+ userId: String(req.headers['x-user-id'] || getDeveloperIdentity()),
13
14
  ip: req.ip || '',
14
15
  userAgent: req.headers['user-agent'] || '',
15
16
  };