autosnippet 3.3.6 → 3.3.8

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 (275) hide show
  1. package/README.md +1 -0
  2. package/dashboard/dist/assets/icons-BMNb0V6L.js +1 -0
  3. package/dashboard/dist/assets/index-DHJ1Dj7u.css +1 -0
  4. package/dashboard/dist/assets/index-DV8biUkH.js +112 -0
  5. package/dashboard/dist/index.html +3 -3
  6. package/dist/bin/cli.js +8 -4
  7. package/dist/lib/agent/AgentRuntime.d.ts +2 -2
  8. package/dist/lib/agent/AgentRuntime.js +26 -18
  9. package/dist/lib/agent/core/ChatAgentPrompts.js +57 -21
  10. package/dist/lib/agent/core/LoopContext.d.ts +1 -0
  11. package/dist/lib/agent/core/ToolExecutionPipeline.js +13 -0
  12. package/dist/lib/agent/domain/ChatAgentTasks.js +4 -0
  13. package/dist/lib/agent/forced-summary.js +7 -2
  14. package/dist/lib/agent/memory/ActiveContext.d.ts +0 -2
  15. package/dist/lib/agent/memory/ActiveContext.js +0 -2
  16. package/dist/lib/agent/memory/MemoryEmbeddingStore.d.ts +49 -0
  17. package/dist/lib/agent/memory/MemoryEmbeddingStore.js +159 -0
  18. package/dist/lib/agent/memory/MemoryRetriever.d.ts +2 -0
  19. package/dist/lib/agent/memory/MemoryRetriever.js +25 -11
  20. package/dist/lib/agent/memory/MemoryStore.d.ts +8 -41
  21. package/dist/lib/agent/memory/MemoryStore.js +196 -261
  22. package/dist/lib/agent/memory/PersistentMemory.d.ts +2 -0
  23. package/dist/lib/agent/memory/PersistentMemory.js +4 -5
  24. package/dist/lib/agent/memory/SessionStore.d.ts +0 -2
  25. package/dist/lib/agent/memory/SessionStore.js +0 -2
  26. package/dist/lib/agent/tools/ast-graph.js +21 -19
  27. package/dist/lib/agent/tools/infrastructure.js +3 -2
  28. package/dist/lib/agent/tools/project-access.d.ts +2 -2
  29. package/dist/lib/agent/tools/project-access.js +5 -4
  30. package/dist/lib/bootstrap.js +2 -1
  31. package/dist/lib/cli/AiScanService.js +8 -21
  32. package/dist/lib/cli/KnowledgeSyncService.d.ts +7 -37
  33. package/dist/lib/cli/KnowledgeSyncService.js +23 -51
  34. package/dist/lib/core/ast/ProjectGraph.js +5 -27
  35. package/dist/lib/core/discovery/ConfigWatcher.d.ts +64 -0
  36. package/dist/lib/core/discovery/ConfigWatcher.js +336 -0
  37. package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +28 -0
  38. package/dist/lib/core/discovery/CustomConfigDiscoverer.js +1303 -0
  39. package/dist/lib/core/discovery/DiscovererPreference.d.ts +44 -0
  40. package/dist/lib/core/discovery/DiscovererPreference.js +141 -0
  41. package/dist/lib/core/discovery/DiscovererRegistry.d.ts +10 -1
  42. package/dist/lib/core/discovery/DiscovererRegistry.js +42 -2
  43. package/dist/lib/core/discovery/ProjectDiscoverer.d.ts +19 -0
  44. package/dist/lib/core/discovery/index.d.ts +2 -0
  45. package/dist/lib/core/discovery/index.js +4 -0
  46. package/dist/lib/core/discovery/parsers/CMakeParser.d.ts +32 -0
  47. package/dist/lib/core/discovery/parsers/CMakeParser.js +148 -0
  48. package/dist/lib/core/discovery/parsers/GradleDslParser.d.ts +43 -0
  49. package/dist/lib/core/discovery/parsers/GradleDslParser.js +171 -0
  50. package/dist/lib/core/discovery/parsers/JsonConfigParser.d.ts +45 -0
  51. package/dist/lib/core/discovery/parsers/JsonConfigParser.js +122 -0
  52. package/dist/lib/core/discovery/parsers/RubyDslParser.d.ts +49 -0
  53. package/dist/lib/core/discovery/parsers/RubyDslParser.js +282 -0
  54. package/dist/lib/core/discovery/parsers/StarlarkParser.d.ts +33 -0
  55. package/dist/lib/core/discovery/parsers/StarlarkParser.js +229 -0
  56. package/dist/lib/core/discovery/parsers/YamlConfigParser.d.ts +37 -0
  57. package/dist/lib/core/discovery/parsers/YamlConfigParser.js +212 -0
  58. package/dist/lib/domain/dimension/DimensionRegistry.d.ts +0 -2
  59. package/dist/lib/domain/dimension/DimensionRegistry.js +0 -2
  60. package/dist/lib/domain/dimension/DimensionSop.js +44 -33
  61. package/dist/lib/domain/dimension/UnifiedDimension.d.ts +0 -2
  62. package/dist/lib/domain/dimension/UnifiedDimension.js +0 -2
  63. package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +7 -1
  64. package/dist/lib/domain/knowledge/KnowledgeEntry.js +17 -3
  65. package/dist/lib/domain/knowledge/Lifecycle.d.ts +26 -0
  66. package/dist/lib/domain/knowledge/Lifecycle.js +42 -0
  67. package/dist/lib/domain/knowledge/index.d.ts +2 -1
  68. package/dist/lib/domain/knowledge/index.js +1 -1
  69. package/dist/lib/external/ai/AiProvider.d.ts +12 -0
  70. package/dist/lib/external/ai/AiProvider.js +24 -0
  71. package/dist/lib/external/ai/AiProviderManager.d.ts +101 -0
  72. package/dist/lib/external/ai/AiProviderManager.js +193 -0
  73. package/dist/lib/external/ai/providers/ClaudeProvider.js +11 -0
  74. package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +18 -0
  75. package/dist/lib/external/ai/providers/MockProvider.d.ts +21 -3
  76. package/dist/lib/external/ai/providers/MockProvider.js +290 -14
  77. package/dist/lib/external/ai/providers/OpenAiProvider.js +16 -0
  78. package/dist/lib/external/lark/LarkTransport.d.ts +5 -1
  79. package/dist/lib/external/lark/LarkTransport.js +10 -2
  80. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.d.ts +2 -1
  81. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/BootstrapSnapshot.js +102 -153
  82. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.d.ts +20 -0
  83. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +432 -0
  84. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +49 -24
  85. package/dist/lib/external/mcp/handlers/bootstrap/refine.js +8 -0
  86. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +1 -1
  87. package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +41 -37
  88. package/dist/lib/external/mcp/handlers/bootstrap-external.d.ts +9 -0
  89. package/dist/lib/external/mcp/handlers/bootstrap-external.js +3 -1
  90. package/dist/lib/external/mcp/handlers/bootstrap-internal.js +2 -0
  91. package/dist/lib/external/mcp/handlers/consolidated.js +2 -1
  92. package/dist/lib/external/mcp/handlers/dimension-complete-external.js +9 -4
  93. package/dist/lib/external/mcp/handlers/evolve-external.d.ts +1 -0
  94. package/dist/lib/external/mcp/handlers/evolve-external.js +18 -18
  95. package/dist/lib/external/mcp/handlers/guard.js +15 -24
  96. package/dist/lib/external/mcp/handlers/knowledge.js +5 -4
  97. package/dist/lib/external/mcp/handlers/panorama.js +9 -9
  98. package/dist/lib/external/mcp/handlers/rescan-external.js +7 -6
  99. package/dist/lib/external/mcp/handlers/rescan-internal.js +9 -5
  100. package/dist/lib/external/mcp/handlers/search.js +3 -1
  101. package/dist/lib/external/mcp/handlers/skill.js +4 -4
  102. package/dist/lib/external/mcp/handlers/structure.js +8 -12
  103. package/dist/lib/external/mcp/handlers/system.js +10 -34
  104. package/dist/lib/http/routes/ai.js +109 -30
  105. package/dist/lib/http/routes/candidates.js +11 -4
  106. package/dist/lib/http/routes/commands.js +10 -1
  107. package/dist/lib/http/routes/guardReport.js +3 -5
  108. package/dist/lib/http/routes/health.js +11 -0
  109. package/dist/lib/http/routes/modules.js +27 -0
  110. package/dist/lib/http/routes/panorama.js +12 -12
  111. package/dist/lib/http/routes/recipes.js +66 -8
  112. package/dist/lib/http/routes/remote.js +3 -13
  113. package/dist/lib/http/routes/search.js +11 -8
  114. package/dist/lib/http/utils/routeHelpers.js +2 -1
  115. package/dist/lib/infrastructure/audit/AuditLogger.d.ts +20 -3
  116. package/dist/lib/infrastructure/audit/AuditStore.d.ts +28 -29
  117. package/dist/lib/infrastructure/audit/AuditStore.js +81 -88
  118. package/dist/lib/infrastructure/database/drizzle/schema.d.ts +180 -2
  119. package/dist/lib/infrastructure/database/drizzle/schema.js +23 -3
  120. package/dist/lib/injection/ServiceContainer.d.ts +6 -5
  121. package/dist/lib/injection/ServiceContainer.js +18 -31
  122. package/dist/lib/injection/ServiceMap.d.ts +22 -0
  123. package/dist/lib/injection/modules/AiModule.d.ts +6 -9
  124. package/dist/lib/injection/modules/AiModule.js +82 -39
  125. package/dist/lib/injection/modules/AppModule.js +2 -1
  126. package/dist/lib/injection/modules/GuardModule.js +5 -5
  127. package/dist/lib/injection/modules/InfraModule.js +60 -0
  128. package/dist/lib/injection/modules/KnowledgeModule.js +86 -51
  129. package/dist/lib/injection/modules/PanoramaModule.js +16 -10
  130. package/dist/lib/injection/modules/VectorModule.js +3 -0
  131. package/dist/lib/repository/audit/AuditRepository.d.ts +107 -0
  132. package/dist/lib/repository/audit/AuditRepository.js +272 -0
  133. package/dist/lib/repository/base/RepositoryBase.d.ts +46 -0
  134. package/dist/lib/repository/base/RepositoryBase.js +32 -0
  135. package/dist/lib/repository/bootstrap/BootstrapRepository.d.ts +94 -0
  136. package/dist/lib/repository/bootstrap/BootstrapRepository.js +246 -0
  137. package/dist/lib/repository/code/CodeEntityRepository.d.ts +91 -0
  138. package/dist/lib/repository/code/CodeEntityRepository.js +361 -0
  139. package/dist/lib/repository/delivery/DeliveryRepoAdapter.d.ts +39 -0
  140. package/dist/lib/repository/delivery/DeliveryRepoAdapter.js +23 -0
  141. package/dist/lib/repository/evolution/LifecycleEventRepository.d.ts +51 -0
  142. package/dist/lib/repository/evolution/LifecycleEventRepository.js +119 -0
  143. package/dist/lib/repository/evolution/ProposalRepository.d.ts +9 -12
  144. package/dist/lib/repository/evolution/ProposalRepository.js +114 -57
  145. package/dist/lib/repository/guard/GuardViolationRepository.d.ts +104 -0
  146. package/dist/lib/repository/guard/GuardViolationRepository.js +217 -0
  147. package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.d.ts +129 -0
  148. package/dist/lib/repository/knowledge/KnowledgeEdgeRepository.js +475 -0
  149. package/dist/lib/repository/knowledge/KnowledgeFileStore.d.ts +39 -0
  150. package/dist/lib/repository/knowledge/KnowledgeFileStore.js +12 -0
  151. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +295 -11
  152. package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +608 -13
  153. package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.d.ts +61 -0
  154. package/dist/lib/repository/knowledge/KnowledgeUnitOfWork.js +156 -0
  155. package/dist/lib/repository/memory/MemoryRepository.d.ts +90 -0
  156. package/dist/lib/repository/memory/MemoryRepository.js +260 -0
  157. package/dist/lib/repository/search/SearchRepoAdapter.d.ts +92 -0
  158. package/dist/lib/repository/search/SearchRepoAdapter.js +124 -0
  159. package/dist/lib/repository/session/SessionRepository.d.ts +46 -0
  160. package/dist/lib/repository/session/SessionRepository.js +110 -0
  161. package/dist/lib/repository/sourceref/RecipeSourceRefRepository.d.ts +66 -0
  162. package/dist/lib/repository/sourceref/RecipeSourceRefRepository.js +182 -0
  163. package/dist/lib/repository/sync/SyncRepoAdapter.d.ts +58 -0
  164. package/dist/lib/repository/sync/SyncRepoAdapter.js +58 -0
  165. package/dist/lib/service/bootstrap/UiStartupTasks.js +5 -6
  166. package/dist/lib/service/bootstrap/bootstrap-event-types.d.ts +0 -1
  167. package/dist/lib/service/bootstrap/bootstrap-event-types.js +0 -1
  168. package/dist/lib/service/cleanup/CleanupService.d.ts +54 -7
  169. package/dist/lib/service/cleanup/CleanupService.js +291 -40
  170. package/dist/lib/service/delivery/CursorDeliveryPipeline.js +6 -8
  171. package/dist/lib/service/evolution/ConsolidationAdvisor.d.ts +4 -9
  172. package/dist/lib/service/evolution/ConsolidationAdvisor.js +34 -70
  173. package/dist/lib/service/evolution/ContentPatcher.d.ts +4 -12
  174. package/dist/lib/service/evolution/ContentPatcher.js +48 -19
  175. package/dist/lib/service/evolution/ContradictionDetector.d.ts +3 -7
  176. package/dist/lib/service/evolution/ContradictionDetector.js +17 -24
  177. package/dist/lib/service/evolution/DecayDetector.d.ts +10 -9
  178. package/dist/lib/service/evolution/DecayDetector.js +63 -57
  179. package/dist/lib/service/evolution/EnhancementSuggester.d.ts +3 -9
  180. package/dist/lib/service/evolution/EnhancementSuggester.js +42 -86
  181. package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +4 -4
  182. package/dist/lib/service/evolution/KnowledgeMetabolism.js +102 -71
  183. package/dist/lib/service/evolution/ProposalExecutor.d.ts +5 -12
  184. package/dist/lib/service/evolution/ProposalExecutor.js +64 -69
  185. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.d.ts +9 -14
  186. package/dist/lib/service/evolution/RecipeLifecycleSupervisor.js +94 -155
  187. package/dist/lib/service/evolution/RecipeRelevanceAuditor.d.ts +4 -1
  188. package/dist/lib/service/evolution/RecipeRelevanceAuditor.js +50 -49
  189. package/dist/lib/service/evolution/RedundancyAnalyzer.d.ts +3 -7
  190. package/dist/lib/service/evolution/RedundancyAnalyzer.js +15 -22
  191. package/dist/lib/service/evolution/StagingManager.d.ts +6 -15
  192. package/dist/lib/service/evolution/StagingManager.js +37 -95
  193. package/dist/lib/service/evolution/createSupersedeProposal.d.ts +1 -1
  194. package/dist/lib/service/evolution/createSupersedeProposal.js +7 -8
  195. package/dist/lib/service/guard/CoverageAnalyzer.d.ts +3 -7
  196. package/dist/lib/service/guard/CoverageAnalyzer.js +9 -11
  197. package/dist/lib/service/guard/GuardCheckEngine.d.ts +3 -0
  198. package/dist/lib/service/guard/GuardCheckEngine.js +14 -22
  199. package/dist/lib/service/guard/ReverseGuard.d.ts +4 -7
  200. package/dist/lib/service/guard/ReverseGuard.js +21 -31
  201. package/dist/lib/service/guard/ViolationsStore.d.ts +15 -21
  202. package/dist/lib/service/guard/ViolationsStore.js +75 -69
  203. package/dist/lib/service/knowledge/CodeEntityGraph.d.ts +45 -63
  204. package/dist/lib/service/knowledge/CodeEntityGraph.js +418 -496
  205. package/dist/lib/service/knowledge/ConfidenceRouter.js +18 -9
  206. package/dist/lib/service/knowledge/KnowledgeFileWriter.d.ts +2 -1
  207. package/dist/lib/service/knowledge/KnowledgeGraphService.d.ts +18 -60
  208. package/dist/lib/service/knowledge/KnowledgeGraphService.js +58 -109
  209. package/dist/lib/service/knowledge/KnowledgeService.d.ts +15 -1
  210. package/dist/lib/service/knowledge/KnowledgeService.js +97 -46
  211. package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +0 -2
  212. package/dist/lib/service/knowledge/RecipeProductionGateway.js +0 -2
  213. package/dist/lib/service/knowledge/SourceRefReconciler.d.ts +5 -13
  214. package/dist/lib/service/knowledge/SourceRefReconciler.js +58 -78
  215. package/dist/lib/service/module/ModuleService.js +10 -19
  216. package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +14 -3
  217. package/dist/lib/service/panorama/CouplingAnalyzer.js +137 -32
  218. package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +7 -4
  219. package/dist/lib/service/panorama/DimensionAnalyzer.js +94 -33
  220. package/dist/lib/service/panorama/LayerInferrer.d.ts +16 -1
  221. package/dist/lib/service/panorama/LayerInferrer.js +118 -1
  222. package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +14 -4
  223. package/dist/lib/service/panorama/ModuleDiscoverer.js +209 -61
  224. package/dist/lib/service/panorama/PanoramaAggregator.d.ts +15 -4
  225. package/dist/lib/service/panorama/PanoramaAggregator.js +128 -62
  226. package/dist/lib/service/panorama/PanoramaScanner.d.ts +5 -1
  227. package/dist/lib/service/panorama/PanoramaScanner.js +60 -31
  228. package/dist/lib/service/panorama/PanoramaService.d.ts +11 -8
  229. package/dist/lib/service/panorama/PanoramaService.js +49 -69
  230. package/dist/lib/service/panorama/PanoramaTypes.d.ts +41 -0
  231. package/dist/lib/service/panorama/RoleRefiner.d.ts +10 -5
  232. package/dist/lib/service/panorama/RoleRefiner.js +92 -282
  233. package/dist/lib/service/panorama/TechStackProfiler.d.ts +13 -0
  234. package/dist/lib/service/panorama/TechStackProfiler.js +79 -0
  235. package/dist/lib/service/quality/QualityScorer.d.ts +45 -26
  236. package/dist/lib/service/quality/QualityScorer.js +157 -83
  237. package/dist/lib/service/search/SearchEngine.d.ts +1 -0
  238. package/dist/lib/service/search/SearchEngine.js +32 -37
  239. package/dist/lib/service/signal/HitRecorder.js +5 -5
  240. package/dist/lib/service/skills/RuleRecallStrategy.js +7 -3
  241. package/dist/lib/service/skills/SignalCollector.d.ts +6 -8
  242. package/dist/lib/service/skills/SignalCollector.js +34 -60
  243. package/dist/lib/service/skills/SkillAdvisor.d.ts +7 -13
  244. package/dist/lib/service/skills/SkillAdvisor.js +30 -79
  245. package/dist/lib/service/vector/ContextualEnricher.d.ts +1 -0
  246. package/dist/lib/service/vector/ContextualEnricher.js +4 -0
  247. package/dist/lib/service/vector/SyncCoordinator.d.ts +3 -1
  248. package/dist/lib/service/vector/SyncCoordinator.js +25 -3
  249. package/dist/lib/service/vector/VectorService.d.ts +2 -0
  250. package/dist/lib/service/vector/VectorService.js +3 -0
  251. package/dist/lib/service/wiki/WikiGenerator.js +1 -1
  252. package/dist/lib/shared/LanguageProfiles.d.ts +109 -0
  253. package/dist/lib/shared/LanguageProfiles.js +939 -0
  254. package/dist/lib/shared/LanguageService.d.ts +6 -0
  255. package/dist/lib/shared/LanguageService.js +19 -0
  256. package/dist/lib/shared/constants.d.ts +19 -19
  257. package/dist/lib/shared/constants.js +10 -10
  258. package/dist/lib/shared/developer-identity.d.ts +18 -0
  259. package/dist/lib/shared/developer-identity.js +62 -0
  260. package/dist/lib/shared/schemas/http-requests.d.ts +8 -17
  261. package/dist/lib/shared/schemas/http-requests.js +9 -6
  262. package/dist/lib/shared/schemas/mcp-tools.d.ts +1 -1
  263. package/dist/lib/types/knowledge-wire.d.ts +1 -0
  264. package/dist/lib/types/project-snapshot-builder.d.ts +0 -1
  265. package/dist/lib/types/project-snapshot-builder.js +0 -1
  266. package/dist/lib/types/project-snapshot.d.ts +0 -1
  267. package/dist/lib/types/project-snapshot.js +0 -1
  268. package/dist/lib/types/snapshot-views.d.ts +0 -2
  269. package/dist/lib/types/snapshot-views.js +0 -1
  270. package/package.json +2 -1
  271. package/dashboard/dist/assets/icons-D1aVZYFW.js +0 -1
  272. package/dashboard/dist/assets/index-CxHOu8Hd.css +0 -1
  273. package/dashboard/dist/assets/index-DDdAOpYT.js +0 -128
  274. package/dist/lib/repository/base/BaseRepository.d.ts +0 -53
  275. package/dist/lib/repository/base/BaseRepository.js +0 -226
