@jingyi0605/codingns 0.8.5 → 0.9.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 (439) hide show
  1. package/bin/codingns.mjs +7 -156
  2. package/dist/public/assets/AdaptiveButlerPage-B17QiMyT.js +2 -0
  3. package/dist/public/assets/{App-BOHBGFOd.js → App-CFBwDUNA.js} +6 -6
  4. package/dist/public/assets/{BootstrapPage-BxHQT4nA.js → BootstrapPage-W5wU3BPh.js} +1 -1
  5. package/dist/public/assets/{ConversationPage-DWFsF6BB.js → ConversationPage-DQLX1bUh.js} +6 -6
  6. package/dist/public/assets/{DesktopDetachPreviewPage-DOgEjYEf.js → DesktopDetachPreviewPage-DTPeuAW-.js} +1 -1
  7. package/dist/public/assets/DesktopModal-6ii53_Y9.js +1 -0
  8. package/dist/public/assets/DesktopWindowPage-D0blSuKd.js +2 -0
  9. package/dist/public/assets/FileContextPanel-BrKO8Xt6.js +1 -0
  10. package/dist/public/assets/GitSidebar-BdwiDtOr.js +6 -0
  11. package/dist/public/assets/MobileCreateSessionSheet-Cx_dBiBb.js +1 -0
  12. package/dist/public/assets/MobileSheet-opTWyRe1.js +1 -0
  13. package/dist/public/assets/{MobileTopHeaderFrame-lcp2GscV.js → MobileTopHeaderFrame-BbNON3Y4.js} +1 -1
  14. package/dist/public/assets/MobileWorkspaceSwitcherHeader-BZEzPeMj.js +1 -0
  15. package/dist/public/assets/{PluginAccessOverview-DGcKAMQl.js → PluginAccessOverview-mQDmAljp.js} +1 -1
  16. package/dist/public/assets/PluginContainerPage-CcxUJpM4.js +1 -0
  17. package/dist/public/assets/{PluginDetailPage-CAJ7LFpD.js → PluginDetailPage-D5--ACIt.js} +1 -1
  18. package/dist/public/assets/{PluginsListPage-BxZG1NyT.js → PluginsListPage-D_oJxYXT.js} +1 -1
  19. package/dist/public/assets/{RelayConnectEntryPage-CfNO_TIl.js → RelayConnectEntryPage-DROxpnkv.js} +1 -1
  20. package/dist/public/assets/{ServerSettingsModal-by36Z_5k.js → ServerSettingsModal-CUUOPqSe.js} +1 -1
  21. package/dist/public/assets/SessionIndexPage-C2Jxh6Gp.js +1 -0
  22. package/dist/public/assets/SettingsPage-BlAZCHsy.js +2 -0
  23. package/dist/public/assets/{TerminalManagerPanel-NVZRxxmH.js → TerminalManagerPanel-CjzbiWjl.js} +1 -1
  24. package/dist/public/assets/{TerminalPage-C4LNoPBp.js → TerminalPage-CwWyFDj8.js} +2 -2
  25. package/dist/public/assets/TerminalRuntimeFallbackModal-CSVVbO8r.js +1 -0
  26. package/dist/public/assets/{ToolFilesPage-47zbdgTW.js → ToolFilesPage-QBEY8oCf.js} +1 -1
  27. package/dist/public/assets/{ToolGitPage-Fuk_b_jg.js → ToolGitPage-BKoZ2l9v.js} +1 -1
  28. package/dist/public/assets/{ToolProcessesPage-sWSMWD-9.js → ToolProcessesPage-BOH0ib4G.js} +1 -1
  29. package/dist/public/assets/{ToolsHomePage-R1mZlbZi.js → ToolsHomePage-BcMZ3BCQ.js} +1 -1
  30. package/dist/public/assets/{WorkbenchLandingPage-CqmiFH2u.js → WorkbenchLandingPage-B5zoppEl.js} +1 -1
  31. package/dist/public/assets/WorkbenchLayout-BksVkkFF.css +1 -0
  32. package/dist/public/assets/WorkbenchLayout-CikJBS62.js +1019 -0
  33. package/dist/public/assets/{WorkbenchModal-C7qoQElW.js → WorkbenchModal-NGmPgqaE.js} +1 -1
  34. package/dist/public/assets/WorkbenchShellRoute-BbbSOiZw.js +1 -0
  35. package/dist/public/assets/WorkbenchShellRoute-DT3VMjWD.css +1 -0
  36. package/dist/public/assets/WorkspaceDebugDetailPage-CVivdPx5.js +1 -0
  37. package/dist/public/assets/{WorkspaceDetailPage-BPsrFffw.js → WorkspaceDetailPage-DgOSjscR.js} +1 -1
  38. package/dist/public/assets/{WorkspaceHomePage-KAtqZOAb.js → WorkspaceHomePage-HPa7M_Vh.js} +1 -1
  39. package/dist/public/assets/{client-runtime-manager-wmCJZKYd.js → client-runtime-manager-DXbI9K1K.js} +1 -1
  40. package/dist/public/assets/index-BxJPQpFM.css +1 -0
  41. package/dist/public/assets/index-CeXGOT_T.js +50 -0
  42. package/dist/public/assets/{login-direct-candidate-resolver-BOAgTuUf.js → login-direct-candidate-resolver-DkKyFtQJ.js} +1 -1
  43. package/dist/public/assets/{plugin-permission-copy-Cq99cnzV.js → plugin-permission-copy-CzN269Bk.js} +1 -1
  44. package/dist/public/assets/{plugins-api-BQTV5DOp.js → plugins-api-Bv9DHpLF.js} +1 -1
  45. package/dist/public/assets/{preferences-service-DJxbEEeg.js → preferences-service-D2ISL2Zz.js} +1 -1
  46. package/dist/public/assets/{relay-entry-D-LfvdiX.js → relay-entry-Bg0OisQy.js} +1 -1
  47. package/dist/public/assets/{terminal-runtime-meta-BJmy8dyK.js → terminal-runtime-meta-C8t-CIDF.js} +1 -1
  48. package/dist/public/assets/{useRegisteredDebugTemplates-DQAWVdCo.js → useRegisteredDebugTemplates-Bol3NVfN.js} +1 -1
  49. package/dist/public/assets/{workbench-navigation-MEzCSmsK.js → workbench-navigation-B7IjRQd8.js} +1 -1
  50. package/dist/public/index.html +2 -2
  51. package/dist/server/middlewares/auth-guard.js +10 -5
  52. package/dist/server/middlewares/auth-guard.js.map +1 -1
  53. package/dist/server/modules/affairs-indexer/contracts/src/errors/app-error.d.ts +11 -0
  54. package/dist/server/modules/affairs-indexer/contracts/src/errors/app-error.js +22 -0
  55. package/dist/server/modules/affairs-indexer/contracts/src/errors/app-error.js.map +1 -0
  56. package/dist/server/modules/affairs-indexer/contracts/src/errors/error-codes.d.ts +23 -0
  57. package/dist/server/modules/affairs-indexer/contracts/src/errors/error-codes.js +23 -0
  58. package/dist/server/modules/affairs-indexer/contracts/src/errors/error-codes.js.map +1 -0
  59. package/dist/server/modules/affairs-indexer/contracts/src/index.d.ts +4 -0
  60. package/dist/server/modules/affairs-indexer/contracts/src/index.js +5 -0
  61. package/dist/server/modules/affairs-indexer/contracts/src/index.js.map +1 -0
  62. package/dist/server/modules/affairs-indexer/contracts/src/types/cli-command-context.d.ts +7 -0
  63. package/dist/server/modules/affairs-indexer/contracts/src/types/cli-command-context.js +2 -0
  64. package/dist/server/modules/affairs-indexer/contracts/src/types/cli-command-context.js.map +1 -0
  65. package/dist/server/modules/affairs-indexer/contracts/src/types/runtime-config.d.ts +16 -0
  66. package/dist/server/modules/affairs-indexer/contracts/src/types/runtime-config.js +2 -0
  67. package/dist/server/modules/affairs-indexer/contracts/src/types/runtime-config.js.map +1 -0
  68. package/dist/server/modules/affairs-indexer/core/src/config/load-runtime-config.d.ts +10 -0
  69. package/dist/server/modules/affairs-indexer/core/src/config/load-runtime-config.js +215 -0
  70. package/dist/server/modules/affairs-indexer/core/src/config/load-runtime-config.js.map +1 -0
  71. package/dist/server/modules/affairs-indexer/core/src/index.d.ts +31 -0
  72. package/dist/server/modules/affairs-indexer/core/src/index.js +32 -0
  73. package/dist/server/modules/affairs-indexer/core/src/index.js.map +1 -0
  74. package/dist/server/modules/affairs-indexer/core/src/logging/structured-logger.d.ts +20 -0
  75. package/dist/server/modules/affairs-indexer/core/src/logging/structured-logger.js +47 -0
  76. package/dist/server/modules/affairs-indexer/core/src/logging/structured-logger.js.map +1 -0
  77. package/dist/server/modules/affairs-indexer/core/src/parser/base-complex-parser-adapter.d.ts +17 -0
  78. package/dist/server/modules/affairs-indexer/core/src/parser/base-complex-parser-adapter.js +63 -0
  79. package/dist/server/modules/affairs-indexer/core/src/parser/base-complex-parser-adapter.js.map +1 -0
  80. package/dist/server/modules/affairs-indexer/core/src/parser/complex-document-skip-adapter.d.ts +12 -0
  81. package/dist/server/modules/affairs-indexer/core/src/parser/complex-document-skip-adapter.js +42 -0
  82. package/dist/server/modules/affairs-indexer/core/src/parser/complex-document-skip-adapter.js.map +1 -0
  83. package/dist/server/modules/affairs-indexer/core/src/parser/csv-parser-adapter.d.ts +7 -0
  84. package/dist/server/modules/affairs-indexer/core/src/parser/csv-parser-adapter.js +107 -0
  85. package/dist/server/modules/affairs-indexer/core/src/parser/csv-parser-adapter.js.map +1 -0
  86. package/dist/server/modules/affairs-indexer/core/src/parser/document-parser.d.ts +19 -0
  87. package/dist/server/modules/affairs-indexer/core/src/parser/document-parser.js +37 -0
  88. package/dist/server/modules/affairs-indexer/core/src/parser/document-parser.js.map +1 -0
  89. package/dist/server/modules/affairs-indexer/core/src/parser/docx-parser-adapter.d.ts +7 -0
  90. package/dist/server/modules/affairs-indexer/core/src/parser/docx-parser-adapter.js +123 -0
  91. package/dist/server/modules/affairs-indexer/core/src/parser/docx-parser-adapter.js.map +1 -0
  92. package/dist/server/modules/affairs-indexer/core/src/parser/openxml-utils.d.ts +8 -0
  93. package/dist/server/modules/affairs-indexer/core/src/parser/openxml-utils.js +111 -0
  94. package/dist/server/modules/affairs-indexer/core/src/parser/openxml-utils.js.map +1 -0
  95. package/dist/server/modules/affairs-indexer/core/src/parser/parser-adapter.d.ts +42 -0
  96. package/dist/server/modules/affairs-indexer/core/src/parser/parser-adapter.js +2 -0
  97. package/dist/server/modules/affairs-indexer/core/src/parser/parser-adapter.js.map +1 -0
  98. package/dist/server/modules/affairs-indexer/core/src/parser/parser-capability-registry.d.ts +18 -0
  99. package/dist/server/modules/affairs-indexer/core/src/parser/parser-capability-registry.js +91 -0
  100. package/dist/server/modules/affairs-indexer/core/src/parser/parser-capability-registry.js.map +1 -0
  101. package/dist/server/modules/affairs-indexer/core/src/parser/parser-router.d.ts +18 -0
  102. package/dist/server/modules/affairs-indexer/core/src/parser/parser-router.js +59 -0
  103. package/dist/server/modules/affairs-indexer/core/src/parser/parser-router.js.map +1 -0
  104. package/dist/server/modules/affairs-indexer/core/src/parser/parser-skip-repository.d.ts +48 -0
  105. package/dist/server/modules/affairs-indexer/core/src/parser/parser-skip-repository.js +193 -0
  106. package/dist/server/modules/affairs-indexer/core/src/parser/parser-skip-repository.js.map +1 -0
  107. package/dist/server/modules/affairs-indexer/core/src/parser/pdf-parser-adapter.d.ts +7 -0
  108. package/dist/server/modules/affairs-indexer/core/src/parser/pdf-parser-adapter.js +371 -0
  109. package/dist/server/modules/affairs-indexer/core/src/parser/pdf-parser-adapter.js.map +1 -0
  110. package/dist/server/modules/affairs-indexer/core/src/parser/plain-text-parser-adapter.d.ts +10 -0
  111. package/dist/server/modules/affairs-indexer/core/src/parser/plain-text-parser-adapter.js +55 -0
  112. package/dist/server/modules/affairs-indexer/core/src/parser/plain-text-parser-adapter.js.map +1 -0
  113. package/dist/server/modules/affairs-indexer/core/src/parser/plain-text-parser.d.ts +9 -0
  114. package/dist/server/modules/affairs-indexer/core/src/parser/plain-text-parser.js +2 -0
  115. package/dist/server/modules/affairs-indexer/core/src/parser/plain-text-parser.js.map +1 -0
  116. package/dist/server/modules/affairs-indexer/core/src/parser/pptx-parser-adapter.d.ts +7 -0
  117. package/dist/server/modules/affairs-indexer/core/src/parser/pptx-parser-adapter.js +130 -0
  118. package/dist/server/modules/affairs-indexer/core/src/parser/pptx-parser-adapter.js.map +1 -0
  119. package/dist/server/modules/affairs-indexer/core/src/parser/xlsx-parser-adapter.d.ts +7 -0
  120. package/dist/server/modules/affairs-indexer/core/src/parser/xlsx-parser-adapter.js +228 -0
  121. package/dist/server/modules/affairs-indexer/core/src/parser/xlsx-parser-adapter.js.map +1 -0
  122. package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-repository.d.ts +205 -0
  123. package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-repository.js +1471 -0
  124. package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-repository.js.map +1 -0
  125. package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-write-repository.d.ts +161 -0
  126. package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-write-repository.js +1350 -0
  127. package/dist/server/modules/affairs-indexer/core/src/repositories/catalog-write-repository.js.map +1 -0
  128. package/dist/server/modules/affairs-indexer/core/src/scanner/file-scanner.d.ts +32 -0
  129. package/dist/server/modules/affairs-indexer/core/src/scanner/file-scanner.js +208 -0
  130. package/dist/server/modules/affairs-indexer/core/src/scanner/file-scanner.js.map +1 -0
  131. package/dist/server/modules/affairs-indexer/core/src/services/dirty/dirty-scope-resolver.d.ts +30 -0
  132. package/dist/server/modules/affairs-indexer/core/src/services/dirty/dirty-scope-resolver.js +66 -0
  133. package/dist/server/modules/affairs-indexer/core/src/services/dirty/dirty-scope-resolver.js.map +1 -0
  134. package/dist/server/modules/affairs-indexer/core/src/services/export/export-builder.d.ts +33 -0
  135. package/dist/server/modules/affairs-indexer/core/src/services/export/export-builder.js +705 -0
  136. package/dist/server/modules/affairs-indexer/core/src/services/export/export-builder.js.map +1 -0
  137. package/dist/server/modules/affairs-indexer/core/src/services/indexer/allowed-extensions-diff-service.d.ts +80 -0
  138. package/dist/server/modules/affairs-indexer/core/src/services/indexer/allowed-extensions-diff-service.js +193 -0
  139. package/dist/server/modules/affairs-indexer/core/src/services/indexer/allowed-extensions-diff-service.js.map +1 -0
  140. package/dist/server/modules/affairs-indexer/core/src/services/indexer/text-indexer.d.ts +77 -0
  141. package/dist/server/modules/affairs-indexer/core/src/services/indexer/text-indexer.js +467 -0
  142. package/dist/server/modules/affairs-indexer/core/src/services/indexer/text-indexer.js.map +1 -0
  143. package/dist/server/modules/affairs-indexer/core/src/services/mcp/mcp-stdio-server.d.ts +17 -0
  144. package/dist/server/modules/affairs-indexer/core/src/services/mcp/mcp-stdio-server.js +264 -0
  145. package/dist/server/modules/affairs-indexer/core/src/services/mcp/mcp-stdio-server.js.map +1 -0
  146. package/dist/server/modules/affairs-indexer/core/src/services/search/offline-search-service.d.ts +11 -0
  147. package/dist/server/modules/affairs-indexer/core/src/services/search/offline-search-service.js +76 -0
  148. package/dist/server/modules/affairs-indexer/core/src/services/search/offline-search-service.js.map +1 -0
  149. package/dist/server/modules/affairs-indexer/core/src/services/search/search-index-builder.d.ts +26 -0
  150. package/dist/server/modules/affairs-indexer/core/src/services/search/search-index-builder.js +305 -0
  151. package/dist/server/modules/affairs-indexer/core/src/services/search/search-index-builder.js.map +1 -0
  152. package/dist/server/modules/affairs-indexer/core/src/services/tagging/tag-recompute-service.d.ts +53 -0
  153. package/dist/server/modules/affairs-indexer/core/src/services/tagging/tag-recompute-service.js +566 -0
  154. package/dist/server/modules/affairs-indexer/core/src/services/tagging/tag-recompute-service.js.map +1 -0
  155. package/dist/server/modules/affairs-indexer/core/src/services/watch/watch-service.d.ts +47 -0
  156. package/dist/server/modules/affairs-indexer/core/src/services/watch/watch-service.js +227 -0
  157. package/dist/server/modules/affairs-indexer/core/src/services/watch/watch-service.js.map +1 -0
  158. package/dist/server/modules/affairs-indexer/core/src/sqlite/catalog-schema.d.ts +5 -0
  159. package/dist/server/modules/affairs-indexer/core/src/sqlite/catalog-schema.js +245 -0
  160. package/dist/server/modules/affairs-indexer/core/src/sqlite/catalog-schema.js.map +1 -0
  161. package/dist/server/modules/affairs-indexer/core/src/sqlite/detect-catalog-schema.d.ts +14 -0
  162. package/dist/server/modules/affairs-indexer/core/src/sqlite/detect-catalog-schema.js +87 -0
  163. package/dist/server/modules/affairs-indexer/core/src/sqlite/detect-catalog-schema.js.map +1 -0
  164. package/dist/server/modules/affairs-indexer/core/src/sqlite/init-catalog.d.ts +13 -0
  165. package/dist/server/modules/affairs-indexer/core/src/sqlite/init-catalog.js +16 -0
  166. package/dist/server/modules/affairs-indexer/core/src/sqlite/init-catalog.js.map +1 -0
  167. package/dist/server/modules/affairs-indexer/core/src/sqlite/migration-runner.d.ts +22 -0
  168. package/dist/server/modules/affairs-indexer/core/src/sqlite/migration-runner.js +430 -0
  169. package/dist/server/modules/affairs-indexer/core/src/sqlite/migration-runner.js.map +1 -0
  170. package/dist/server/modules/affairs-indexer/core/src/sqlite/open-database.d.ts +9 -0
  171. package/dist/server/modules/affairs-indexer/core/src/sqlite/open-database.js +19 -0
  172. package/dist/server/modules/affairs-indexer/core/src/sqlite/open-database.js.map +1 -0
  173. package/dist/server/modules/affairs-indexer/core/src/tagging/simple-tag-inference.d.ts +21 -0
  174. package/dist/server/modules/affairs-indexer/core/src/tagging/simple-tag-inference.js +94 -0
  175. package/dist/server/modules/affairs-indexer/core/src/tagging/simple-tag-inference.js.map +1 -0
  176. package/dist/server/modules/affairs-indexer/core/src/utils/abort.d.ts +2 -0
  177. package/dist/server/modules/affairs-indexer/core/src/utils/abort.js +13 -0
  178. package/dist/server/modules/affairs-indexer/core/src/utils/abort.js.map +1 -0
  179. package/dist/server/modules/affairs-indexer/core/src/utils/file-streaming.d.ts +9 -0
  180. package/dist/server/modules/affairs-indexer/core/src/utils/file-streaming.js +64 -0
  181. package/dist/server/modules/affairs-indexer/core/src/utils/file-streaming.js.map +1 -0
  182. package/dist/server/modules/affairs-indexer/core/src/utils/root-command-lock.d.ts +10 -0
  183. package/dist/server/modules/affairs-indexer/core/src/utils/root-command-lock.js +230 -0
  184. package/dist/server/modules/affairs-indexer/core/src/utils/root-command-lock.js.map +1 -0
  185. package/dist/server/modules/affairs-indexer/core/src/utils/rss-log.d.ts +2 -0
  186. package/dist/server/modules/affairs-indexer/core/src/utils/rss-log.js +19 -0
  187. package/dist/server/modules/affairs-indexer/core/src/utils/rss-log.js.map +1 -0
  188. package/dist/server/modules/affairs-indexer/internal-command-runner.d.ts +31 -0
  189. package/dist/server/modules/affairs-indexer/internal-command-runner.js +643 -0
  190. package/dist/server/modules/affairs-indexer/internal-command-runner.js.map +1 -0
  191. package/dist/server/modules/assistant-capability/assistant-capability-controller.d.ts +0 -49
  192. package/dist/server/modules/assistant-capability/assistant-capability-controller.js +10 -56
  193. package/dist/server/modules/assistant-capability/assistant-capability-controller.js.map +1 -1
  194. package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +2 -46
  195. package/dist/server/modules/assistant-capability/assistant-capability-service.js +15 -158
  196. package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
  197. package/dist/server/modules/browser-runtime/opencli-bridge-browser-executor.d.ts +4 -2
  198. package/dist/server/modules/browser-runtime/opencli-bridge-browser-executor.js +62 -21
  199. package/dist/server/modules/browser-runtime/opencli-bridge-browser-executor.js.map +1 -1
  200. package/dist/server/modules/butler/butler-control-session-service.d.ts +3 -4
  201. package/dist/server/modules/butler/butler-control-session-service.js +39 -62
  202. package/dist/server/modules/butler/butler-control-session-service.js.map +1 -1
  203. package/dist/server/modules/butler/butler-controller.d.ts +11 -3
  204. package/dist/server/modules/butler/butler-controller.js +19 -4
  205. package/dist/server/modules/butler/butler-controller.js.map +1 -1
  206. package/dist/server/modules/butler/butler-follow-up-service.js.map +1 -1
  207. package/dist/server/modules/butler/butler-profile-service.d.ts +1 -1
  208. package/dist/server/modules/butler/butler-profile-service.js +34 -63
  209. package/dist/server/modules/butler/butler-profile-service.js.map +1 -1
  210. package/dist/server/modules/butler/butler-project-service.d.ts +1 -3
  211. package/dist/server/modules/butler/butler-project-service.js +1 -7
  212. package/dist/server/modules/butler/butler-project-service.js.map +1 -1
  213. package/dist/server/modules/butler/butler-session-service.d.ts +1 -0
  214. package/dist/server/modules/butler/butler-session-service.js +109 -0
  215. package/dist/server/modules/butler/butler-session-service.js.map +1 -1
  216. package/dist/server/modules/butler/butler-session-summary-service.js +0 -2
  217. package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -1
  218. package/dist/server/modules/butler/butler-workspace-context.d.ts +5 -1
  219. package/dist/server/modules/butler/butler-workspace-context.js +21 -12
  220. package/dist/server/modules/butler/butler-workspace-context.js.map +1 -1
  221. package/dist/server/modules/file/file-content-service.d.ts +11 -0
  222. package/dist/server/modules/file/file-content-service.js +55 -0
  223. package/dist/server/modules/file/file-content-service.js.map +1 -1
  224. package/dist/server/modules/file/file-controller.d.ts +26 -1
  225. package/dist/server/modules/file/file-controller.js +59 -4
  226. package/dist/server/modules/file/file-controller.js.map +1 -1
  227. package/dist/server/modules/file/file-preview-link-service.d.ts +1 -0
  228. package/dist/server/modules/file/file-preview-link-service.js +25 -0
  229. package/dist/server/modules/file/file-preview-link-service.js.map +1 -1
  230. package/dist/server/modules/file/file-preview-service.js +15 -4
  231. package/dist/server/modules/file/file-preview-service.js.map +1 -1
  232. package/dist/server/modules/file/file-preview-types.d.ts +9 -1
  233. package/dist/server/modules/file/file-preview-types.js +8 -1
  234. package/dist/server/modules/file/file-preview-types.js.map +1 -1
  235. package/dist/server/modules/file/recent-modified-file-service.d.ts +15 -0
  236. package/dist/server/modules/file/recent-modified-file-service.js +102 -0
  237. package/dist/server/modules/file/recent-modified-file-service.js.map +1 -0
  238. package/dist/server/modules/file/runtime/codingns-workspace-bridge.js +6 -0
  239. package/dist/server/modules/file/workspace-file-bridge-service.d.ts +11 -0
  240. package/dist/server/modules/file/workspace-file-bridge-service.js +19 -0
  241. package/dist/server/modules/file/workspace-file-bridge-service.js.map +1 -1
  242. package/dist/server/modules/file/workspace-index-apply-service.d.ts +25 -0
  243. package/dist/server/modules/file/workspace-index-apply-service.js +42 -0
  244. package/dist/server/modules/file/workspace-index-apply-service.js.map +1 -0
  245. package/dist/server/modules/office/office-controller.d.ts +15 -1
  246. package/dist/server/modules/office/office-controller.js +26 -1
  247. package/dist/server/modules/office/office-controller.js.map +1 -1
  248. package/dist/server/modules/office/onlyoffice-integration-service.d.ts +78 -0
  249. package/dist/server/modules/office/onlyoffice-integration-service.js +610 -0
  250. package/dist/server/modules/office/onlyoffice-integration-service.js.map +1 -0
  251. package/dist/server/modules/plugins/plugin-file-gateway-service.d.ts +12 -0
  252. package/dist/server/modules/plugins/plugin-file-gateway-service.js +13 -0
  253. package/dist/server/modules/plugins/plugin-file-gateway-service.js.map +1 -1
  254. package/dist/server/modules/preferences/profile-service.d.ts +1 -0
  255. package/dist/server/modules/preferences/profile-service.js +27 -3
  256. package/dist/server/modules/preferences/profile-service.js.map +1 -1
  257. package/dist/server/modules/sessions/codex-app-server-helper-process.js +0 -8
  258. package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
  259. package/dist/server/modules/sessions/session-controller.d.ts +1 -0
  260. package/dist/server/modules/sessions/session-controller.js +3 -0
  261. package/dist/server/modules/sessions/session-controller.js.map +1 -1
  262. package/dist/server/modules/sessions/session-history-service.d.ts +2 -0
  263. package/dist/server/modules/sessions/session-history-service.js +78 -3
  264. package/dist/server/modules/sessions/session-history-service.js.map +1 -1
  265. package/dist/server/modules/sessions/session-live-runtime-service.d.ts +1 -0
  266. package/dist/server/modules/sessions/session-live-runtime-service.js +4 -0
  267. package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
  268. package/dist/server/modules/sessions/session-permission-request-service.js +0 -4
  269. package/dist/server/modules/sessions/session-permission-request-service.js.map +1 -1
  270. package/dist/server/modules/sessions/workspace-session-instruction-watch-service.d.ts +23 -0
  271. package/dist/server/modules/sessions/workspace-session-instruction-watch-service.js +122 -0
  272. package/dist/server/modules/sessions/workspace-session-instruction-watch-service.js.map +1 -0
  273. package/dist/server/modules/sessions/workspace-session-runtime-context-service.d.ts +15 -0
  274. package/dist/server/modules/sessions/workspace-session-runtime-context-service.js +93 -10
  275. package/dist/server/modules/sessions/workspace-session-runtime-context-service.js.map +1 -1
  276. package/dist/server/modules/skills/builtin-skills/codingns-assistant/SKILL.md +6 -7
  277. package/dist/server/modules/skills/builtin-skills/codingns-assistant/references/cli-workflow.md +2 -3
  278. package/dist/server/modules/system/host-resource-controller.d.ts +7 -0
  279. package/dist/server/modules/system/host-resource-controller.js +12 -0
  280. package/dist/server/modules/system/host-resource-controller.js.map +1 -0
  281. package/dist/server/modules/system/host-resource-service.d.ts +54 -0
  282. package/dist/server/modules/system/host-resource-service.js +162 -0
  283. package/dist/server/modules/system/host-resource-service.js.map +1 -0
  284. package/dist/server/modules/tasks/observability-service.d.ts +12 -2
  285. package/dist/server/modules/tasks/observability-service.js +13 -1
  286. package/dist/server/modules/tasks/observability-service.js.map +1 -1
  287. package/dist/server/modules/tasks/task-helper-client.d.ts +36 -2
  288. package/dist/server/modules/tasks/task-helper-client.js +201 -19
  289. package/dist/server/modules/tasks/task-helper-client.js.map +1 -1
  290. package/dist/server/modules/tasks/task-helper-pool.d.ts +37 -0
  291. package/dist/server/modules/tasks/task-helper-pool.js +173 -0
  292. package/dist/server/modules/tasks/task-helper-pool.js.map +1 -0
  293. package/dist/server/modules/tasks/task-helper-process-handlers.d.ts +27 -0
  294. package/dist/server/modules/tasks/task-helper-process-handlers.js +25 -1
  295. package/dist/server/modules/tasks/task-helper-process-handlers.js.map +1 -1
  296. package/dist/server/modules/tasks/task-helper-process.js +75 -26
  297. package/dist/server/modules/tasks/task-helper-process.js.map +1 -1
  298. package/dist/server/modules/tasks/task-helper-scheduling.d.ts +11 -0
  299. package/dist/server/modules/tasks/task-helper-scheduling.js +43 -0
  300. package/dist/server/modules/tasks/task-helper-scheduling.js.map +1 -0
  301. package/dist/server/modules/tasks/task-lane-executors.js +19 -3
  302. package/dist/server/modules/tasks/task-lane-executors.js.map +1 -1
  303. package/dist/server/modules/tasks/task-manager.d.ts +1 -0
  304. package/dist/server/modules/tasks/task-manager.js +3 -0
  305. package/dist/server/modules/tasks/task-manager.js.map +1 -1
  306. package/dist/server/modules/tasks/task-registry.d.ts +1 -0
  307. package/dist/server/modules/tasks/task-registry.js +3 -0
  308. package/dist/server/modules/tasks/task-registry.js.map +1 -1
  309. package/dist/server/modules/tasks/task-scheduler.d.ts +6 -0
  310. package/dist/server/modules/tasks/task-scheduler.js +162 -7
  311. package/dist/server/modules/tasks/task-scheduler.js.map +1 -1
  312. package/dist/server/modules/tasks/task-types.d.ts +28 -3
  313. package/dist/server/modules/tasks/task-types.js +14 -2
  314. package/dist/server/modules/tasks/task-types.js.map +1 -1
  315. package/dist/server/modules/workbench/affairs-assistant-session-snapshot-service.d.ts +68 -0
  316. package/dist/server/modules/workbench/affairs-assistant-session-snapshot-service.js +286 -0
  317. package/dist/server/modules/workbench/affairs-assistant-session-snapshot-service.js.map +1 -0
  318. package/dist/server/modules/workbench/workbench-controller.js +11 -1
  319. package/dist/server/modules/workbench/workbench-controller.js.map +1 -1
  320. package/dist/server/modules/workbench/workbench-service.d.ts +9 -2
  321. package/dist/server/modules/workbench/workbench-service.js +58 -18
  322. package/dist/server/modules/workbench/workbench-service.js.map +1 -1
  323. package/dist/server/modules/workspace/affairs-library-controller.d.ts +117 -0
  324. package/dist/server/modules/workspace/affairs-library-controller.js +164 -0
  325. package/dist/server/modules/workspace/affairs-library-controller.js.map +1 -0
  326. package/dist/server/modules/workspace/affairs-library-debug-log.d.ts +23 -0
  327. package/dist/server/modules/workspace/affairs-library-debug-log.js +107 -0
  328. package/dist/server/modules/workspace/affairs-library-debug-log.js.map +1 -0
  329. package/dist/server/modules/workspace/affairs-library-dirty-watch-service.d.ts +52 -0
  330. package/dist/server/modules/workspace/affairs-library-dirty-watch-service.js +555 -0
  331. package/dist/server/modules/workspace/affairs-library-dirty-watch-service.js.map +1 -0
  332. package/dist/server/modules/workspace/affairs-library-preview-link-service.d.ts +24 -0
  333. package/dist/server/modules/workspace/affairs-library-preview-link-service.js +157 -0
  334. package/dist/server/modules/workspace/affairs-library-preview-link-service.js.map +1 -0
  335. package/dist/server/modules/workspace/affairs-library-refresh-contract.d.ts +56 -0
  336. package/dist/server/modules/workspace/affairs-library-refresh-contract.js +48 -0
  337. package/dist/server/modules/workspace/affairs-library-refresh-contract.js.map +1 -0
  338. package/dist/server/modules/workspace/affairs-library-service.d.ts +344 -0
  339. package/dist/server/modules/workspace/affairs-library-service.js +3755 -0
  340. package/dist/server/modules/workspace/affairs-library-service.js.map +1 -0
  341. package/dist/server/modules/workspace/affairs-lightweight-session-controller.d.ts +78 -0
  342. package/dist/server/modules/workspace/affairs-lightweight-session-controller.js +146 -0
  343. package/dist/server/modules/workspace/affairs-lightweight-session-controller.js.map +1 -0
  344. package/dist/server/modules/workspace/affairs-lightweight-session-service.d.ts +133 -0
  345. package/dist/server/modules/workspace/affairs-lightweight-session-service.js +1447 -0
  346. package/dist/server/modules/workspace/affairs-lightweight-session-service.js.map +1 -0
  347. package/dist/server/modules/workspace/affairs-tag-controller.d.ts +107 -0
  348. package/dist/server/modules/workspace/affairs-tag-controller.js +97 -0
  349. package/dist/server/modules/workspace/affairs-tag-controller.js.map +1 -0
  350. package/dist/server/modules/workspace/affairs-tag-service.d.ts +153 -0
  351. package/dist/server/modules/workspace/affairs-tag-service.js +680 -0
  352. package/dist/server/modules/workspace/affairs-tag-service.js.map +1 -0
  353. package/dist/server/modules/workspace/workspace-controller.d.ts +2 -1
  354. package/dist/server/modules/workspace/workspace-controller.js +8 -2
  355. package/dist/server/modules/workspace/workspace-controller.js.map +1 -1
  356. package/dist/server/modules/workspace/workspace-service.js +60 -9
  357. package/dist/server/modules/workspace/workspace-service.js.map +1 -1
  358. package/dist/server/routes/affairs.d.ts +3 -0
  359. package/dist/server/routes/affairs.js +7 -0
  360. package/dist/server/routes/affairs.js.map +1 -0
  361. package/dist/server/routes/assistant.js +0 -5
  362. package/dist/server/routes/assistant.js.map +1 -1
  363. package/dist/server/routes/files.js +5 -0
  364. package/dist/server/routes/files.js.map +1 -1
  365. package/dist/server/routes/office.js +4 -0
  366. package/dist/server/routes/office.js.map +1 -1
  367. package/dist/server/routes/system.d.ts +2 -1
  368. package/dist/server/routes/system.js +2 -1
  369. package/dist/server/routes/system.js.map +1 -1
  370. package/dist/server/routes/workspaces.d.ts +4 -1
  371. package/dist/server/routes/workspaces.js +46 -1
  372. package/dist/server/routes/workspaces.js.map +1 -1
  373. package/dist/server/server/create-server.d.ts +6 -2
  374. package/dist/server/server/create-server.js +113 -32
  375. package/dist/server/server/create-server.js.map +1 -1
  376. package/dist/server/shared/http/error-handler.js +10 -0
  377. package/dist/server/shared/http/error-handler.js.map +1 -1
  378. package/dist/server/storage/repositories/affairs-assistant-session-snapshot-repository.d.ts +10 -0
  379. package/dist/server/storage/repositories/affairs-assistant-session-snapshot-repository.js +47 -0
  380. package/dist/server/storage/repositories/affairs-assistant-session-snapshot-repository.js.map +1 -0
  381. package/dist/server/storage/repositories/butler-profile-repository.js +7 -3
  382. package/dist/server/storage/repositories/butler-profile-repository.js.map +1 -1
  383. package/dist/server/storage/repositories/office-onlyoffice-setting-repository.d.ts +19 -0
  384. package/dist/server/storage/repositories/office-onlyoffice-setting-repository.js +55 -0
  385. package/dist/server/storage/repositories/office-onlyoffice-setting-repository.js.map +1 -0
  386. package/dist/server/storage/repositories/session-index-repository.js +9 -2
  387. package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
  388. package/dist/server/storage/repositories/user-affairs-library-setting-repository.d.ts +10 -0
  389. package/dist/server/storage/repositories/user-affairs-library-setting-repository.js +69 -0
  390. package/dist/server/storage/repositories/user-affairs-library-setting-repository.js.map +1 -0
  391. package/dist/server/storage/repositories/user-preference-profile-repository.js +6 -3
  392. package/dist/server/storage/repositories/user-preference-profile-repository.js.map +1 -1
  393. package/dist/server/storage/repositories/workspace-navigation-state-repository.d.ts +3 -0
  394. package/dist/server/storage/repositories/workspace-navigation-state-repository.js +47 -4
  395. package/dist/server/storage/repositories/workspace-navigation-state-repository.js.map +1 -1
  396. package/dist/server/storage/sqlite/client.js +230 -123
  397. package/dist/server/storage/sqlite/client.js.map +1 -1
  398. package/dist/server/storage/sqlite/schema.sql +48 -25
  399. package/dist/server/types/domain.d.ts +27 -20
  400. package/dist/server/ws/workbench-ws-hub.js +2 -2
  401. package/dist/server/ws/workbench-ws-hub.js.map +1 -1
  402. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.js +0 -2
  403. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.js.map +1 -1
  404. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +0 -6
  405. package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +1 -1
  406. package/package.json +1 -1
  407. package/dist/public/assets/AdaptiveButlerPage-B153lk5H.css +0 -1
  408. package/dist/public/assets/AdaptiveButlerPage-CJw8Ae62.js +0 -3
  409. package/dist/public/assets/DesktopModal-D_A8sgQU.js +0 -1
  410. package/dist/public/assets/DesktopWindowPage-DK7L7osV.js +0 -2
  411. package/dist/public/assets/FileContextPanel-BdCoubcJ.js +0 -1
  412. package/dist/public/assets/GitSidebar-BeZ0hj7A.js +0 -6
  413. package/dist/public/assets/MobileCreateSessionSheet-DfLMVu8q.js +0 -1
  414. package/dist/public/assets/MobileSheet-5kZ-w-gU.js +0 -1
  415. package/dist/public/assets/MobileWorkspaceSwitcherHeader-C6JMiOq_.js +0 -1
  416. package/dist/public/assets/PluginContainerPage-BlY-xJDh.js +0 -1
  417. package/dist/public/assets/SessionIndexPage-DkBp9Mqz.js +0 -1
  418. package/dist/public/assets/SettingsPage-C-ASmJAG.js +0 -2
  419. package/dist/public/assets/TerminalRuntimeFallbackModal-Bzum5nZ0.js +0 -1
  420. package/dist/public/assets/WorkbenchLayout-OFi6CWgH.js +0 -244
  421. package/dist/public/assets/WorkbenchShellRoute-B4XB8SwG.css +0 -1
  422. package/dist/public/assets/WorkbenchShellRoute-BAQe_E0O.js +0 -1
  423. package/dist/public/assets/WorkspaceDebugDetailPage-DhKa6e9y.js +0 -1
  424. package/dist/public/assets/file-tree-icon-Mg1DiBRX.js +0 -590
  425. package/dist/public/assets/index-C4t-vvqk.css +0 -1
  426. package/dist/public/assets/index-CL97fwWB.js +0 -42
  427. package/dist/public/assets/realtime-client-CLafKzzJ.js +0 -1
  428. package/dist/server/modules/butler/assistant-sandbox-cleanup-scheduler.d.ts +0 -32
  429. package/dist/server/modules/butler/assistant-sandbox-cleanup-scheduler.js +0 -93
  430. package/dist/server/modules/butler/assistant-sandbox-cleanup-scheduler.js.map +0 -1
  431. package/dist/server/modules/butler/assistant-sandbox-service.d.ts +0 -69
  432. package/dist/server/modules/butler/assistant-sandbox-service.js +0 -399
  433. package/dist/server/modules/butler/assistant-sandbox-service.js.map +0 -1
  434. package/dist/server/modules/channels/wechat-claw-client.d.ts +0 -51
  435. package/dist/server/modules/channels/wechat-claw-client.js +0 -245
  436. package/dist/server/modules/channels/wechat-claw-client.js.map +0 -1
  437. package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.d.ts +0 -18
  438. package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.js +0 -191
  439. package/dist/server/storage/repositories/assistant-sandbox-workspace-repository.js.map +0 -1
@@ -0,0 +1,1350 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { openDatabase } from "../sqlite/open-database.js";
5
+ function makeStableId(prefix, value) {
6
+ const digest = crypto.createHash("sha1").update(value).digest("hex");
7
+ return `${prefix}_${digest}`;
8
+ }
9
+ /**
10
+ * 最小写入仓库。
11
+ * 第二阶段补上 prepared statement 复用与批量连接内执行,减少大批量索引时的重复 prepare 与全表清理成本。
12
+ */
13
+ export class CatalogWriteRepository {
14
+ dbPath;
15
+ tagIdCache = new Map();
16
+ activeDb = null;
17
+ activeStatements = null;
18
+ activeBootstrapSession = false;
19
+ constructor(dbPath) {
20
+ this.dbPath = dbPath;
21
+ }
22
+ beginSession() {
23
+ if (this.activeDb) {
24
+ return;
25
+ }
26
+ this.activeDb = openDatabase(this.dbPath);
27
+ this.activeStatements = this.prepareStatements(this.activeDb);
28
+ this.activeBootstrapSession = this.detectBootstrapSession(this.activeDb);
29
+ }
30
+ endSession() {
31
+ if (!this.activeDb) {
32
+ return;
33
+ }
34
+ this.activeDb.close();
35
+ this.activeDb = null;
36
+ this.activeStatements = null;
37
+ this.activeBootstrapSession = false;
38
+ }
39
+ withConnection(handler) {
40
+ if (this.activeDb && this.activeStatements) {
41
+ return handler(this.activeDb, this.activeStatements);
42
+ }
43
+ const db = openDatabase(this.dbPath);
44
+ const statements = this.prepareStatements(db);
45
+ try {
46
+ return handler(db, statements);
47
+ }
48
+ finally {
49
+ db.close();
50
+ }
51
+ }
52
+ normalizeRelativePath(relativePath) {
53
+ return relativePath.split(path.sep).join("/");
54
+ }
55
+ invalidateCachedTagPath(tagPath) {
56
+ const normalizedPath = tagPath?.trim();
57
+ if (!normalizedPath) {
58
+ return;
59
+ }
60
+ this.tagIdCache.delete(normalizedPath);
61
+ }
62
+ getSchemaMeta(key) {
63
+ return this.withConnection(db => {
64
+ const row = db.prepare(`SELECT value FROM schema_meta WHERE key = ?`).get(key);
65
+ return typeof row?.value === "string" ? row.value : null;
66
+ });
67
+ }
68
+ setSchemaMeta(key, value, updatedAt = new Date().toISOString()) {
69
+ this.withConnection(db => {
70
+ const updated = db.prepare(`
71
+ UPDATE schema_meta
72
+ SET value = ?, updated_at = ?
73
+ WHERE key = ?
74
+ `).run(value, updatedAt, key);
75
+ if ((updated.changes || 0) > 0) {
76
+ return;
77
+ }
78
+ db.prepare(`
79
+ INSERT INTO schema_meta(key, value, updated_at)
80
+ VALUES(?, ?, ?)
81
+ `).run(key, value, updatedAt);
82
+ });
83
+ }
84
+ countActiveIndexedDocuments() {
85
+ return this.withConnection(db => {
86
+ const row = db.prepare(`
87
+ SELECT COUNT(*) AS count
88
+ FROM documents d
89
+ JOIN files f ON f.id = d.file_id
90
+ WHERE f.status = 'active'
91
+ AND d.index_status = 'indexed'
92
+ `).get();
93
+ return Number(row?.count ?? 0);
94
+ });
95
+ }
96
+ countActiveFiles() {
97
+ return this.withConnection(db => {
98
+ const row = db.prepare(`
99
+ SELECT COUNT(*) AS count
100
+ FROM files
101
+ WHERE status = 'active'
102
+ `).get();
103
+ return Number(row?.count ?? 0);
104
+ });
105
+ }
106
+ countRows(tableName) {
107
+ return this.withConnection(db => {
108
+ const row = db.prepare(`SELECT COUNT(*) AS count FROM ${tableName}`).get();
109
+ return Number(row?.count ?? 0);
110
+ });
111
+ }
112
+ detectBootstrapSession(db) {
113
+ const row = db.prepare(`
114
+ SELECT
115
+ (SELECT COUNT(*) FROM documents) AS document_count,
116
+ (SELECT COUNT(*) FROM document_tags) AS document_tag_count,
117
+ (SELECT COUNT(*) FROM derived_document_tags) AS derived_tag_count
118
+ `).get();
119
+ return Number(row?.document_count ?? 0) === 0
120
+ && Number(row?.document_tag_count ?? 0) === 0
121
+ && Number(row?.derived_tag_count ?? 0) === 0;
122
+ }
123
+ prepareStatements(db) {
124
+ return {
125
+ upsertFile: db.prepare(`
126
+ INSERT INTO files(id, path, dir_path, name, extension, size, mtime, ctime, inode_key, content_hash, status, last_seen_at)
127
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'active', ?)
128
+ ON CONFLICT(path) DO UPDATE SET
129
+ dir_path = excluded.dir_path,
130
+ name = excluded.name,
131
+ extension = excluded.extension,
132
+ size = excluded.size,
133
+ mtime = excluded.mtime,
134
+ ctime = excluded.ctime,
135
+ inode_key = excluded.inode_key,
136
+ content_hash = excluded.content_hash,
137
+ status = 'active',
138
+ last_seen_at = excluded.last_seen_at
139
+ `),
140
+ upsertDocument: db.prepare(`
141
+ INSERT INTO documents(id, file_id, title, summary, language, parse_status, parse_error, index_status, chunk_count, last_indexed_at)
142
+ VALUES(?, ?, ?, ?, 'zh', ?, ?, ?, ?, ?)
143
+ ON CONFLICT(file_id) DO UPDATE SET
144
+ id = excluded.id,
145
+ title = excluded.title,
146
+ summary = excluded.summary,
147
+ parse_status = excluded.parse_status,
148
+ parse_error = excluded.parse_error,
149
+ index_status = excluded.index_status,
150
+ chunk_count = excluded.chunk_count,
151
+ last_indexed_at = excluded.last_indexed_at
152
+ `),
153
+ insertChunk: db.prepare(`
154
+ INSERT INTO chunks(id, document_id, chunk_index, content, content_hash, page_no, sheet_name, heading_path, token_count, vector_point_id)
155
+ VALUES(?, ?, ?, ?, NULL, NULL, NULL, NULL, NULL, NULL)
156
+ `),
157
+ insertTag: db.prepare(`
158
+ INSERT OR IGNORE INTO tags(id, root_type, path, name, parent_id, canonical_name, description, status, created_by, created_at, updated_at, disabled_at)
159
+ VALUES(?, ?, ?, ?, ?, ?, '', 'active', ?, ?, ?, NULL)
160
+ `),
161
+ updateTagDefinition: db.prepare(`
162
+ UPDATE tags
163
+ SET root_type = ?,
164
+ path = ?,
165
+ name = ?,
166
+ parent_id = ?,
167
+ canonical_name = ?,
168
+ description = ?,
169
+ status = ?,
170
+ updated_at = ?,
171
+ disabled_at = ?
172
+ WHERE id = ?
173
+ `),
174
+ selectTagById: db.prepare(`
175
+ SELECT id, root_type, path, name, parent_id, canonical_name, description, status, created_by, created_at, updated_at, disabled_at
176
+ FROM tags
177
+ WHERE id = ?
178
+ `),
179
+ selectTagByPath: db.prepare(`
180
+ SELECT id, root_type, path, name, parent_id, canonical_name, description, status, created_by, created_at, updated_at, disabled_at
181
+ FROM tags
182
+ WHERE path = ?
183
+ `),
184
+ selectTagChildrenByParentId: db.prepare(`
185
+ SELECT id
186
+ FROM tags
187
+ WHERE parent_id = ?
188
+ `),
189
+ deleteManualDocumentBindingsByDocumentId: db.prepare(`DELETE FROM manual_document_tag_bindings WHERE document_id = ?`),
190
+ insertManualDocumentBinding: db.prepare(`
191
+ INSERT OR REPLACE INTO manual_document_tag_bindings(id, document_id, tag_id, source, created_at, updated_at)
192
+ VALUES(?, ?, ?, ?, ?, ?)
193
+ `),
194
+ deleteManualFileBindingsByTagId: db.prepare(`DELETE FROM manual_file_tag_bindings WHERE tag_id = ?`),
195
+ insertManualFileBinding: db.prepare(`
196
+ INSERT OR REPLACE INTO manual_file_tag_bindings(id, inode_key, content_hash, file_size, extension, tag_id, source, created_at, updated_at)
197
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)
198
+ `),
199
+ selectManualFileBindingsForIdentity: db.prepare(`
200
+ SELECT id
201
+ FROM manual_file_tag_bindings
202
+ WHERE (? IS NOT NULL AND inode_key = ?)
203
+ OR (
204
+ ? IS NOT NULL
205
+ AND inode_key IS NULL
206
+ AND content_hash = ?
207
+ AND file_size = ?
208
+ AND extension = ?
209
+ )
210
+ `),
211
+ deleteManualFileBindingById: db.prepare(`DELETE FROM manual_file_tag_bindings WHERE id = ?`),
212
+ deleteFolderBindingsByFolderPath: db.prepare(`DELETE FROM folder_tag_bindings WHERE folder_path = ?`),
213
+ insertFolderBinding: db.prepare(`
214
+ INSERT INTO folder_tag_bindings(id, folder_path, tag_id, apply_mode, created_at, updated_at)
215
+ VALUES(?, ?, ?, ?, ?, ?)
216
+ `),
217
+ deleteTagRulesByTagId: db.prepare(`DELETE FROM tag_rules WHERE tag_id = ?`),
218
+ insertTagRule: db.prepare(`
219
+ INSERT INTO tag_rules(id, tag_id, enabled, rule_type, scope_json, matcher_json, min_score, priority, source, created_at, updated_at)
220
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
221
+ `),
222
+ deleteDocumentTagByPair: db.prepare(`DELETE FROM document_tags WHERE document_id = ? AND tag_id = ?`),
223
+ deleteDerivedDocumentTagByPair: db.prepare(`DELETE FROM derived_document_tags WHERE document_id = ? AND tag_id = ?`),
224
+ deleteDocumentTagByDocumentAndSource: db.prepare(`DELETE FROM document_tags WHERE document_id = ? AND source = ?`),
225
+ deleteDerivedDocumentTagByDocumentAndSource: db.prepare(`DELETE FROM derived_document_tags WHERE document_id = ? AND source = ?`),
226
+ insertDocumentTag: db.prepare(`
227
+ INSERT INTO document_tags(id, document_id, tag_id, confidence, source, source_ref, evidence, manual_override, updated_at)
228
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)
229
+ `),
230
+ insertDerivedTag: db.prepare(`
231
+ INSERT INTO derived_document_tags(id, document_id, tag_id, source, source_ref, rule_name, evidence, computed_at, updated_at, expires_at)
232
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)
233
+ `),
234
+ upsertDocumentTag: db.prepare(`
235
+ INSERT INTO document_tags(id, document_id, tag_id, confidence, source, source_ref, evidence, manual_override, updated_at)
236
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?)
237
+ ON CONFLICT(document_id, tag_id) DO UPDATE SET
238
+ confidence = excluded.confidence,
239
+ source = excluded.source,
240
+ source_ref = excluded.source_ref,
241
+ evidence = excluded.evidence,
242
+ manual_override = excluded.manual_override,
243
+ updated_at = excluded.updated_at
244
+ `),
245
+ upsertDerivedTag: db.prepare(`
246
+ INSERT INTO derived_document_tags(id, document_id, tag_id, source, source_ref, rule_name, evidence, computed_at, updated_at, expires_at)
247
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, NULL)
248
+ ON CONFLICT(document_id, tag_id) DO UPDATE SET
249
+ source = excluded.source,
250
+ source_ref = excluded.source_ref,
251
+ rule_name = excluded.rule_name,
252
+ evidence = excluded.evidence,
253
+ computed_at = excluded.computed_at,
254
+ updated_at = excluded.updated_at,
255
+ expires_at = excluded.expires_at
256
+ `),
257
+ selectActiveFileIdentityByPath: db.prepare(`
258
+ SELECT
259
+ d.id AS document_id,
260
+ f.inode_key,
261
+ f.content_hash,
262
+ f.size,
263
+ f.extension
264
+ FROM files f
265
+ JOIN documents d ON d.file_id = f.id
266
+ WHERE f.path = ?
267
+ AND f.status = 'active'
268
+ AND d.index_status IN ('indexed', 'failed', 'skipped')
269
+ `),
270
+ selectFileByPath: db.prepare(`SELECT id FROM files WHERE path = ?`),
271
+ selectDocumentByFileId: db.prepare(`SELECT id FROM documents WHERE file_id = ?`),
272
+ selectUnseenIdentityCandidates: db.prepare(`
273
+ SELECT
274
+ f.id AS file_id,
275
+ f.path,
276
+ f.inode_key,
277
+ f.content_hash,
278
+ f.size,
279
+ f.extension,
280
+ d.id AS document_id
281
+ FROM files f
282
+ JOIN documents d ON d.file_id = f.id
283
+ WHERE f.status = 'active'
284
+ AND d.index_status IN ('indexed', 'failed', 'skipped')
285
+ AND f.path <> ?
286
+ AND f.last_seen_at <> ?
287
+ AND (
288
+ (? IS NOT NULL AND f.inode_key = ?)
289
+ OR (
290
+ ? IS NOT NULL
291
+ AND f.content_hash = ?
292
+ AND f.size = ?
293
+ AND f.extension = ?
294
+ )
295
+ )
296
+ ORDER BY
297
+ CASE
298
+ WHEN ? IS NOT NULL AND f.inode_key = ? THEN 0
299
+ ELSE 1
300
+ END,
301
+ f.last_seen_at DESC,
302
+ f.path
303
+ `),
304
+ selectManualBindingsByDocumentId: db.prepare(`
305
+ SELECT id, tag_id, source, created_at, updated_at
306
+ FROM manual_document_tag_bindings
307
+ WHERE document_id = ?
308
+ ORDER BY tag_id
309
+ `),
310
+ selectManualFileBindingRowsForIdentity: db.prepare(`
311
+ SELECT id, tag_id, source, created_at, updated_at
312
+ FROM manual_file_tag_bindings
313
+ WHERE (? IS NOT NULL AND inode_key = ?)
314
+ OR (
315
+ ? IS NOT NULL
316
+ AND inode_key IS NULL
317
+ AND content_hash = ?
318
+ AND file_size = ?
319
+ AND extension = ?
320
+ )
321
+ ORDER BY updated_at DESC, id
322
+ `),
323
+ deleteManualBindingByPair: db.prepare(`DELETE FROM manual_document_tag_bindings WHERE document_id = ? AND tag_id = ?`),
324
+ selectManualDocumentTagsByDocumentId: db.prepare(`
325
+ SELECT tag_id, confidence, source_ref, evidence, manual_override, updated_at
326
+ FROM document_tags
327
+ WHERE document_id = ?
328
+ AND source = 'manual_document'
329
+ ORDER BY tag_id
330
+ `),
331
+ selectDocumentTagIds: db.prepare(`SELECT tag_id FROM document_tags WHERE document_id = ?`),
332
+ selectDerivedTagIds: db.prepare(`SELECT tag_id FROM derived_document_tags WHERE document_id = ?`),
333
+ deleteDocumentTags: db.prepare(`DELETE FROM document_tags WHERE document_id = ?`),
334
+ deleteDerivedDocumentTags: db.prepare(`DELETE FROM derived_document_tags WHERE document_id = ?`),
335
+ deleteChunksByDocumentId: db.prepare(`DELETE FROM chunks WHERE document_id = ?`),
336
+ deleteDocumentById: db.prepare(`DELETE FROM documents WHERE id = ?`),
337
+ markFileDeleted: db.prepare(`
338
+ UPDATE files
339
+ SET status = 'deleted',
340
+ last_seen_at = ?
341
+ WHERE id = ?
342
+ `),
343
+ listActiveFilesAll: db.prepare(`SELECT path, last_seen_at FROM files WHERE status = 'active'`),
344
+ listActiveFilesExact: db.prepare(`SELECT path, last_seen_at FROM files WHERE status = 'active' AND path = ?`),
345
+ listActiveFilesPrefix: db.prepare(`SELECT path, last_seen_at FROM files WHERE status = 'active' AND (path = ? OR path LIKE ?)`),
346
+ countActiveIndexedDocuments: db.prepare(`
347
+ SELECT COUNT(*) AS count
348
+ FROM documents d
349
+ JOIN files f ON f.id = d.file_id
350
+ WHERE f.status = 'active'
351
+ AND d.index_status = 'indexed'
352
+ `),
353
+ selectActiveIndexedFileStateByPath: db.prepare(`
354
+ SELECT
355
+ f.path,
356
+ f.extension,
357
+ f.size,
358
+ f.mtime,
359
+ d.index_status
360
+ FROM files f
361
+ JOIN documents d ON d.file_id = f.id
362
+ WHERE f.path = ?
363
+ AND f.status = 'active'
364
+ AND d.index_status IN ('indexed', 'failed', 'skipped')
365
+ LIMIT 1
366
+ `),
367
+ };
368
+ }
369
+ ensureTagInConnection(db, statements, tagCache, tagPath, createdBy) {
370
+ const cached = tagCache.get(tagPath);
371
+ if (cached) {
372
+ const existingById = statements.selectTagById.get(cached);
373
+ if (existingById?.id && existingById.path === tagPath) {
374
+ return cached;
375
+ }
376
+ tagCache.delete(tagPath);
377
+ this.invalidateCachedTagPath(tagPath);
378
+ }
379
+ const existingByPath = statements.selectTagByPath.get(tagPath);
380
+ if (existingByPath?.id) {
381
+ tagCache.set(tagPath, existingByPath.id);
382
+ this.tagIdCache.set(tagPath, existingByPath.id);
383
+ return existingByPath.id;
384
+ }
385
+ const segments = tagPath.split("/").filter(Boolean);
386
+ const rootType = segments[0] ?? "未分类";
387
+ const parentPath = segments.length > 1 ? segments.slice(0, -1).join("/") : null;
388
+ const parentId = parentPath ? this.ensureTagInConnection(db, statements, tagCache, parentPath, createdBy) : null;
389
+ const name = segments[segments.length - 1] ?? rootType;
390
+ const tagId = makeStableId("tag", tagPath);
391
+ statements.insertTag.run(tagId, rootType, tagPath, name, parentId, name, createdBy, new Date().toISOString(), new Date().toISOString());
392
+ tagCache.set(tagPath, tagId);
393
+ this.tagIdCache.set(tagPath, tagId);
394
+ return tagId;
395
+ }
396
+ cleanupOrphanTagsInConnection(db) {
397
+ const selectOrphans = db.prepare(`
398
+ SELECT t.id, t.path
399
+ FROM tags t
400
+ WHERE NOT EXISTS (
401
+ SELECT 1 FROM tags child WHERE child.parent_id = t.id
402
+ )
403
+ AND NOT EXISTS (
404
+ SELECT 1 FROM document_tags dt WHERE dt.tag_id = t.id
405
+ )
406
+ AND NOT EXISTS (
407
+ SELECT 1 FROM derived_document_tags ddt WHERE ddt.tag_id = t.id
408
+ )
409
+ AND NOT EXISTS (
410
+ SELECT 1 FROM tag_aliases ta WHERE ta.tag_id = t.id
411
+ )
412
+ AND NOT EXISTS (
413
+ SELECT 1 FROM tag_rules tr WHERE tr.tag_id = t.id
414
+ )
415
+ AND NOT EXISTS (
416
+ SELECT 1 FROM manual_document_tag_bindings mdtb WHERE mdtb.tag_id = t.id
417
+ )
418
+ AND NOT EXISTS (
419
+ SELECT 1 FROM manual_file_tag_bindings mftb WHERE mftb.tag_id = t.id
420
+ )
421
+ AND NOT EXISTS (
422
+ SELECT 1 FROM folder_tag_bindings ftb WHERE ftb.tag_id = t.id
423
+ )
424
+ `);
425
+ const deleteTag = db.prepare(`DELETE FROM tags WHERE id = ?`);
426
+ while (true) {
427
+ const orphanRows = selectOrphans.all();
428
+ if (orphanRows.length === 0) {
429
+ return;
430
+ }
431
+ for (const row of orphanRows) {
432
+ deleteTag.run(row.id);
433
+ this.invalidateCachedTagPath(row.path);
434
+ }
435
+ }
436
+ }
437
+ buildDocumentIdentityFingerprint(file, document) {
438
+ return {
439
+ inodeKey: normalizeFileIdentityValue(file.inodeKey),
440
+ contentHash: buildDocumentContentHash(document.text),
441
+ };
442
+ }
443
+ resolveMigrationCandidateInConnection(statements, file, fingerprint, observedAt) {
444
+ if (!fingerprint.inodeKey && !fingerprint.contentHash) {
445
+ return null;
446
+ }
447
+ const rows = statements.selectUnseenIdentityCandidates.all(file.relativePath, observedAt, fingerprint.inodeKey, fingerprint.inodeKey, fingerprint.contentHash, fingerprint.contentHash, file.size, file.extension, fingerprint.inodeKey, fingerprint.inodeKey);
448
+ if (rows.length === 0) {
449
+ return null;
450
+ }
451
+ const candidates = rows.map((row) => ({
452
+ fileId: String(row.file_id),
453
+ path: String(row.path),
454
+ documentId: String(row.document_id),
455
+ inodeKey: normalizeFileIdentityValue(row.inode_key),
456
+ contentHash: typeof row.content_hash === "string" && row.content_hash.trim() ? row.content_hash : null,
457
+ size: Number(row.size ?? 0),
458
+ extension: String(row.extension ?? ""),
459
+ }));
460
+ const inodeMatches = fingerprint.inodeKey
461
+ ? candidates.filter((candidate) => candidate.inodeKey === fingerprint.inodeKey)
462
+ : [];
463
+ if (inodeMatches.length === 1) {
464
+ return inodeMatches[0];
465
+ }
466
+ if (inodeMatches.length > 1) {
467
+ return null;
468
+ }
469
+ const contentMatches = fingerprint.contentHash
470
+ ? candidates.filter((candidate) => candidate.contentHash === fingerprint.contentHash
471
+ && candidate.size === file.size
472
+ && candidate.extension === file.extension
473
+ && !doesSiblingPathStillExist(file, candidate.path))
474
+ : [];
475
+ if (contentMatches.length === 1) {
476
+ return contentMatches[0];
477
+ }
478
+ return null;
479
+ }
480
+ migrateManualBindingsInConnection(statements, previousDocumentId, nextDocumentId, observedAt) {
481
+ if (!previousDocumentId || !nextDocumentId || previousDocumentId === nextDocumentId) {
482
+ return;
483
+ }
484
+ const bindingRows = statements.selectManualBindingsByDocumentId.all(previousDocumentId);
485
+ bindingRows.forEach((row) => {
486
+ const tagId = String(row.tag_id);
487
+ statements.insertManualDocumentBinding.run(makeStableId("manual_binding", `${nextDocumentId}:${tagId}`), nextDocumentId, tagId, String(row.source ?? "manual_document"), String(row.created_at ?? observedAt), observedAt);
488
+ statements.deleteManualBindingByPair.run(previousDocumentId, tagId);
489
+ });
490
+ const manualTagRows = statements.selectManualDocumentTagsByDocumentId.all(previousDocumentId);
491
+ manualTagRows.forEach((row) => {
492
+ const tagId = String(row.tag_id);
493
+ statements.upsertDocumentTag.run(makeStableId("doc_tag", `${nextDocumentId}:${tagId}`), nextDocumentId, tagId, Number(row.confidence ?? 1), "manual_document", typeof row.source_ref === "string" ? row.source_ref : null, typeof row.evidence === "string" ? row.evidence : "手动分配", Number(row.manual_override ?? 1) ? 1 : 0, observedAt);
494
+ });
495
+ }
496
+ deleteManualFileBindingsForTargetInConnection(statements, target) {
497
+ const existingRows = statements.selectManualFileBindingsForIdentity.all(target.inodeKey, target.inodeKey, target.contentHash, target.contentHash, target.size, target.extension);
498
+ existingRows.forEach((row) => {
499
+ statements.deleteManualFileBindingById.run(String(row.id));
500
+ });
501
+ }
502
+ buildManualBindingTarget(file, fingerprint, documentId) {
503
+ return {
504
+ documentId,
505
+ inodeKey: fingerprint.inodeKey,
506
+ contentHash: fingerprint.contentHash,
507
+ size: file.size,
508
+ extension: file.extension,
509
+ };
510
+ }
511
+ getActiveManualBindingTargetByPathInConnection(statements, relativePath) {
512
+ const row = statements.selectActiveFileIdentityByPath.get(relativePath);
513
+ if (!row?.document_id) {
514
+ return null;
515
+ }
516
+ return {
517
+ documentId: String(row.document_id),
518
+ inodeKey: normalizeFileIdentityValue(row.inode_key),
519
+ contentHash: typeof row.content_hash === "string" && row.content_hash.trim() ? String(row.content_hash) : null,
520
+ size: Number(row.size ?? 0),
521
+ extension: String(row.extension ?? ""),
522
+ };
523
+ }
524
+ listManualFileBindingRowsForIdentityInConnection(statements, target) {
525
+ if (!target.inodeKey && !target.contentHash) {
526
+ return [];
527
+ }
528
+ const rows = statements.selectManualFileBindingRowsForIdentity.all(target.inodeKey, target.inodeKey, target.contentHash, target.contentHash, target.size, target.extension);
529
+ return rows.map((row) => ({
530
+ id: String(row.id),
531
+ tagId: String(row.tag_id),
532
+ source: String(row.source ?? "manual_document"),
533
+ createdAt: String(row.created_at),
534
+ updatedAt: String(row.updated_at),
535
+ }));
536
+ }
537
+ carryForwardManualFileBindingsForSameDocumentInConnection(statements, previousTarget, nextTarget, observedAt) {
538
+ if (!previousTarget || previousTarget.documentId !== nextTarget.documentId) {
539
+ return;
540
+ }
541
+ if (hasSameManualBindingIdentity(previousTarget, nextTarget)) {
542
+ return;
543
+ }
544
+ const existingNextRows = this.listManualFileBindingRowsForIdentityInConnection(statements, nextTarget);
545
+ if (existingNextRows.length > 0) {
546
+ return;
547
+ }
548
+ const previousRows = this.listManualFileBindingRowsForIdentityInConnection(statements, previousTarget);
549
+ if (previousRows.length === 0) {
550
+ return;
551
+ }
552
+ previousRows.forEach((row) => {
553
+ statements.insertManualFileBinding.run(makeStableId("manual_file_binding", serializeManualFileBindingIdentity(nextTarget, row.tagId)), nextTarget.inodeKey, nextTarget.contentHash, nextTarget.size, nextTarget.extension, row.tagId, row.source, row.createdAt, observedAt);
554
+ statements.deleteManualFileBindingById.run(row.id);
555
+ });
556
+ }
557
+ resolveManualFileBindingsForTargetInConnection(db, target) {
558
+ if (!target.inodeKey && !target.contentHash) {
559
+ return [];
560
+ }
561
+ const candidateRows = db.prepare(`
562
+ SELECT id, inode_key, content_hash, file_size, extension, tag_id, source, created_at, updated_at
563
+ FROM manual_file_tag_bindings
564
+ WHERE (? IS NOT NULL AND inode_key = ?)
565
+ OR (? IS NOT NULL AND content_hash = ? AND file_size = ? AND extension = ?)
566
+ ORDER BY updated_at DESC, id
567
+ `).all(target.inodeKey, target.inodeKey, target.contentHash, target.contentHash, target.size, target.extension);
568
+ if (candidateRows.length === 0) {
569
+ return [];
570
+ }
571
+ const candidateInodeKeys = [...new Set(candidateRows
572
+ .map((row) => normalizeFileIdentityValue(row.inode_key))
573
+ .filter((value) => Boolean(value)))];
574
+ const candidateContentHashes = [...new Set(candidateRows
575
+ .map((row) => typeof row.content_hash === "string" && row.content_hash.trim() ? String(row.content_hash) : null)
576
+ .filter((value) => Boolean(value)))];
577
+ const activeIdentityRows = this.listActiveDocumentIdentityRowsInConnection(db, candidateInodeKeys, candidateContentHashes);
578
+ const activeDocIdsByInode = buildIdentityDocumentIdsByInode(activeIdentityRows);
579
+ const activeDocIdsByContent = buildIdentityDocumentIdsByContent(activeIdentityRows);
580
+ const targetContentKey = buildIdentityContentKey(target.contentHash, target.size, target.extension);
581
+ return candidateRows
582
+ .filter((row) => {
583
+ const candidateInodeKey = normalizeFileIdentityValue(row.inode_key);
584
+ if (candidateInodeKey && target.inodeKey && candidateInodeKey === target.inodeKey) {
585
+ return true;
586
+ }
587
+ const candidateContentKey = buildIdentityContentKey(typeof row.content_hash === "string" && row.content_hash.trim() ? String(row.content_hash) : null, Number(row.file_size ?? 0), String(row.extension ?? ""));
588
+ if (!candidateContentKey || !targetContentKey || candidateContentKey !== targetContentKey) {
589
+ return false;
590
+ }
591
+ const contentMatches = activeDocIdsByContent.get(candidateContentKey);
592
+ if (contentMatches !== 1) {
593
+ return false;
594
+ }
595
+ if (!candidateInodeKey) {
596
+ return true;
597
+ }
598
+ return !activeDocIdsByInode.has(candidateInodeKey);
599
+ })
600
+ .map((row) => ({
601
+ id: String(row.id),
602
+ tagId: String(row.tag_id),
603
+ source: String(row.source ?? "manual_document"),
604
+ createdAt: String(row.created_at),
605
+ updatedAt: String(row.updated_at),
606
+ }));
607
+ }
608
+ listActiveDocumentIdentityRowsInConnection(db, inodeKeys, contentHashes) {
609
+ if (inodeKeys.length === 0 && contentHashes.length === 0) {
610
+ return [];
611
+ }
612
+ const predicateParts = [];
613
+ const params = [];
614
+ if (inodeKeys.length > 0) {
615
+ predicateParts.push(`f.inode_key IN (${inodeKeys.map(() => "?").join(", ")})`);
616
+ params.push(...inodeKeys);
617
+ }
618
+ if (contentHashes.length > 0) {
619
+ predicateParts.push(`f.content_hash IN (${contentHashes.map(() => "?").join(", ")})`);
620
+ params.push(...contentHashes);
621
+ }
622
+ const rows = db.prepare(`
623
+ SELECT f.inode_key, f.content_hash, f.size, f.extension
624
+ FROM documents d
625
+ JOIN files f ON f.id = d.file_id
626
+ WHERE f.status = 'active'
627
+ AND d.index_status = 'indexed'
628
+ AND (${predicateParts.join(" OR ")})
629
+ `).all(...params);
630
+ return rows.map((row) => ({
631
+ inodeKey: normalizeFileIdentityValue(row.inode_key),
632
+ contentHash: typeof row.content_hash === "string" && row.content_hash.trim() ? String(row.content_hash) : null,
633
+ size: Number(row.size ?? 0),
634
+ extension: String(row.extension ?? ""),
635
+ }));
636
+ }
637
+ syncManualResolvedTagsForDocumentInConnection(db, statements, target, observedAt) {
638
+ const existingManualTagRows = statements.selectManualDocumentTagsByDocumentId.all(target.documentId);
639
+ this.backfillManualFileBindingsFromLegacyDocumentBindingsInConnection(statements, target, observedAt);
640
+ statements.deleteDocumentTagByDocumentAndSource.run(target.documentId, "manual_document");
641
+ let manualBindings = this.resolveManualFileBindingsForTargetInConnection(db, target);
642
+ if (manualBindings.length === 0 && existingManualTagRows.length > 0 && (target.inodeKey || target.contentHash)) {
643
+ existingManualTagRows.forEach((row) => {
644
+ const tagId = String(row.tag_id);
645
+ statements.insertManualFileBinding.run(makeStableId("manual_file_binding", serializeManualFileBindingIdentity(target, tagId)), target.inodeKey, target.contentHash, target.size, target.extension, tagId, "manual_document", String(row.updated_at ?? observedAt), observedAt);
646
+ });
647
+ manualBindings = this.resolveManualFileBindingsForTargetInConnection(db, target);
648
+ }
649
+ manualBindings.forEach((binding) => {
650
+ statements.upsertDocumentTag.run(makeStableId("doc_tag", `${target.documentId}:${binding.tagId}`), target.documentId, binding.tagId, 1, "manual_document", binding.id, "手动分配", 1, observedAt);
651
+ });
652
+ }
653
+ backfillManualFileBindingsFromLegacyDocumentBindingsInConnection(statements, target, observedAt) {
654
+ if (!target.inodeKey && !target.contentHash) {
655
+ return;
656
+ }
657
+ const existingIdentityRows = statements.selectManualFileBindingsForIdentity.all(target.inodeKey, target.inodeKey, target.contentHash, target.contentHash, target.size, target.extension);
658
+ if (existingIdentityRows.length > 0) {
659
+ return;
660
+ }
661
+ const legacyRows = statements.selectManualBindingsByDocumentId.all(target.documentId);
662
+ legacyRows.forEach((row) => {
663
+ const tagId = String(row.tag_id);
664
+ statements.insertManualFileBinding.run(makeStableId("manual_file_binding", serializeManualFileBindingIdentity(target, tagId)), target.inodeKey, target.contentHash, target.size, target.extension, tagId, String(row.source ?? "manual_document"), String(row.created_at ?? observedAt), observedAt);
665
+ });
666
+ }
667
+ deleteDocumentInConnection(db, statements, relativePath, deletedAt) {
668
+ const normalizedPath = this.normalizeRelativePath(relativePath);
669
+ const fileRow = statements.selectFileByPath.get(normalizedPath);
670
+ if (!fileRow?.id) {
671
+ return false;
672
+ }
673
+ const documentRow = statements.selectDocumentByFileId.get(fileRow.id);
674
+ if (documentRow?.id) {
675
+ statements.deleteChunksByDocumentId.run(documentRow.id);
676
+ statements.deleteManualDocumentBindingsByDocumentId.run(documentRow.id);
677
+ statements.deleteDocumentTags.run(documentRow.id);
678
+ statements.deleteDerivedDocumentTags.run(documentRow.id);
679
+ statements.deleteDocumentById.run(documentRow.id);
680
+ }
681
+ statements.markFileDeleted.run(deletedAt, fileRow.id);
682
+ return true;
683
+ }
684
+ upsertDocumentInConnection(db, statements, tagCache, file, document, tags = [], derivedTags = [], observedAt = new Date().toISOString()) {
685
+ const fileId = makeStableId("file", file.relativePath);
686
+ const documentId = makeStableId("doc", file.relativePath);
687
+ const fingerprint = this.buildDocumentIdentityFingerprint(file, document);
688
+ const previousManualBindingTarget = this.getActiveManualBindingTargetByPathInConnection(statements, file.relativePath);
689
+ const migrationCandidate = this.resolveMigrationCandidateInConnection(statements, file, fingerprint, observedAt);
690
+ const manualBindingTarget = this.buildManualBindingTarget(file, fingerprint, documentId);
691
+ statements.upsertFile.run(fileId, file.relativePath, file.relativePath.includes("/") ? file.relativePath.slice(0, file.relativePath.lastIndexOf("/")) : ".", file.name, file.extension, file.size, file.mtime, file.ctime, fingerprint.inodeKey, fingerprint.contentHash, observedAt);
692
+ statements.upsertDocument.run(documentId, fileId, document.title, document.summary, "parsed", null, "indexed", document.text.trim() ? 1 : 0, observedAt);
693
+ if (migrationCandidate) {
694
+ this.migrateManualBindingsInConnection(statements, migrationCandidate.documentId, documentId, observedAt);
695
+ }
696
+ this.carryForwardManualFileBindingsForSameDocumentInConnection(statements, previousManualBindingTarget, manualBindingTarget, observedAt);
697
+ this.syncManualResolvedTagsForDocumentInConnection(db, statements, manualBindingTarget, observedAt);
698
+ statements.deleteChunksByDocumentId.run(documentId);
699
+ if (document.text.trim()) {
700
+ statements.insertChunk.run(makeStableId("chunk", `${documentId}:0`), documentId, 0, document.text);
701
+ }
702
+ if (this.activeBootstrapSession) {
703
+ for (const tag of tags) {
704
+ const tagId = this.ensureTagInConnection(db, statements, tagCache, tag.tagPath, tag.source.split("+")[0] || "rule");
705
+ statements.insertDocumentTag.run(makeStableId("doc_tag", `${documentId}:${tagId}`), documentId, tagId, tag.confidence, tag.source, null, tag.evidence, tag.manualOverride ? 1 : 0, observedAt);
706
+ }
707
+ for (const tag of derivedTags) {
708
+ const tagId = this.ensureTagInConnection(db, statements, tagCache, tag.tagPath, tag.source);
709
+ statements.insertDerivedTag.run(makeStableId("derived_tag", `${documentId}:${tagId}`), documentId, tagId, "system_derived", null, tag.source, tag.evidence, observedAt, observedAt);
710
+ }
711
+ return { fileId, documentId };
712
+ }
713
+ for (const tag of tags) {
714
+ const tagId = this.ensureTagInConnection(db, statements, tagCache, tag.tagPath, tag.source.split("+")[0] || "rule");
715
+ statements.upsertDocumentTag.run(makeStableId("doc_tag", `${documentId}:${tagId}`), documentId, tagId, tag.confidence, tag.source, null, tag.evidence, tag.manualOverride ? 1 : 0, observedAt);
716
+ }
717
+ const existingDerivedTagRows = statements.selectDerivedTagIds.all(documentId);
718
+ const existingDerivedTagIds = new Set(existingDerivedTagRows.map(row => String(row.tag_id)));
719
+ const nextDerivedTagIds = new Set();
720
+ for (const tag of derivedTags) {
721
+ const tagId = this.ensureTagInConnection(db, statements, tagCache, tag.tagPath, tag.source);
722
+ nextDerivedTagIds.add(tagId);
723
+ statements.upsertDerivedTag.run(makeStableId("derived_tag", `${documentId}:${tagId}`), documentId, tagId, "system_derived", null, tag.source, tag.evidence, observedAt, observedAt);
724
+ }
725
+ for (const tagId of existingDerivedTagIds) {
726
+ if (!nextDerivedTagIds.has(tagId)) {
727
+ statements.deleteDerivedDocumentTagByPair.run(documentId, tagId);
728
+ }
729
+ }
730
+ return { fileId, documentId };
731
+ }
732
+ upsertParseFailureInConnection(db, statements, file, error, observedAt = new Date().toISOString()) {
733
+ const fileId = makeStableId("file", file.relativePath);
734
+ const documentId = makeStableId("doc", file.relativePath);
735
+ const inodeKey = normalizeFileIdentityValue(file.inodeKey);
736
+ statements.upsertFile.run(fileId, file.relativePath, file.relativePath.includes("/") ? file.relativePath.slice(0, file.relativePath.lastIndexOf("/")) : ".", file.name, file.extension, file.size, file.mtime, file.ctime, inodeKey, null, observedAt);
737
+ statements.upsertDocument.run(documentId, fileId, file.name, "", "failed", error.message, "failed", 0, observedAt);
738
+ statements.deleteChunksByDocumentId.run(documentId);
739
+ statements.deleteDocumentTags.run(documentId);
740
+ statements.deleteDerivedDocumentTags.run(documentId);
741
+ return { fileId, documentId };
742
+ }
743
+ markSkippedDocumentInConnection(db, statements, entry, observedAt = new Date().toISOString()) {
744
+ const { file, adapter, reasonCode, message } = entry;
745
+ const fileId = makeStableId("file", file.relativePath);
746
+ const documentId = makeStableId("doc", file.relativePath);
747
+ const inodeKey = normalizeFileIdentityValue(file.inodeKey);
748
+ statements.upsertFile.run(fileId, file.relativePath, file.relativePath.includes("/") ? file.relativePath.slice(0, file.relativePath.lastIndexOf("/")) : ".", file.name, file.extension, file.size, file.mtime, file.ctime, inodeKey, null, observedAt);
749
+ statements.upsertDocument.run(documentId, fileId, file.name, "", "skipped", `${reasonCode}: ${adapter}${message ? ` - ${message}` : ""}`, "skipped", 0, observedAt);
750
+ statements.deleteChunksByDocumentId.run(documentId);
751
+ statements.deleteDocumentTags.run(documentId);
752
+ statements.deleteDerivedDocumentTags.run(documentId);
753
+ statements.deleteChunksByDocumentId.run(documentId);
754
+ return { fileId, documentId };
755
+ }
756
+ upsertTextDocument(file, parsed, tags = [], derivedTags = [], observedAt) {
757
+ return this.withConnection((db, statements) => {
758
+ const tagCache = new Map(this.tagIdCache);
759
+ try {
760
+ db.exec("BEGIN");
761
+ const result = this.upsertDocumentInConnection(db, statements, tagCache, file, parsed, tags, derivedTags, observedAt);
762
+ db.exec("COMMIT");
763
+ return result;
764
+ }
765
+ catch (error) {
766
+ db.exec("ROLLBACK");
767
+ throw error;
768
+ }
769
+ });
770
+ }
771
+ upsertParseFailure(file, error, observedAt) {
772
+ return this.withConnection((db, statements) => {
773
+ try {
774
+ db.exec("BEGIN");
775
+ const result = this.upsertParseFailureInConnection(db, statements, file, error, observedAt);
776
+ this.cleanupOrphanTagsInConnection(db);
777
+ db.exec("COMMIT");
778
+ return result;
779
+ }
780
+ catch (failure) {
781
+ db.exec("ROLLBACK");
782
+ throw failure;
783
+ }
784
+ });
785
+ }
786
+ batchUpsertDocuments(entries, observedAt) {
787
+ if (entries.length === 0) {
788
+ return [];
789
+ }
790
+ return this.withConnection((db, statements) => {
791
+ const tagCache = new Map(this.tagIdCache);
792
+ try {
793
+ db.exec("BEGIN IMMEDIATE");
794
+ const results = entries.map(entry => this.upsertDocumentInConnection(db, statements, tagCache, entry.file, entry.document, entry.tags, entry.derivedTags, observedAt));
795
+ db.exec("COMMIT");
796
+ return results;
797
+ }
798
+ catch (error) {
799
+ db.exec("ROLLBACK");
800
+ throw error;
801
+ }
802
+ });
803
+ }
804
+ batchUpsertParseFailures(entries, observedAt) {
805
+ if (entries.length === 0) {
806
+ return [];
807
+ }
808
+ return this.withConnection((db, statements) => {
809
+ try {
810
+ db.exec("BEGIN IMMEDIATE");
811
+ const results = entries.map(entry => this.upsertParseFailureInConnection(db, statements, entry.file, entry.error, observedAt));
812
+ db.exec("COMMIT");
813
+ return results;
814
+ }
815
+ catch (error) {
816
+ db.exec("ROLLBACK");
817
+ throw error;
818
+ }
819
+ });
820
+ }
821
+ batchMarkSkippedDocuments(entries, observedAt) {
822
+ if (entries.length === 0) {
823
+ return [];
824
+ }
825
+ return this.withConnection((db, statements) => {
826
+ try {
827
+ db.exec("BEGIN IMMEDIATE");
828
+ const results = entries.map(entry => this.markSkippedDocumentInConnection(db, statements, entry, observedAt));
829
+ db.exec("COMMIT");
830
+ return results;
831
+ }
832
+ catch (error) {
833
+ db.exec("ROLLBACK");
834
+ throw error;
835
+ }
836
+ });
837
+ }
838
+ cleanupOrphanTags() {
839
+ this.withConnection(db => {
840
+ try {
841
+ db.exec("BEGIN IMMEDIATE");
842
+ this.cleanupOrphanTagsInConnection(db);
843
+ db.exec("COMMIT");
844
+ }
845
+ catch (error) {
846
+ db.exec("ROLLBACK");
847
+ throw error;
848
+ }
849
+ });
850
+ }
851
+ getActiveIndexedFileState(relativePath) {
852
+ return this.withConnection((_, statements) => {
853
+ const normalizedPath = this.normalizeRelativePath(relativePath);
854
+ const row = statements.selectActiveIndexedFileStateByPath.get(normalizedPath);
855
+ if (!row?.path) {
856
+ return null;
857
+ }
858
+ return {
859
+ path: String(row.path),
860
+ extension: String(row.extension ?? ""),
861
+ size: Number(row.size ?? 0),
862
+ mtime: String(row.mtime ?? ""),
863
+ indexStatus: String(row.index_status ?? ""),
864
+ };
865
+ });
866
+ }
867
+ reconcileScope(scope, observedAt, options = {}) {
868
+ return this.withConnection((db, statements) => {
869
+ const now = new Date().toISOString();
870
+ try {
871
+ db.exec("BEGIN IMMEDIATE");
872
+ let rows = [];
873
+ if (scope.kind === "exact" && scope.value) {
874
+ rows = statements.listActiveFilesExact.all(this.normalizeRelativePath(scope.value));
875
+ }
876
+ else if (scope.kind === "prefix" && scope.value) {
877
+ const normalizedPrefix = this.normalizeRelativePath(scope.value).replace(/\/+$/, "");
878
+ rows = statements.listActiveFilesPrefix.all(normalizedPrefix, `${normalizedPrefix}/%`);
879
+ }
880
+ else {
881
+ rows = statements.listActiveFilesAll.all();
882
+ }
883
+ const deletedPaths = [];
884
+ for (const row of rows) {
885
+ if (options.seenPaths?.has(row.path) || row.last_seen_at === observedAt) {
886
+ continue;
887
+ }
888
+ if (this.deleteDocumentInConnection(db, statements, row.path, now)) {
889
+ deletedPaths.push(row.path);
890
+ }
891
+ }
892
+ this.cleanupOrphanTagsInConnection(db);
893
+ db.exec("COMMIT");
894
+ return {
895
+ deletedCount: deletedPaths.length,
896
+ deletedPaths,
897
+ };
898
+ }
899
+ catch (error) {
900
+ db.exec("ROLLBACK");
901
+ throw error;
902
+ }
903
+ });
904
+ }
905
+ deleteActiveFilesByExtensions(extensions, deletedAt = new Date().toISOString()) {
906
+ const normalizedExtensions = [...new Set(extensions
907
+ .map(item => item.trim().toLowerCase())
908
+ .filter(Boolean)
909
+ .map(item => item.startsWith(".") ? item : `.${item}`))];
910
+ if (normalizedExtensions.length === 0) {
911
+ return {
912
+ deletedCount: 0,
913
+ deletedPaths: [],
914
+ };
915
+ }
916
+ return this.withConnection((db, statements) => {
917
+ try {
918
+ db.exec("BEGIN IMMEDIATE");
919
+ const placeholders = normalizedExtensions.map(() => "?").join(", ");
920
+ const rows = db.prepare(`
921
+ SELECT path
922
+ FROM files
923
+ WHERE status = 'active'
924
+ AND extension IN (${placeholders})
925
+ ORDER BY path
926
+ `).all(...normalizedExtensions);
927
+ const deletedPaths = [];
928
+ for (const row of rows) {
929
+ if (this.deleteDocumentInConnection(db, statements, row.path, deletedAt)) {
930
+ deletedPaths.push(String(row.path));
931
+ }
932
+ }
933
+ this.cleanupOrphanTagsInConnection(db);
934
+ db.exec("COMMIT");
935
+ return {
936
+ deletedCount: deletedPaths.length,
937
+ deletedPaths,
938
+ };
939
+ }
940
+ catch (error) {
941
+ db.exec("ROLLBACK");
942
+ throw error;
943
+ }
944
+ });
945
+ }
946
+ recomputeDocumentTags(entries, observedAt = new Date().toISOString()) {
947
+ if (entries.length === 0) {
948
+ return { updatedCount: 0 };
949
+ }
950
+ return this.withConnection((db, statements) => {
951
+ const tagCache = new Map(this.tagIdCache);
952
+ try {
953
+ db.exec("BEGIN IMMEDIATE");
954
+ for (const entry of entries) {
955
+ statements.deleteDocumentTags.run(entry.documentId);
956
+ statements.deleteDerivedDocumentTags.run(entry.documentId);
957
+ for (const tag of entry.tags) {
958
+ const tagId = this.ensureTagInConnection(db, statements, tagCache, tag.tagPath, tag.source.split("+")[0] || "rule");
959
+ statements.upsertDocumentTag.run(makeStableId("doc_tag", `${entry.documentId}:${tagId}`), entry.documentId, tagId, tag.confidence, tag.source, null, tag.evidence, tag.manualOverride ? 1 : 0, observedAt);
960
+ }
961
+ for (const tag of entry.derivedTags) {
962
+ const tagId = this.ensureTagInConnection(db, statements, tagCache, tag.tagPath, tag.source);
963
+ statements.upsertDerivedTag.run(makeStableId("derived_tag", `${entry.documentId}:${tagId}`), entry.documentId, tagId, "system_derived", null, tag.source, tag.evidence, observedAt, observedAt);
964
+ }
965
+ }
966
+ this.cleanupOrphanTagsInConnection(db);
967
+ db.exec("COMMIT");
968
+ return { updatedCount: entries.length };
969
+ }
970
+ catch (error) {
971
+ db.exec("ROLLBACK");
972
+ throw error;
973
+ }
974
+ });
975
+ }
976
+ saveTagDefinition(input, observedAt = new Date().toISOString()) {
977
+ return this.withConnection((db, statements) => {
978
+ try {
979
+ db.exec("BEGIN IMMEDIATE");
980
+ const tagId = input.id?.trim() || makeStableId("tag", input.path);
981
+ const disabledAt = input.status === "disabled" ? observedAt : null;
982
+ const existing = statements.selectTagById.get(tagId);
983
+ if (existing?.id) {
984
+ statements.updateTagDefinition.run(input.rootType, input.path, input.name, input.parentId ?? null, input.canonicalName ?? input.name, input.description ?? null, input.status, observedAt, disabledAt, tagId);
985
+ if (typeof existing.path === "string" && existing.path && existing.path !== input.path) {
986
+ this.tagIdCache.delete(existing.path);
987
+ }
988
+ }
989
+ else {
990
+ db.prepare(`
991
+ INSERT INTO tags(id, root_type, path, name, parent_id, canonical_name, description, status, created_by, created_at, updated_at, disabled_at)
992
+ VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
993
+ `).run(tagId, input.rootType, input.path, input.name, input.parentId ?? null, input.canonicalName ?? input.name, input.description ?? null, input.status, input.createdBy, observedAt, observedAt, disabledAt);
994
+ }
995
+ this.tagIdCache.set(input.path, tagId);
996
+ db.exec("COMMIT");
997
+ return { id: tagId };
998
+ }
999
+ catch (error) {
1000
+ db.exec("ROLLBACK");
1001
+ throw error;
1002
+ }
1003
+ });
1004
+ }
1005
+ replaceTagRules(tagId, rules, observedAt = new Date().toISOString()) {
1006
+ const normalizedTagId = tagId.trim();
1007
+ if (!normalizedTagId) {
1008
+ return;
1009
+ }
1010
+ this.withConnection((db, statements) => {
1011
+ try {
1012
+ db.exec("BEGIN IMMEDIATE");
1013
+ statements.deleteTagRulesByTagId.run(normalizedTagId);
1014
+ rules
1015
+ .filter(rule => Number.isFinite(rule.priority))
1016
+ .sort((left, right) => left.priority - right.priority)
1017
+ .forEach((rule, index) => {
1018
+ const priority = Number.isFinite(rule.priority) ? rule.priority : index;
1019
+ const relation = rule.relation === "or" || rule.relation === "not" ? rule.relation : "and";
1020
+ const scopeJson = JSON.stringify({ relation });
1021
+ const matcherJson = JSON.stringify(rule.matcher ?? {});
1022
+ const ruleId = makeStableId("tag_rule", `${normalizedTagId}:${priority}:${rule.ruleType}:${matcherJson}:${relation}`);
1023
+ statements.insertTagRule.run(ruleId, normalizedTagId, rule.enabled ? 1 : 0, rule.ruleType, scopeJson, matcherJson, null, priority, "smart_rule", observedAt, observedAt);
1024
+ });
1025
+ db.exec("COMMIT");
1026
+ }
1027
+ catch (error) {
1028
+ db.exec("ROLLBACK");
1029
+ throw error;
1030
+ }
1031
+ });
1032
+ }
1033
+ replaceManualDocumentTagBindings(target, tagIds, observedAt = new Date().toISOString()) {
1034
+ const normalizedTagIds = [...new Set(tagIds.map(item => item.trim()).filter(Boolean))];
1035
+ this.withConnection((db, statements) => {
1036
+ try {
1037
+ db.exec("BEGIN IMMEDIATE");
1038
+ // 旧 manual_document_tag_bindings 现在只保留给历史数据兼容。
1039
+ // 新写入统一只进 manual_file_tag_bindings,避免继续把 document_id 绑定当主链路。
1040
+ statements.deleteManualDocumentBindingsByDocumentId.run(target.documentId);
1041
+ this.deleteManualFileBindingsForTargetInConnection(statements, target);
1042
+ normalizedTagIds.forEach(tagId => {
1043
+ statements.insertManualFileBinding.run(makeStableId("manual_file_binding", serializeManualFileBindingIdentity(target, tagId)), target.inodeKey, target.contentHash, target.size, target.extension, tagId, "manual_document", observedAt, observedAt);
1044
+ });
1045
+ db.exec("COMMIT");
1046
+ }
1047
+ catch (error) {
1048
+ db.exec("ROLLBACK");
1049
+ throw error;
1050
+ }
1051
+ });
1052
+ }
1053
+ replaceFolderTagBindings(folderPath, tagIds, observedAt = new Date().toISOString()) {
1054
+ const normalizedFolderPath = this.normalizeRelativePath(folderPath).replace(/^\.\/+/, "").replace(/\/+$/g, "") || ".";
1055
+ const normalizedTagIds = [...new Set(tagIds.map(item => item.trim()).filter(Boolean))];
1056
+ this.withConnection((db, statements) => {
1057
+ try {
1058
+ db.exec("BEGIN IMMEDIATE");
1059
+ statements.deleteFolderBindingsByFolderPath.run(normalizedFolderPath);
1060
+ normalizedTagIds.forEach(tagId => {
1061
+ statements.insertFolderBinding.run(makeStableId("folder_binding", `${normalizedFolderPath}:${tagId}:descendant_files`), normalizedFolderPath, tagId, "descendant_files", observedAt, observedAt);
1062
+ });
1063
+ db.exec("COMMIT");
1064
+ }
1065
+ catch (error) {
1066
+ db.exec("ROLLBACK");
1067
+ throw error;
1068
+ }
1069
+ });
1070
+ }
1071
+ deleteTagDefinitions(tagIds) {
1072
+ const normalizedTagIds = [...new Set(tagIds.map(item => item.trim()).filter(Boolean))];
1073
+ if (normalizedTagIds.length === 0) {
1074
+ return;
1075
+ }
1076
+ this.withConnection((db, statements) => {
1077
+ try {
1078
+ db.exec("BEGIN IMMEDIATE");
1079
+ const selectTagPath = db.prepare(`SELECT path FROM tags WHERE id = ?`);
1080
+ const deleteManualBindings = db.prepare(`DELETE FROM manual_document_tag_bindings WHERE tag_id = ?`);
1081
+ const deleteManualFileBindings = statements.deleteManualFileBindingsByTagId;
1082
+ const deleteFolderBindings = db.prepare(`DELETE FROM folder_tag_bindings WHERE tag_id = ?`);
1083
+ const deleteRules = db.prepare(`DELETE FROM tag_rules WHERE tag_id = ?`);
1084
+ const deleteDocumentTags = db.prepare(`DELETE FROM document_tags WHERE tag_id = ?`);
1085
+ const deleteDerivedTags = db.prepare(`DELETE FROM derived_document_tags WHERE tag_id = ?`);
1086
+ const deleteTag = db.prepare(`DELETE FROM tags WHERE id = ?`);
1087
+ normalizedTagIds.forEach((tagId) => {
1088
+ const current = selectTagPath.get(tagId);
1089
+ deleteManualBindings.run(tagId);
1090
+ deleteManualFileBindings.run(tagId);
1091
+ deleteFolderBindings.run(tagId);
1092
+ deleteRules.run(tagId);
1093
+ deleteDocumentTags.run(tagId);
1094
+ deleteDerivedTags.run(tagId);
1095
+ deleteTag.run(tagId);
1096
+ if (typeof current?.path === "string" && current.path) {
1097
+ this.tagIdCache.delete(current.path);
1098
+ }
1099
+ });
1100
+ db.exec("COMMIT");
1101
+ }
1102
+ catch (error) {
1103
+ db.exec("ROLLBACK");
1104
+ throw error;
1105
+ }
1106
+ });
1107
+ }
1108
+ deleteResolvedTagsBySource(documentId, sourceTypes) {
1109
+ const normalizedTypes = [...new Set(sourceTypes)];
1110
+ if (normalizedTypes.length === 0) {
1111
+ return;
1112
+ }
1113
+ this.withConnection((db, statements) => {
1114
+ try {
1115
+ db.exec("BEGIN IMMEDIATE");
1116
+ normalizedTypes.forEach(sourceType => {
1117
+ if (sourceType === "system_derived") {
1118
+ statements.deleteDerivedDocumentTagByDocumentAndSource.run(documentId, sourceType);
1119
+ }
1120
+ else {
1121
+ statements.deleteDocumentTagByDocumentAndSource.run(documentId, sourceType);
1122
+ }
1123
+ });
1124
+ db.exec("COMMIT");
1125
+ }
1126
+ catch (error) {
1127
+ db.exec("ROLLBACK");
1128
+ throw error;
1129
+ }
1130
+ });
1131
+ }
1132
+ recomputeResolvedTags(entries, observedAt = new Date().toISOString(), documentIds = []) {
1133
+ const normalizedDocumentIds = [...new Set([
1134
+ ...documentIds,
1135
+ ...entries.map(item => item.documentId),
1136
+ ].map(item => item.trim()).filter(Boolean))];
1137
+ if (normalizedDocumentIds.length === 0) {
1138
+ return { updatedCount: 0, updatedDocumentIds: [] };
1139
+ }
1140
+ return this.withConnection((db, statements) => {
1141
+ const tagCache = new Map(this.tagIdCache);
1142
+ const selectCurrentDocumentTags = db.prepare(`
1143
+ SELECT tag_id, confidence, source, source_ref, evidence, manual_override
1144
+ FROM document_tags
1145
+ WHERE document_id = ?
1146
+ ORDER BY tag_id, source, COALESCE(source_ref, ''), COALESCE(evidence, '')
1147
+ `);
1148
+ const selectCurrentDerivedTags = db.prepare(`
1149
+ SELECT tag_id, source, source_ref, rule_name, evidence
1150
+ FROM derived_document_tags
1151
+ WHERE document_id = ?
1152
+ ORDER BY tag_id, source, COALESCE(source_ref, ''), rule_name, COALESCE(evidence, '')
1153
+ `);
1154
+ try {
1155
+ db.exec("BEGIN IMMEDIATE");
1156
+ const groupedByDocument = new Map();
1157
+ entries.forEach(entry => {
1158
+ const current = groupedByDocument.get(entry.documentId) ?? [];
1159
+ current.push(entry);
1160
+ groupedByDocument.set(entry.documentId, current);
1161
+ });
1162
+ let updatedCount = 0;
1163
+ const updatedDocumentIds = [];
1164
+ for (const documentId of normalizedDocumentIds) {
1165
+ const documentEntries = groupedByDocument.get(documentId) ?? [];
1166
+ const nextDocumentTags = [];
1167
+ const nextDerivedTags = [];
1168
+ for (const entry of documentEntries) {
1169
+ const tagId = this.ensureTagInConnection(db, statements, tagCache, entry.tagPath, entry.sourceType);
1170
+ if (entry.sourceType === "system_derived") {
1171
+ nextDerivedTags.push({
1172
+ tag_id: tagId,
1173
+ source: entry.sourceType,
1174
+ source_ref: entry.sourceRef ?? null,
1175
+ rule_name: entry.sourceRef ?? "system_derived",
1176
+ evidence: entry.evidence ?? null,
1177
+ });
1178
+ }
1179
+ else {
1180
+ nextDocumentTags.push({
1181
+ tag_id: tagId,
1182
+ confidence: entry.confidence,
1183
+ source: entry.sourceType,
1184
+ source_ref: entry.sourceRef ?? null,
1185
+ evidence: entry.evidence ?? null,
1186
+ manual_override: entry.sourceType === "manual_document" ? 1 : 0,
1187
+ });
1188
+ }
1189
+ }
1190
+ nextDocumentTags.sort(compareCurrentResolvedDocumentTagRow);
1191
+ nextDerivedTags.sort(compareCurrentResolvedDerivedTagRow);
1192
+ const currentDocumentTags = selectCurrentDocumentTags.all(documentId);
1193
+ const currentDerivedTags = selectCurrentDerivedTags.all(documentId);
1194
+ if (areResolvedDocumentTagRowsEqual(currentDocumentTags, nextDocumentTags)
1195
+ && areResolvedDerivedTagRowsEqual(currentDerivedTags, nextDerivedTags)) {
1196
+ continue;
1197
+ }
1198
+ statements.deleteDocumentTags.run(documentId);
1199
+ statements.deleteDerivedDocumentTags.run(documentId);
1200
+ for (const nextDocumentTag of nextDocumentTags) {
1201
+ statements.upsertDocumentTag.run(makeStableId("doc_tag", `${documentId}:${nextDocumentTag.tag_id}`), documentId, nextDocumentTag.tag_id, nextDocumentTag.confidence, nextDocumentTag.source, nextDocumentTag.source_ref, nextDocumentTag.evidence, nextDocumentTag.manual_override, observedAt);
1202
+ }
1203
+ for (const nextDerivedTag of nextDerivedTags) {
1204
+ statements.upsertDerivedTag.run(makeStableId("derived_tag", `${documentId}:${nextDerivedTag.tag_id}`), documentId, nextDerivedTag.tag_id, nextDerivedTag.source, nextDerivedTag.source_ref, nextDerivedTag.rule_name, nextDerivedTag.evidence, observedAt, observedAt);
1205
+ }
1206
+ updatedCount += 1;
1207
+ updatedDocumentIds.push(documentId);
1208
+ }
1209
+ db.exec("COMMIT");
1210
+ return { updatedCount, updatedDocumentIds };
1211
+ }
1212
+ catch (error) {
1213
+ db.exec("ROLLBACK");
1214
+ throw error;
1215
+ }
1216
+ });
1217
+ }
1218
+ }
1219
+ function normalizeFileIdentityValue(value) {
1220
+ if (typeof value !== "string") {
1221
+ return null;
1222
+ }
1223
+ const normalized = value.trim();
1224
+ return normalized ? normalized : null;
1225
+ }
1226
+ function buildDocumentContentHash(text) {
1227
+ if (!text.trim()) {
1228
+ return null;
1229
+ }
1230
+ return crypto.createHash("sha1").update(text).digest("hex");
1231
+ }
1232
+ function compareCurrentResolvedDocumentTagRow(left, right) {
1233
+ return compareComparableTuples([
1234
+ left.tag_id,
1235
+ String(left.confidence),
1236
+ left.source,
1237
+ left.source_ref ?? "",
1238
+ left.evidence ?? "",
1239
+ String(left.manual_override),
1240
+ ], [
1241
+ right.tag_id,
1242
+ String(right.confidence),
1243
+ right.source,
1244
+ right.source_ref ?? "",
1245
+ right.evidence ?? "",
1246
+ String(right.manual_override),
1247
+ ]);
1248
+ }
1249
+ function compareCurrentResolvedDerivedTagRow(left, right) {
1250
+ return compareComparableTuples([
1251
+ left.tag_id,
1252
+ left.source,
1253
+ left.source_ref ?? "",
1254
+ left.rule_name,
1255
+ left.evidence ?? "",
1256
+ ], [
1257
+ right.tag_id,
1258
+ right.source,
1259
+ right.source_ref ?? "",
1260
+ right.rule_name,
1261
+ right.evidence ?? "",
1262
+ ]);
1263
+ }
1264
+ function areResolvedDocumentTagRowsEqual(currentRows, nextRows) {
1265
+ if (currentRows.length !== nextRows.length) {
1266
+ return false;
1267
+ }
1268
+ return currentRows.every((row, index) => compareCurrentResolvedDocumentTagRow(row, nextRows[index]) === 0);
1269
+ }
1270
+ function areResolvedDerivedTagRowsEqual(currentRows, nextRows) {
1271
+ if (currentRows.length !== nextRows.length) {
1272
+ return false;
1273
+ }
1274
+ return currentRows.every((row, index) => compareCurrentResolvedDerivedTagRow(row, nextRows[index]) === 0);
1275
+ }
1276
+ function compareComparableTuples(left, right) {
1277
+ const maxLength = Math.max(left.length, right.length);
1278
+ for (let index = 0; index < maxLength; index += 1) {
1279
+ const leftValue = left[index] ?? "";
1280
+ const rightValue = right[index] ?? "";
1281
+ if (leftValue === rightValue) {
1282
+ continue;
1283
+ }
1284
+ return leftValue < rightValue ? -1 : 1;
1285
+ }
1286
+ return 0;
1287
+ }
1288
+ function doesSiblingPathStillExist(file, candidateRelativePath) {
1289
+ const rootDir = resolveRootDirFromFile(file);
1290
+ if (!rootDir) {
1291
+ return true;
1292
+ }
1293
+ return fs.existsSync(path.join(rootDir, candidateRelativePath));
1294
+ }
1295
+ function resolveRootDirFromFile(file) {
1296
+ const normalizedRelativePath = file.relativePath.split(path.sep).join("/");
1297
+ if (!normalizedRelativePath) {
1298
+ return path.dirname(file.fullPath);
1299
+ }
1300
+ const suffix = normalizedRelativePath.split("/").join(path.sep);
1301
+ if (!file.fullPath.endsWith(suffix)) {
1302
+ return path.dirname(file.fullPath);
1303
+ }
1304
+ const rootDir = file.fullPath.slice(0, file.fullPath.length - suffix.length);
1305
+ return rootDir.replace(/[\\/]$/, "") || path.parse(file.fullPath).root || null;
1306
+ }
1307
+ function serializeManualFileBindingIdentity(target, tagId) {
1308
+ return JSON.stringify({
1309
+ inodeKey: target.inodeKey ?? null,
1310
+ contentHash: target.contentHash ?? null,
1311
+ size: target.size,
1312
+ extension: target.extension,
1313
+ tagId,
1314
+ });
1315
+ }
1316
+ function buildIdentityContentKey(contentHash, size, extension) {
1317
+ if (!contentHash) {
1318
+ return null;
1319
+ }
1320
+ return `${contentHash}::${size}::${extension}`;
1321
+ }
1322
+ function buildIdentityDocumentIdsByInode(rows) {
1323
+ const counts = new Map();
1324
+ rows.forEach((row) => {
1325
+ if (!row.inodeKey) {
1326
+ return;
1327
+ }
1328
+ counts.set(row.inodeKey, (counts.get(row.inodeKey) ?? 0) + 1);
1329
+ });
1330
+ return counts;
1331
+ }
1332
+ function buildIdentityDocumentIdsByContent(rows) {
1333
+ const counts = new Map();
1334
+ rows.forEach((row) => {
1335
+ const key = buildIdentityContentKey(row.contentHash, row.size, row.extension);
1336
+ if (!key) {
1337
+ return;
1338
+ }
1339
+ counts.set(key, (counts.get(key) ?? 0) + 1);
1340
+ });
1341
+ return counts;
1342
+ }
1343
+ function hasSameManualBindingIdentity(left, right) {
1344
+ if (left.inodeKey && right.inodeKey) {
1345
+ return left.inodeKey === right.inodeKey;
1346
+ }
1347
+ return buildIdentityContentKey(left.contentHash, left.size, left.extension)
1348
+ === buildIdentityContentKey(right.contentHash, right.size, right.extension);
1349
+ }
1350
+ //# sourceMappingURL=catalog-write-repository.js.map