@@ -0,0 +1,272 @@
1
+ /**
2
+ * AuditRepository — 审计日志的仓储实现
3
+ *
4
+ * 从 AuditStore 提取的数据操作,
5
+ * 使用 Drizzle 类型安全 API 操作 audit_logs 表。
6
+ */
7
+ import { and, avg, count, desc, eq, gt, gte, like, lte, sql } from 'drizzle-orm';
8
+ import { auditLogs } from '../../infrastructure/database/drizzle/schema.js';
9
+ import { RepositoryBase } from '../base/RepositoryBase.js';
10
+ /* ═══ Repository 实现 ═══ */
11
+ export class AuditRepositoryImpl extends RepositoryBase {
12
+ constructor(drizzle) {
13
+ super(drizzle, auditLogs);
14
+ }
15
+ /* ─── CRUD ─── */
16
+ async findById(id) {
17
+ const row = this.drizzle.select().from(this.table).where(eq(this.table.id, id)).limit(1).get();
18
+ return row ? this.#mapRow(row) : null;
19
+ }
20
+ async create(data) {
21
+ this.drizzle
22
+ .insert(this.table)
23
+ .values({
24
+ id: data.id,
25
+ timestamp: data.timestamp,
26
+ actor: data.actor,
27
+ actorContext: data.actorContext ?? '{}',
28
+ action: data.action,
29
+ resource: data.resource ?? null,
30
+ operationData: data.operationData ?? '{}',
31
+ result: data.result,
32
+ errorMessage: data.errorMessage ?? null,
33
+ duration: data.duration ?? null,
34
+ })
35
+ .run();
36
+ return (await this.findById(data.id));
37
+ }
38
+ async delete(id) {
39
+ const result = this.drizzle.delete(this.table).where(eq(this.table.id, id)).run();
40
+ return result.changes > 0;
41
+ }
42
+ /* ─── 查询 ─── */
43
+ /** 动态多条件查询 */
44
+ async query(filters = {}) {
45
+ const conditions = [];
46
+ if (filters.actor) {
47
+ conditions.push(eq(this.table.actor, filters.actor));
48
+ }
49
+ if (filters.action) {
50
+ conditions.push(eq(this.table.action, filters.action));
51
+ }
52
+ if (filters.result) {
53
+ conditions.push(eq(this.table.result, filters.result));
54
+ }
55
+ if (filters.startDate) {
56
+ conditions.push(gte(this.table.timestamp, filters.startDate));
57
+ }
58
+ if (filters.endDate) {
59
+ conditions.push(lte(this.table.timestamp, filters.endDate));
60
+ }
61
+ const condition = conditions.length > 0 ? and(...conditions) : undefined;
62
+ let query = this.drizzle
63
+ .select()
64
+ .from(this.table)
65
+ .where(condition)
66
+ .orderBy(desc(this.table.timestamp));
67
+ if (filters.limit) {
68
+ query = query.limit(filters.limit);
69
+ }
70
+ return query.all().map((r) => this.#mapRow(r));
71
+ }
72
+ /** 根据请求 ID 查询 */
73
+ async findByRequestId(requestId) {
74
+ return this.findById(requestId);
75
+ }
76
+ /** 根据角色查询 */
77
+ async findByActor(actor, limit = 100) {
78
+ const rows = this.drizzle
79
+ .select()
80
+ .from(this.table)
81
+ .where(eq(this.table.actor, actor))
82
+ .orderBy(desc(this.table.timestamp))
83
+ .limit(limit)
84
+ .all();
85
+ return rows.map((r) => this.#mapRow(r));
86
+ }
87
+ /** 根据操作查询 */
88
+ async findByAction(action, limit = 100) {
89
+ const rows = this.drizzle
90
+ .select()
91
+ .from(this.table)
92
+ .where(eq(this.table.action, action))
93
+ .orderBy(desc(this.table.timestamp))
94
+ .limit(limit)
95
+ .all();
96
+ return rows.map((r) => this.#mapRow(r));
97
+ }
98
+ /** 根据结果查询 */
99
+ async findByResult(result, limit = 100) {
100
+ const rows = this.drizzle
101
+ .select()
102
+ .from(this.table)
103
+ .where(eq(this.table.result, result))
104
+ .orderBy(desc(this.table.timestamp))
105
+ .limit(limit)
106
+ .all();
107
+ return rows.map((r) => this.#mapRow(r));
108
+ }
109
+ /* ─── 统计 ─── */
110
+ /** 获取统计数据 */
111
+ async getStats(timeRange = '24h') {
112
+ const hours = timeRange === '24h' ? 24 : timeRange === '7d' ? 168 : 720; // 30d
113
+ const startTime = Date.now() - hours * 60 * 60 * 1000;
114
+ const startCondition = gte(this.table.timestamp, startTime);
115
+ // 总数
116
+ const [totalRow] = this.drizzle
117
+ .select({ cnt: count() })
118
+ .from(this.table)
119
+ .where(startCondition)
120
+ .all();
121
+ const total = totalRow?.cnt ?? 0;
122
+ // 成功数
123
+ const [successRow] = this.drizzle
124
+ .select({ cnt: count() })
125
+ .from(this.table)
126
+ .where(and(startCondition, eq(this.table.result, 'success')))
127
+ .all();
128
+ const success = successRow?.cnt ?? 0;
129
+ // 失败数
130
+ const [failureRow] = this.drizzle
131
+ .select({ cnt: count() })
132
+ .from(this.table)
133
+ .where(and(startCondition, eq(this.table.result, 'failure')))
134
+ .all();
135
+ const failure = failureRow?.cnt ?? 0;
136
+ // 按角色统计
137
+ const byActor = this.drizzle
138
+ .select({
139
+ actor: this.table.actor,
140
+ count: count(),
141
+ })
142
+ .from(this.table)
143
+ .where(startCondition)
144
+ .groupBy(this.table.actor)
145
+ .orderBy(desc(count()))
146
+ .all();
147
+ // 按操作统计
148
+ const byAction = this.drizzle
149
+ .select({
150
+ action: this.table.action,
151
+ count: count(),
152
+ })
153
+ .from(this.table)
154
+ .where(startCondition)
155
+ .groupBy(this.table.action)
156
+ .orderBy(desc(count()))
157
+ .all();
158
+ // 平均响应时间
159
+ const [avgRow] = this.drizzle
160
+ .select({
161
+ avgDuration: avg(this.table.duration),
162
+ })
163
+ .from(this.table)
164
+ .where(and(startCondition, sql `${this.table.duration} IS NOT NULL`))
165
+ .all();
166
+ const avgDuration = avgRow?.avgDuration ? `${Math.round(Number(avgRow.avgDuration))}ms` : 'N/A';
167
+ return {
168
+ timeRange,
169
+ total,
170
+ success,
171
+ failure,
172
+ successRate: total > 0 ? `${((success / total) * 100).toFixed(2)}%` : '0%',
173
+ avgDuration,
174
+ byActor,
175
+ byAction,
176
+ };
177
+ }
178
+ /* ─── 清理 ─── */
179
+ /**
180
+ * 清理过期审计日志
181
+ * @param maxAgeDays 保留天数
182
+ */
183
+ async cleanup(maxAgeDays = 90) {
184
+ try {
185
+ const cutoff = Date.now() - maxAgeDays * 86400000;
186
+ const result = this.drizzle
187
+ .delete(this.table)
188
+ .where(sql `${this.table.timestamp} < ${cutoff}`)
189
+ .run();
190
+ return { deleted: result.changes ?? 0 };
191
+ }
192
+ catch {
193
+ return { deleted: 0 };
194
+ }
195
+ }
196
+ /**
197
+ * Guard 违规规则名 TOP-N (SkillAdvisor.#getGuardPatterns)
198
+ */
199
+ async findTopGuardViolationRules(minCount, limit) {
200
+ return this.drizzle
201
+ .select({
202
+ ruleName: sql `json_extract(${this.table.operationData}, '$.ruleName')`.as('ruleName'),
203
+ cnt: count(),
204
+ })
205
+ .from(this.table)
206
+ .where(and(like(this.table.action, 'guard%'), eq(this.table.result, 'violation')))
207
+ .groupBy(sql `json_extract(${this.table.operationData}, '$.ruleName')`)
208
+ .having(sql `count(*) >= ${minCount}`)
209
+ .orderBy(desc(count()))
210
+ .limit(limit)
211
+ .all();
212
+ }
213
+ /**
214
+ * Guard 违规信号 (SignalCollector.#collectGuardSignals)
215
+ */
216
+ async findGuardViolationSignals(limit) {
217
+ return this.drizzle
218
+ .select({
219
+ ruleName: sql `json_extract(${this.table.operationData}, '$.ruleName')`.as('ruleName'),
220
+ cnt: count(),
221
+ lastAt: sql `MAX(${this.table.timestamp})`.as('lastAt'),
222
+ })
223
+ .from(this.table)
224
+ .where(and(like(this.table.action, 'guard%'), eq(this.table.result, 'violation')))
225
+ .groupBy(sql `json_extract(${this.table.operationData}, '$.ruleName')`)
226
+ .having(sql `count(*) > 0`)
227
+ .orderBy(desc(count()))
228
+ .limit(limit)
229
+ .all();
230
+ }
231
+ /**
232
+ * 最近动作日志 (SignalCollector.#collectActionSignals)
233
+ */
234
+ async findRecentActions(sinceTs, limit) {
235
+ return this.drizzle
236
+ .select({
237
+ actor: this.table.actor,
238
+ action: this.table.action,
239
+ resource: this.table.resource,
240
+ result: this.table.result,
241
+ timestamp: this.table.timestamp,
242
+ })
243
+ .from(this.table)
244
+ .where(gt(this.table.timestamp, sinceTs))
245
+ .orderBy(desc(this.table.timestamp))
246
+ .limit(limit)
247
+ .all();
248
+ }
249
+ /* ─── 内部辅助 ─── */
250
+ #mapRow(row) {
251
+ return {
252
+ id: row.id,
253
+ timestamp: row.timestamp,
254
+ actor: row.actor,
255
+ actorContext: safeParseJSON(row.actorContext, {}),
256
+ action: row.action,
257
+ resource: row.resource ?? null,
258
+ operationData: safeParseJSON(row.operationData, {}),
259
+ result: row.result,
260
+ errorMessage: row.errorMessage ?? null,
261
+ duration: row.duration ?? null,
262
+ };
263
+ }
264
+ }
265
+ function safeParseJSON(str, fallback) {
266
+ try {
267
+ return str ? JSON.parse(str) : fallback;
268
+ }
269
+ catch {
270
+ return fallback;
271
+ }
272
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * RepositoryBase — Drizzle-first 仓储基类
3
+ *
4
+ * 与旧 BaseRepository 的区别:
5
+ * - 构造器接收 DrizzleDB 而非 raw Database
6
+ * - 子类应使用 Drizzle 类型安全 API 实现 CRUD
7
+ * - 保留 rawQuery() 作为复杂查询逃生舱
8
+ * - 无 _assertSafeColumn() —— Drizzle 自带列类型约束
9
+ */
10
+ import type { SQLiteTable } from 'drizzle-orm/sqlite-core';
11
+ import type { Logger as WinstonLogger } from 'winston';
12
+ import type { DrizzleDB } from '../../infrastructure/database/drizzle/index.js';
13
+ /** Drizzle 事务类型 */
14
+ export type DrizzleTx = Parameters<Parameters<DrizzleDB['transaction']>[0]>[0];
15
+ export interface PaginationOptions {
16
+ page?: number;
17
+ pageSize?: number;
18
+ }
19
+ export interface PaginatedResult<T> {
20
+ data: T[];
21
+ pagination: {
22
+ page: number;
23
+ pageSize: number;
24
+ total: number;
25
+ pages: number;
26
+ };
27
+ }
28
+ /**
29
+ * 新基类:以 Drizzle typed API 为主,raw SQL 为逃生舱。
30
+ *
31
+ * @typeParam TTable Drizzle 表定义(如 typeof knowledgeEdges)
32
+ * @typeParam TEntity 领域实体类型
33
+ */
34
+ export declare abstract class RepositoryBase<TTable extends SQLiteTable, TEntity> {
35
+ protected readonly drizzle: DrizzleDB;
36
+ protected readonly table: TTable;
37
+ protected readonly logger: WinstonLogger;
38
+ constructor(drizzle: DrizzleDB, table: TTable);
39
+ /**
40
+ * Drizzle 事务包装 — 所有 DB 变更意图应在事务内执行
41
+ */
42
+ protected transaction<R>(fn: (tx: DrizzleTx) => R): R;
43
+ abstract findById(id: string | number): Promise<TEntity | null>;
44
+ abstract create(data: unknown): Promise<TEntity>;
45
+ abstract delete(id: string | number): Promise<boolean>;
46
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * RepositoryBase — Drizzle-first 仓储基类
3
+ *
4
+ * 与旧 BaseRepository 的区别:
5
+ * - 构造器接收 DrizzleDB 而非 raw Database
6
+ * - 子类应使用 Drizzle 类型安全 API 实现 CRUD
7
+ * - 保留 rawQuery() 作为复杂查询逃生舱
8
+ * - 无 _assertSafeColumn() —— Drizzle 自带列类型约束
9
+ */
10
+ import Logger from '../../infrastructure/logging/Logger.js';
11
+ /**
12
+ * 新基类:以 Drizzle typed API 为主,raw SQL 为逃生舱。
13
+ *
14
+ * @typeParam TTable Drizzle 表定义(如 typeof knowledgeEdges)
15
+ * @typeParam TEntity 领域实体类型
16
+ */
17
+ export class RepositoryBase {
18
+ drizzle;
19
+ table;
20
+ logger;
21
+ constructor(drizzle, table) {
22
+ this.drizzle = drizzle;
23
+ this.table = table;
24
+ this.logger = Logger.getInstance();
25
+ }
26
+ /**
27
+ * Drizzle 事务包装 — 所有 DB 变更意图应在事务内执行
28
+ */
29
+ transaction(fn) {
30
+ return this.drizzle.transaction(fn);
31
+ }
32
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * BootstrapRepository — Bootstrap 快照的仓储实现
3
+ *
4
+ * 从 BootstrapSnapshot 提取的数据操作,
5
+ * 使用 Drizzle 类型安全 API 操作 bootstrap_snapshots + bootstrap_dim_files 表。
6
+ */
7
+ import { bootstrapSnapshots } from '../../infrastructure/database/drizzle/schema.js';
8
+ import { RepositoryBase } from '../base/RepositoryBase.js';
9
+ export interface BootstrapSnapshotEntity {
10
+ id: string;
11
+ sessionId: string | null;
12
+ projectRoot: string;
13
+ createdAt: string;
14
+ durationMs: number;
15
+ fileCount: number;
16
+ dimensionCount: number;
17
+ candidateCount: number;
18
+ primaryLang: string | null;
19
+ fileHashes: Record<string, string>;
20
+ dimensionMeta: Record<string, DimensionStatMeta>;
21
+ episodicData: Record<string, unknown> | null;
22
+ isIncremental: boolean;
23
+ parentId: string | null;
24
+ changedFiles: string[];
25
+ affectedDims: string[];
26
+ status: string;
27
+ }
28
+ export interface DimensionStatMeta {
29
+ candidateCount: number;
30
+ analysisChars: number;
31
+ referencedFiles: number;
32
+ durationMs: number;
33
+ }
34
+ export interface BootstrapSnapshotInsert {
35
+ id: string;
36
+ sessionId?: string | null;
37
+ projectRoot: string;
38
+ createdAt: string;
39
+ durationMs?: number;
40
+ fileCount?: number;
41
+ dimensionCount?: number;
42
+ candidateCount?: number;
43
+ primaryLang?: string | null;
44
+ fileHashes: Record<string, string>;
45
+ dimensionMeta: Record<string, DimensionStatMeta>;
46
+ episodicData?: unknown | null;
47
+ isIncremental?: boolean;
48
+ parentId?: string | null;
49
+ changedFiles?: string[];
50
+ affectedDims?: string[];
51
+ status?: string;
52
+ }
53
+ export interface DimFileInsert {
54
+ snapshotId: string;
55
+ dimId: string;
56
+ filePath: string;
57
+ role?: string;
58
+ }
59
+ export interface DimFileEntry {
60
+ dimId: string;
61
+ filePath: string;
62
+ }
63
+ export declare class BootstrapRepositoryImpl extends RepositoryBase<typeof bootstrapSnapshots, BootstrapSnapshotEntity> {
64
+ #private;
65
+ /** 默认快照保留数量 */
66
+ static readonly MAX_SNAPSHOTS = 5;
67
+ constructor(drizzle: ConstructorParameters<typeof RepositoryBase<typeof bootstrapSnapshots, BootstrapSnapshotEntity>>[0]);
68
+ findById(id: string): Promise<BootstrapSnapshotEntity | null>;
69
+ create(data: BootstrapSnapshotInsert): Promise<BootstrapSnapshotEntity>;
70
+ delete(id: string): Promise<boolean>;
71
+ /** 获取项目最新完成的快照 */
72
+ getLatest(projectRoot: string): Promise<BootstrapSnapshotEntity | null>;
73
+ /** 获取项目的快照列表 (按时间降序) */
74
+ listByProject(projectRoot: string, limit?: number): Promise<BootstrapSnapshotEntity[]>;
75
+ /** 批量插入维度-文件关联 (INSERT OR IGNORE) */
76
+ saveDimFiles(entries: DimFileInsert[]): Promise<number>;
77
+ /** 获取快照的维度-文件关联 */
78
+ getDimFiles(snapshotId: string): Promise<DimFileEntry[]>;
79
+ /** 获取快照中每个维度引用的文件集合 */
80
+ getDimFileMap(snapshotId: string): Promise<Record<string, Set<string>>>;
81
+ /** 保留项目最新 N 个快照,删除旧的 */
82
+ enforceCapacity(projectRoot: string, maxSnapshots?: number): Promise<number>;
83
+ /** 清除项目的所有快照 */
84
+ clearProject(projectRoot: string): Promise<number>;
85
+ /**
86
+ * 事务保存快照 + 维度-文件关联 + 容量控制
87
+ * 替代 BootstrapSnapshot.save() 中的事务逻辑
88
+ */
89
+ saveWithDimFiles(snapshot: BootstrapSnapshotInsert, dimFiles: DimFileInsert[]): Promise<BootstrapSnapshotEntity>;
90
+ /** 获取项目最新的主语言 (Panorama 域用于维度/角色检测) */
91
+ getLatestPrimaryLang(projectRoot: string): Promise<string | null>;
92
+ /** 获取快照总数 */
93
+ getSnapshotCount(projectRoot?: string): Promise<number>;
94
+ }
@@ -0,0 +1,246 @@
1
+ /**
2
+ * BootstrapRepository — Bootstrap 快照的仓储实现
3
+ *
4
+ * 从 BootstrapSnapshot 提取的数据操作,
5
+ * 使用 Drizzle 类型安全 API 操作 bootstrap_snapshots + bootstrap_dim_files 表。
6
+ */
7
+ import { and, count, desc, eq, sql } from 'drizzle-orm';
8
+ import { bootstrapDimFiles, bootstrapSnapshots, } from '../../infrastructure/database/drizzle/schema.js';
9
+ import { RepositoryBase } from '../base/RepositoryBase.js';
10
+ /* ═══ Repository 实现 ═══ */
11
+ export class BootstrapRepositoryImpl extends RepositoryBase {
12
+ /** 默认快照保留数量 */
13
+ static MAX_SNAPSHOTS = 5;
14
+ constructor(drizzle) {
15
+ super(drizzle, bootstrapSnapshots);
16
+ }
17
+ /* ─── CRUD ─── */
18
+ async findById(id) {
19
+ const rows = this.drizzle.select().from(this.table).where(eq(this.table.id, id)).limit(1).all();
20
+ return rows.length > 0 ? this.#mapRow(rows[0]) : null;
21
+ }
22
+ async create(data) {
23
+ this.drizzle
24
+ .insert(this.table)
25
+ .values({
26
+ id: data.id,
27
+ sessionId: data.sessionId ?? null,
28
+ projectRoot: data.projectRoot,
29
+ createdAt: data.createdAt,
30
+ durationMs: data.durationMs ?? 0,
31
+ fileCount: data.fileCount ?? 0,
32
+ dimensionCount: data.dimensionCount ?? 0,
33
+ candidateCount: data.candidateCount ?? 0,
34
+ primaryLang: data.primaryLang ?? null,
35
+ fileHashes: JSON.stringify(data.fileHashes),
36
+ dimensionMeta: JSON.stringify(data.dimensionMeta),
37
+ episodicData: data.episodicData ? JSON.stringify(data.episodicData) : null,
38
+ isIncremental: data.isIncremental ? 1 : 0,
39
+ parentId: data.parentId ?? null,
40
+ changedFiles: JSON.stringify(data.changedFiles ?? []),
41
+ affectedDims: JSON.stringify(data.affectedDims ?? []),
42
+ status: data.status ?? 'complete',
43
+ })
44
+ .run();
45
+ return (await this.findById(data.id));
46
+ }
47
+ async delete(id) {
48
+ const result = this.drizzle.delete(this.table).where(eq(this.table.id, id)).run();
49
+ return result.changes > 0;
50
+ }
51
+ /* ─── 快照查询 ─── */
52
+ /** 获取项目最新完成的快照 */
53
+ async getLatest(projectRoot) {
54
+ const rows = this.drizzle
55
+ .select()
56
+ .from(this.table)
57
+ .where(and(eq(this.table.projectRoot, projectRoot), eq(this.table.status, 'complete')))
58
+ .orderBy(desc(this.table.createdAt))
59
+ .limit(1)
60
+ .all();
61
+ return rows.length > 0 ? this.#mapRow(rows[0]) : null;
62
+ }
63
+ /** 获取项目的快照列表 (按时间降序) */
64
+ async listByProject(projectRoot, limit = 10) {
65
+ const rows = this.drizzle
66
+ .select()
67
+ .from(this.table)
68
+ .where(eq(this.table.projectRoot, projectRoot))
69
+ .orderBy(desc(this.table.createdAt))
70
+ .limit(limit)
71
+ .all();
72
+ return rows.map((r) => this.#mapRow(r));
73
+ }
74
+ /* ─── 维度-文件关联 ─── */
75
+ /** 批量插入维度-文件关联 (INSERT OR IGNORE) */
76
+ async saveDimFiles(entries) {
77
+ if (entries.length === 0) {
78
+ return 0;
79
+ }
80
+ let inserted = 0;
81
+ this.transaction((tx) => {
82
+ for (const entry of entries) {
83
+ tx.insert(bootstrapDimFiles)
84
+ .values({
85
+ snapshotId: entry.snapshotId,
86
+ dimId: entry.dimId,
87
+ filePath: entry.filePath,
88
+ role: entry.role ?? 'referenced',
89
+ })
90
+ .onConflictDoNothing()
91
+ .run();
92
+ inserted++;
93
+ }
94
+ });
95
+ return inserted;
96
+ }
97
+ /** 获取快照的维度-文件关联 */
98
+ async getDimFiles(snapshotId) {
99
+ const rows = this.drizzle
100
+ .select({
101
+ dimId: bootstrapDimFiles.dimId,
102
+ filePath: bootstrapDimFiles.filePath,
103
+ })
104
+ .from(bootstrapDimFiles)
105
+ .where(eq(bootstrapDimFiles.snapshotId, snapshotId))
106
+ .all();
107
+ return rows;
108
+ }
109
+ /** 获取快照中每个维度引用的文件集合 */
110
+ async getDimFileMap(snapshotId) {
111
+ const entries = await this.getDimFiles(snapshotId);
112
+ const map = {};
113
+ for (const row of entries) {
114
+ if (!map[row.dimId]) {
115
+ map[row.dimId] = new Set();
116
+ }
117
+ map[row.dimId].add(row.filePath);
118
+ }
119
+ return map;
120
+ }
121
+ /* ─── 容量控制 ─── */
122
+ /** 保留项目最新 N 个快照,删除旧的 */
123
+ async enforceCapacity(projectRoot, maxSnapshots = BootstrapRepositoryImpl.MAX_SNAPSHOTS) {
124
+ const result = this.drizzle
125
+ .delete(this.table)
126
+ .where(and(eq(this.table.projectRoot, projectRoot), sql `${this.table.id} NOT IN (
127
+ SELECT ${this.table.id} FROM ${this.table}
128
+ WHERE ${this.table.projectRoot} = ${projectRoot}
129
+ ORDER BY ${this.table.createdAt} DESC
130
+ LIMIT ${maxSnapshots}
131
+ )`))
132
+ .run();
133
+ return result.changes;
134
+ }
135
+ /** 清除项目的所有快照 */
136
+ async clearProject(projectRoot) {
137
+ const snapshots = await this.listByProject(projectRoot, 9999);
138
+ let deleted = 0;
139
+ for (const snap of snapshots) {
140
+ if (await this.delete(snap.id)) {
141
+ deleted++;
142
+ }
143
+ }
144
+ return deleted;
145
+ }
146
+ /* ─── 事务保存 ─── */
147
+ /**
148
+ * 事务保存快照 + 维度-文件关联 + 容量控制
149
+ * 替代 BootstrapSnapshot.save() 中的事务逻辑
150
+ */
151
+ async saveWithDimFiles(snapshot, dimFiles) {
152
+ this.transaction((tx) => {
153
+ // 主记录
154
+ tx.insert(this.table)
155
+ .values({
156
+ id: snapshot.id,
157
+ sessionId: snapshot.sessionId ?? null,
158
+ projectRoot: snapshot.projectRoot,
159
+ createdAt: snapshot.createdAt,
160
+ durationMs: snapshot.durationMs ?? 0,
161
+ fileCount: snapshot.fileCount ?? 0,
162
+ dimensionCount: snapshot.dimensionCount ?? 0,
163
+ candidateCount: snapshot.candidateCount ?? 0,
164
+ primaryLang: snapshot.primaryLang ?? null,
165
+ fileHashes: JSON.stringify(snapshot.fileHashes),
166
+ dimensionMeta: JSON.stringify(snapshot.dimensionMeta),
167
+ episodicData: snapshot.episodicData ? JSON.stringify(snapshot.episodicData) : null,
168
+ isIncremental: snapshot.isIncremental ? 1 : 0,
169
+ parentId: snapshot.parentId ?? null,
170
+ changedFiles: JSON.stringify(snapshot.changedFiles ?? []),
171
+ affectedDims: JSON.stringify(snapshot.affectedDims ?? []),
172
+ status: snapshot.status ?? 'complete',
173
+ })
174
+ .run();
175
+ // 维度-文件关联
176
+ for (const df of dimFiles) {
177
+ tx.insert(bootstrapDimFiles)
178
+ .values({
179
+ snapshotId: df.snapshotId,
180
+ dimId: df.dimId,
181
+ filePath: df.filePath,
182
+ role: df.role ?? 'referenced',
183
+ })
184
+ .onConflictDoNothing()
185
+ .run();
186
+ }
187
+ // 容量控制
188
+ tx.delete(this.table)
189
+ .where(and(eq(this.table.projectRoot, snapshot.projectRoot), sql `${this.table.id} NOT IN (
190
+ SELECT ${this.table.id} FROM ${this.table}
191
+ WHERE ${this.table.projectRoot} = ${snapshot.projectRoot}
192
+ ORDER BY ${this.table.createdAt} DESC
193
+ LIMIT ${BootstrapRepositoryImpl.MAX_SNAPSHOTS}
194
+ )`))
195
+ .run();
196
+ });
197
+ return (await this.findById(snapshot.id));
198
+ }
199
+ /** 获取项目最新的主语言 (Panorama 域用于维度/角色检测) */
200
+ async getLatestPrimaryLang(projectRoot) {
201
+ const rows = this.drizzle
202
+ .select({ primaryLang: this.table.primaryLang })
203
+ .from(this.table)
204
+ .where(eq(this.table.projectRoot, projectRoot))
205
+ .orderBy(desc(this.table.createdAt))
206
+ .limit(1)
207
+ .all();
208
+ return rows.length > 0 ? (rows[0].primaryLang ?? null) : null;
209
+ }
210
+ /** 获取快照总数 */
211
+ async getSnapshotCount(projectRoot) {
212
+ const condition = projectRoot ? eq(this.table.projectRoot, projectRoot) : undefined;
213
+ const [row] = this.drizzle.select({ cnt: count() }).from(this.table).where(condition).all();
214
+ return row?.cnt ?? 0;
215
+ }
216
+ /* ─── 内部辅助 ─── */
217
+ #mapRow(row) {
218
+ return {
219
+ id: row.id,
220
+ sessionId: row.sessionId ?? null,
221
+ projectRoot: row.projectRoot,
222
+ createdAt: row.createdAt,
223
+ durationMs: row.durationMs ?? 0,
224
+ fileCount: row.fileCount ?? 0,
225
+ dimensionCount: row.dimensionCount ?? 0,
226
+ candidateCount: row.candidateCount ?? 0,
227
+ primaryLang: row.primaryLang ?? null,
228
+ fileHashes: safeParseJSON(row.fileHashes, {}),
229
+ dimensionMeta: safeParseJSON(row.dimensionMeta, {}),
230
+ episodicData: safeParseJSON(row.episodicData, null),
231
+ isIncremental: !!row.isIncremental,
232
+ parentId: row.parentId ?? null,
233
+ changedFiles: safeParseJSON(row.changedFiles, []),
234
+ affectedDims: safeParseJSON(row.affectedDims, []),
235
+ status: row.status ?? 'complete',
236
+ };
237
+ }
238
+ }
239
+ function safeParseJSON(str, fallback) {
240
+ try {
241
+ return str ? JSON.parse(str) : fallback;
242
+ }
243
+ catch {
244
+ return fallback;
245
+ }
246
+ }