gitnexus 1.6.11-rc.8 → 1.6.11

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 (531) hide show
  1. package/README.md +95 -26
  2. package/dist/_shared/graph/types.d.ts +81 -2
  3. package/dist/_shared/graph/types.d.ts.map +1 -1
  4. package/dist/_shared/integrations/circuit-breaker.d.ts.map +1 -1
  5. package/dist/_shared/integrations/circuit-breaker.js.map +1 -1
  6. package/dist/_shared/integrations/resilient-fetch.d.ts.map +1 -1
  7. package/dist/_shared/integrations/resilient-fetch.js.map +1 -1
  8. package/dist/_shared/language-detection.d.ts.map +1 -1
  9. package/dist/_shared/language-detection.js +2 -0
  10. package/dist/_shared/language-detection.js.map +1 -1
  11. package/dist/_shared/languages.d.ts +2 -1
  12. package/dist/_shared/languages.d.ts.map +1 -1
  13. package/dist/_shared/languages.js +1 -0
  14. package/dist/_shared/languages.js.map +1 -1
  15. package/dist/_shared/lbug/schema-constants.d.ts +2 -2
  16. package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
  17. package/dist/_shared/lbug/schema-constants.js +4 -0
  18. package/dist/_shared/lbug/schema-constants.js.map +1 -1
  19. package/dist/_shared/scope-resolution/evidence-weights.d.ts.map +1 -1
  20. package/dist/_shared/scope-resolution/finalize-algorithm.js +1 -0
  21. package/dist/_shared/scope-resolution/finalize-algorithm.js.map +1 -1
  22. package/dist/_shared/scope-resolution/language-classification.d.ts +3 -0
  23. package/dist/_shared/scope-resolution/language-classification.d.ts.map +1 -1
  24. package/dist/_shared/scope-resolution/language-classification.js +4 -0
  25. package/dist/_shared/scope-resolution/language-classification.js.map +1 -1
  26. package/dist/_shared/scope-resolution/reference-site.d.ts +10 -0
  27. package/dist/_shared/scope-resolution/reference-site.d.ts.map +1 -1
  28. package/dist/_shared/scope-resolution/registries/field-registry.js.map +1 -1
  29. package/dist/_shared/scope-resolution/registries/method-registry.js.map +1 -1
  30. package/dist/_shared/scope-resolution/scope-tree.d.ts.map +1 -1
  31. package/dist/_shared/scope-resolution/scope-tree.js.map +1 -1
  32. package/dist/_shared/scope-resolution/types.d.ts +15 -0
  33. package/dist/_shared/scope-resolution/types.d.ts.map +1 -1
  34. package/dist/cli/ai-context.d.ts +4 -0
  35. package/dist/cli/ai-context.js +90 -14
  36. package/dist/cli/analyze-config.js +14 -0
  37. package/dist/cli/analyze-options.d.ts +14 -0
  38. package/dist/cli/analyze-watch.d.ts +20 -0
  39. package/dist/cli/analyze-watch.js +434 -0
  40. package/dist/cli/analyze.js +27 -9
  41. package/dist/cli/auto-sync.d.ts +1 -0
  42. package/dist/cli/auto-sync.js +107 -0
  43. package/dist/cli/command-banner.d.ts +15 -0
  44. package/dist/cli/command-banner.js +42 -0
  45. package/dist/cli/detect-changes-format.js +21 -0
  46. package/dist/cli/doctor.d.ts +7 -0
  47. package/dist/cli/doctor.js +14 -0
  48. package/dist/cli/group.js +7 -1
  49. package/dist/cli/help-i18n.js +4 -0
  50. package/dist/cli/i18n/en.d.ts +18 -3
  51. package/dist/cli/i18n/en.js +18 -3
  52. package/dist/cli/i18n/resources.d.ts +33 -3
  53. package/dist/cli/i18n/zh-CN.d.ts +15 -0
  54. package/dist/cli/i18n/zh-CN.js +18 -3
  55. package/dist/cli/index.js +34 -7
  56. package/dist/cli/mcp.d.ts +15 -0
  57. package/dist/cli/mcp.js +44 -0
  58. package/dist/cli/optional-grammars.d.ts +3 -2
  59. package/dist/cli/optional-grammars.js +19 -3
  60. package/dist/cli/setup.js +41 -15
  61. package/dist/cli/update-notice.d.ts +34 -0
  62. package/dist/cli/update-notice.js +91 -0
  63. package/dist/cli/update.d.ts +21 -0
  64. package/dist/cli/update.js +93 -0
  65. package/dist/cli/watch.d.ts +1 -20
  66. package/dist/cli/watch.js +5 -371
  67. package/dist/core/analysis-feature-registry.d.ts +2 -0
  68. package/dist/core/analysis-feature-registry.js +15 -0
  69. package/dist/core/analyzer-identity.js +21 -3
  70. package/dist/core/auto-sync/analysis-worker-launch.d.ts +30 -0
  71. package/dist/core/auto-sync/analysis-worker-launch.js +166 -0
  72. package/dist/core/auto-sync/config.d.ts +33 -0
  73. package/dist/core/auto-sync/config.js +320 -0
  74. package/dist/core/auto-sync/index.d.ts +6 -0
  75. package/dist/core/auto-sync/index.js +6 -0
  76. package/dist/core/auto-sync/path-security.d.ts +9 -0
  77. package/dist/core/auto-sync/path-security.js +251 -0
  78. package/dist/core/auto-sync/repo.d.ts +1 -0
  79. package/dist/core/auto-sync/repo.js +6 -0
  80. package/dist/core/auto-sync/runner.d.ts +44 -0
  81. package/dist/core/auto-sync/runner.js +428 -0
  82. package/dist/core/auto-sync/starter.d.ts +50 -0
  83. package/dist/core/auto-sync/starter.js +497 -0
  84. package/dist/core/auto-sync/state.d.ts +37 -0
  85. package/dist/core/auto-sync/state.js +119 -0
  86. package/dist/core/group/config-parser.js +8 -1
  87. package/dist/core/group/cross-impact.d.ts +3 -0
  88. package/dist/core/group/cross-impact.js +36 -4
  89. package/dist/core/group/extractors/http-patterns/java.js +22 -8
  90. package/dist/core/group/extractors/http-patterns/kotlin.js +86 -31
  91. package/dist/core/group/extractors/http-patterns/node.js +145 -8
  92. package/dist/core/group/extractors/http-patterns/python.js +11 -7
  93. package/dist/core/group/extractors/http-route-extractor.js +63 -17
  94. package/dist/core/group/extractors/java-workspace-extractor.js +244 -31
  95. package/dist/core/group/group-lock.js +5 -6
  96. package/dist/core/group/normalization.d.ts +38 -1
  97. package/dist/core/group/normalization.js +59 -3
  98. package/dist/core/group/service.d.ts +11 -0
  99. package/dist/core/group/service.js +13 -1
  100. package/dist/core/group/storage.js +2 -2
  101. package/dist/core/group/sync.d.ts +24 -1
  102. package/dist/core/group/sync.js +46 -8
  103. package/dist/core/group/types.d.ts +13 -0
  104. package/dist/core/incremental/derived-writeback.d.ts +36 -0
  105. package/dist/core/incremental/derived-writeback.js +68 -0
  106. package/dist/core/incremental/spring-config-drift.d.ts +12 -0
  107. package/dist/core/incremental/spring-config-drift.js +46 -0
  108. package/dist/core/incremental/subgraph-extract.d.ts +7 -4
  109. package/dist/core/incremental/subgraph-extract.js +30 -5
  110. package/dist/core/ingestion/asyncapi/document.d.ts +226 -0
  111. package/dist/core/ingestion/asyncapi/document.js +758 -0
  112. package/dist/core/ingestion/asyncapi/protocol.d.ts +79 -0
  113. package/dist/core/ingestion/asyncapi/protocol.js +203 -0
  114. package/dist/core/ingestion/call-extractors/configs/zig.d.ts +2 -0
  115. package/dist/core/ingestion/call-extractors/configs/zig.js +4 -0
  116. package/dist/core/ingestion/call-extractors/zig-static-gating.d.ts +100 -0
  117. package/dist/core/ingestion/call-extractors/zig-static-gating.js +319 -0
  118. package/dist/core/ingestion/call-processor.js +3 -1
  119. package/dist/core/ingestion/class-extractors/configs/zig.d.ts +2 -0
  120. package/dist/core/ingestion/class-extractors/configs/zig.js +32 -0
  121. package/dist/core/ingestion/class-extractors/generic.js +4 -1
  122. package/dist/core/ingestion/class-types.d.ts +6 -2
  123. package/dist/core/ingestion/destination-key.d.ts +59 -0
  124. package/dist/core/ingestion/destination-key.js +61 -0
  125. package/dist/core/ingestion/entry-point-scoring.d.ts +6 -2
  126. package/dist/core/ingestion/entry-point-scoring.js +8 -44
  127. package/dist/core/ingestion/export-detection.d.ts +37 -0
  128. package/dist/core/ingestion/export-detection.js +60 -0
  129. package/dist/core/ingestion/field-extractors/configs/zig.d.ts +8 -0
  130. package/dist/core/ingestion/field-extractors/configs/zig.js +49 -0
  131. package/dist/core/ingestion/field-extractors/generic.d.ts +1 -1
  132. package/dist/core/ingestion/field-extractors/generic.js +12 -1
  133. package/dist/core/ingestion/frameworks/spring/actuator-runtime.d.ts +26 -0
  134. package/dist/core/ingestion/frameworks/spring/actuator-runtime.js +843 -0
  135. package/dist/core/ingestion/frameworks/spring/analysis-features.d.ts +5 -0
  136. package/dist/core/ingestion/frameworks/spring/analysis-features.js +9 -0
  137. package/dist/core/ingestion/frameworks/spring/annotation-arguments.js +8 -1
  138. package/dist/core/ingestion/frameworks/spring/argument-facts.d.ts +80 -0
  139. package/dist/core/ingestion/frameworks/spring/argument-facts.js +85 -0
  140. package/dist/core/ingestion/frameworks/spring/config-bindings.d.ts +1 -0
  141. package/dist/core/ingestion/frameworks/spring/config-bindings.js +2 -1
  142. package/dist/core/ingestion/frameworks/spring/destinations.d.ts +539 -0
  143. package/dist/core/ingestion/frameworks/spring/destinations.js +840 -0
  144. package/dist/core/ingestion/frameworks/spring/dynamic-lookups.d.ts +22 -0
  145. package/dist/core/ingestion/frameworks/spring/dynamic-lookups.js +124 -0
  146. package/dist/core/ingestion/frameworks/spring/message-producers.d.ts +78 -0
  147. package/dist/core/ingestion/frameworks/spring/message-producers.js +90 -0
  148. package/dist/core/ingestion/frameworks/spring/non-http-handlers.d.ts +29 -0
  149. package/dist/core/ingestion/frameworks/spring/vendor-prefixes.d.ts +7 -0
  150. package/dist/core/ingestion/frameworks/spring/vendor-prefixes.js +22 -0
  151. package/dist/core/ingestion/import-resolvers/configs/zig.d.ts +13 -0
  152. package/dist/core/ingestion/import-resolvers/configs/zig.js +26 -0
  153. package/dist/core/ingestion/import-resolvers/zig.d.ts +34 -0
  154. package/dist/core/ingestion/import-resolvers/zig.js +193 -0
  155. package/dist/core/ingestion/language-config.d.ts +177 -0
  156. package/dist/core/ingestion/language-config.js +719 -0
  157. package/dist/core/ingestion/language-provider.d.ts +178 -1
  158. package/dist/core/ingestion/language-provider.js +22 -0
  159. package/dist/core/ingestion/languages/csharp/razor-view-components.d.ts +62 -0
  160. package/dist/core/ingestion/languages/csharp/razor-view-components.js +954 -0
  161. package/dist/core/ingestion/languages/csharp/resolution-config.d.ts +3 -0
  162. package/dist/core/ingestion/languages/csharp/resolution-config.js +6 -1
  163. package/dist/core/ingestion/languages/csharp/scope-resolver.js +8 -0
  164. package/dist/core/ingestion/languages/index.d.ts +1 -0
  165. package/dist/core/ingestion/languages/index.js +2 -0
  166. package/dist/core/ingestion/languages/java/analysis-features.d.ts +1 -1
  167. package/dist/core/ingestion/languages/java/analysis-features.js +14 -6
  168. package/dist/core/ingestion/languages/java/capture-side-channel.d.ts +8 -0
  169. package/dist/core/ingestion/languages/java/capture-side-channel.js +34 -0
  170. package/dist/core/ingestion/languages/java/captures.js +22 -1
  171. package/dist/core/ingestion/languages/java/lombok-synthesizer.d.ts +42 -0
  172. package/dist/core/ingestion/languages/java/lombok-synthesizer.js +439 -0
  173. package/dist/core/ingestion/languages/java/scope-resolver.js +2 -0
  174. package/dist/core/ingestion/languages/java/spring-actuator.d.ts +2 -0
  175. package/dist/core/ingestion/languages/java/spring-actuator.js +62 -0
  176. package/dist/core/ingestion/languages/java/spring-di.d.ts +17 -1
  177. package/dist/core/ingestion/languages/java/spring-di.js +44 -2
  178. package/dist/core/ingestion/languages/java/spring-dynamic-lookup.d.ts +8 -0
  179. package/dist/core/ingestion/languages/java/spring-dynamic-lookup.js +62 -0
  180. package/dist/core/ingestion/languages/java/spring-message-producers.d.ts +20 -0
  181. package/dist/core/ingestion/languages/java/spring-message-producers.js +83 -0
  182. package/dist/core/ingestion/languages/java/spring-non-http-handlers.d.ts +11 -1
  183. package/dist/core/ingestion/languages/java/spring-non-http-handlers.js +13 -3
  184. package/dist/core/ingestion/languages/java.js +18 -8
  185. package/dist/core/ingestion/languages/jvm/accessor-synthesis.d.ts +99 -0
  186. package/dist/core/ingestion/languages/jvm/accessor-synthesis.js +173 -0
  187. package/dist/core/ingestion/languages/jvm/beanspec.d.ts +17 -0
  188. package/dist/core/ingestion/languages/jvm/beanspec.js +42 -0
  189. package/dist/core/ingestion/languages/kotlin/capture-side-channel.d.ts +15 -0
  190. package/dist/core/ingestion/languages/kotlin/capture-side-channel.js +50 -0
  191. package/dist/core/ingestion/languages/kotlin/captures.js +24 -1
  192. package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.d.ts +26 -0
  193. package/dist/core/ingestion/languages/kotlin/lombok-synthesizer.js +417 -0
  194. package/dist/core/ingestion/languages/kotlin/scope-resolver.js +4 -0
  195. package/dist/core/ingestion/languages/kotlin/spring-actuator.d.ts +4 -0
  196. package/dist/core/ingestion/languages/kotlin/spring-actuator.js +172 -0
  197. package/dist/core/ingestion/languages/kotlin/spring-config-bindings.d.ts +18 -0
  198. package/dist/core/ingestion/languages/kotlin/spring-config-bindings.js +411 -0
  199. package/dist/core/ingestion/languages/kotlin/spring-di.d.ts +51 -1
  200. package/dist/core/ingestion/languages/kotlin/spring-di.js +86 -6
  201. package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.d.ts +8 -0
  202. package/dist/core/ingestion/languages/kotlin/spring-dynamic-lookup.js +77 -0
  203. package/dist/core/ingestion/languages/kotlin/spring-message-producers.d.ts +17 -0
  204. package/dist/core/ingestion/languages/kotlin/spring-message-producers.js +119 -0
  205. package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.d.ts +16 -0
  206. package/dist/core/ingestion/languages/kotlin/spring-non-http-handlers.js +77 -2
  207. package/dist/core/ingestion/languages/kotlin.js +24 -0
  208. package/dist/core/ingestion/languages/python.js +2 -0
  209. package/dist/core/ingestion/languages/rust/query.js +8 -8
  210. package/dist/core/ingestion/languages/zig/captures.d.ts +260 -0
  211. package/dist/core/ingestion/languages/zig/captures.js +2070 -0
  212. package/dist/core/ingestion/languages/zig/index.d.ts +4 -0
  213. package/dist/core/ingestion/languages/zig/index.js +4 -0
  214. package/dist/core/ingestion/languages/zig/interpret.d.ts +36 -0
  215. package/dist/core/ingestion/languages/zig/interpret.js +176 -0
  216. package/dist/core/ingestion/languages/zig/query.d.ts +3 -0
  217. package/dist/core/ingestion/languages/zig/query.js +426 -0
  218. package/dist/core/ingestion/languages/zig/range-binding.d.ts +37 -0
  219. package/dist/core/ingestion/languages/zig/range-binding.js +373 -0
  220. package/dist/core/ingestion/languages/zig/scope-resolver.d.ts +12 -0
  221. package/dist/core/ingestion/languages/zig/scope-resolver.js +58 -0
  222. package/dist/core/ingestion/languages/zig/simple-hooks.d.ts +25 -0
  223. package/dist/core/ingestion/languages/zig/simple-hooks.js +143 -0
  224. package/dist/core/ingestion/languages/zig.d.ts +32 -0
  225. package/dist/core/ingestion/languages/zig.js +130 -0
  226. package/dist/core/ingestion/method-extractors/configs/zig.d.ts +2 -0
  227. package/dist/core/ingestion/method-extractors/configs/zig.js +120 -0
  228. package/dist/core/ingestion/method-extractors/generic.js +16 -4
  229. package/dist/core/ingestion/method-types.d.ts +10 -4
  230. package/dist/core/ingestion/model/field-registry.d.ts +3 -3
  231. package/dist/core/ingestion/model/field-registry.js +11 -7
  232. package/dist/core/ingestion/model/method-registry.d.ts +3 -3
  233. package/dist/core/ingestion/model/method-registry.js +13 -8
  234. package/dist/core/ingestion/model/registration-table.js +3 -0
  235. package/dist/core/ingestion/model/type-registry.js +8 -4
  236. package/dist/core/ingestion/pipeline-phases/di.js +47 -14
  237. package/dist/core/ingestion/pipeline-phases/index.d.ts +1 -0
  238. package/dist/core/ingestion/pipeline-phases/index.js +1 -0
  239. package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +7 -1
  240. package/dist/core/ingestion/pipeline-phases/parse-impl.js +79 -91
  241. package/dist/core/ingestion/pipeline-phases/parse.d.ts +17 -0
  242. package/dist/core/ingestion/pipeline-phases/processes.d.ts +2 -1
  243. package/dist/core/ingestion/pipeline-phases/processes.js +39 -4
  244. package/dist/core/ingestion/pipeline-phases/routes.js +27 -18
  245. package/dist/core/ingestion/pipeline-phases/runner.d.ts +4 -1
  246. package/dist/core/ingestion/pipeline-phases/runner.js +34 -14
  247. package/dist/core/ingestion/pipeline-phases/scan.js +82 -1
  248. package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.d.ts +2 -0
  249. package/dist/core/ingestion/pipeline-phases/spring-auto-configuration.js +8 -0
  250. package/dist/core/ingestion/pipeline-phases/spring-destinations.d.ts +123 -0
  251. package/dist/core/ingestion/pipeline-phases/spring-destinations.js +688 -0
  252. package/dist/core/ingestion/pipeline.d.ts +30 -1
  253. package/dist/core/ingestion/pipeline.js +52 -14
  254. package/dist/core/ingestion/resolve-references.js +5 -0
  255. package/dist/core/ingestion/route-extractors/constant-resolver.d.ts +16 -0
  256. package/dist/core/ingestion/route-extractors/constant-resolver.js +10 -0
  257. package/dist/core/ingestion/route-extractors/java-const-resolver.d.ts +27 -1
  258. package/dist/core/ingestion/route-extractors/java-const-resolver.js +161 -13
  259. package/dist/core/ingestion/route-extractors/kotlin-const-resolver.d.ts +12 -18
  260. package/dist/core/ingestion/route-extractors/kotlin-const-resolver.js +214 -33
  261. package/dist/core/ingestion/route-extractors/kotlin-spring.d.ts +13 -0
  262. package/dist/core/ingestion/route-extractors/kotlin-spring.js +259 -0
  263. package/dist/core/ingestion/route-extractors/python-decorator-handler.d.ts +6 -0
  264. package/dist/core/ingestion/route-extractors/python-decorator-handler.js +16 -0
  265. package/dist/core/ingestion/route-extractors/spring-shared.d.ts +30 -1
  266. package/dist/core/ingestion/route-extractors/spring-shared.js +77 -9
  267. package/dist/core/ingestion/route-extractors/spring.js +9 -5
  268. package/dist/core/ingestion/scope-extractor.js +8 -0
  269. package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +62 -0
  270. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +4 -0
  271. package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +2 -0
  272. package/dist/core/ingestion/scope-resolution/graph-bridge/ids.js +28 -0
  273. package/dist/core/ingestion/scope-resolution/graph-bridge/node-lookup.js +11 -2
  274. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +23 -2
  275. package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +24 -3
  276. package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.d.ts +11 -1
  277. package/dist/core/ingestion/scope-resolution/passes/callable-value-flow.js +26 -6
  278. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.d.ts +11 -0
  279. package/dist/core/ingestion/scope-resolution/passes/compound-receiver.js +29 -3
  280. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +8 -1
  281. package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +35 -9
  282. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +1 -4
  283. package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +277 -9
  284. package/dist/core/ingestion/scope-resolution/pipeline/registry.js +2 -0
  285. package/dist/core/ingestion/scope-resolution/pipeline/run.js +7 -3
  286. package/dist/core/ingestion/scope-resolution/scope/walkers.d.ts +43 -9
  287. package/dist/core/ingestion/scope-resolution/scope/walkers.js +71 -10
  288. package/dist/core/ingestion/tree-sitter-queries.d.ts +1 -0
  289. package/dist/core/ingestion/tree-sitter-queries.js +161 -0
  290. package/dist/core/ingestion/type-extractors/zig.d.ts +2 -0
  291. package/dist/core/ingestion/type-extractors/zig.js +17 -0
  292. package/dist/core/ingestion/utils/ast-helpers.d.ts +40 -1
  293. package/dist/core/ingestion/utils/ast-helpers.js +124 -6
  294. package/dist/core/ingestion/utils/callable-flow-captures.d.ts +9 -0
  295. package/dist/core/ingestion/utils/callable-flow-captures.js +75 -14
  296. package/dist/core/ingestion/utils/test-file-path.d.ts +28 -0
  297. package/dist/core/ingestion/utils/test-file-path.js +87 -0
  298. package/dist/core/ingestion/variable-extractors/configs/zig.d.ts +2 -0
  299. package/dist/core/ingestion/variable-extractors/configs/zig.js +71 -0
  300. package/dist/core/ingestion/workers/parse-worker.js +85 -14
  301. package/dist/core/install-context.d.ts +4 -0
  302. package/dist/core/install-context.js +152 -0
  303. package/dist/core/lbug/csv-generator.d.ts +10 -1
  304. package/dist/core/lbug/csv-generator.js +92 -22
  305. package/dist/core/lbug/graph-emit-sink.d.ts +3 -2
  306. package/dist/core/lbug/graph-emit-sink.js +4 -2
  307. package/dist/core/lbug/lbug-adapter.d.ts +100 -5
  308. package/dist/core/lbug/lbug-adapter.js +236 -24
  309. package/dist/core/lbug/schema.d.ts +6 -5
  310. package/dist/core/lbug/schema.js +84 -22
  311. package/dist/core/net/url-guard.d.ts +6 -0
  312. package/dist/core/net/url-guard.js +147 -0
  313. package/dist/core/package-version.d.ts +5 -0
  314. package/dist/core/package-version.js +10 -0
  315. package/dist/core/run-analyze.d.ts +21 -4
  316. package/dist/core/run-analyze.js +426 -117
  317. package/dist/core/search/fts-indexes.d.ts +19 -1
  318. package/dist/core/search/fts-indexes.js +28 -1
  319. package/dist/core/tree-sitter/parser-loader.js +16 -0
  320. package/dist/core/update-cache.d.ts +39 -0
  321. package/dist/core/update-cache.js +141 -0
  322. package/dist/core/update-check.d.ts +31 -0
  323. package/dist/core/update-check.js +274 -0
  324. package/dist/mcp/local/local-backend.d.ts +27 -13
  325. package/dist/mcp/local/local-backend.js +148 -60
  326. package/dist/mcp/repository-policy.d.ts +5 -1
  327. package/dist/mcp/repository-policy.js +48 -4
  328. package/dist/mcp/resources.js +10 -1
  329. package/dist/mcp/server.js +8 -9
  330. package/dist/mcp/tools.js +32 -22
  331. package/dist/server/analyze-launch.d.ts +2 -0
  332. package/dist/server/analyze-launch.js +2 -0
  333. package/dist/server/analyze-worker-protocol.d.ts +6 -1
  334. package/dist/server/analyze-worker.d.ts +1 -0
  335. package/dist/server/analyze-worker.js +38 -20
  336. package/dist/server/api.d.ts +5 -1
  337. package/dist/server/api.js +105 -88
  338. package/dist/server/git-clone.d.ts +40 -16
  339. package/dist/server/git-clone.js +253 -199
  340. package/dist/server/grep-params.d.ts +18 -0
  341. package/dist/server/grep-params.js +83 -0
  342. package/dist/server/grep-scan.d.ts +23 -0
  343. package/dist/server/grep-scan.js +107 -0
  344. package/dist/server/grep-worker.d.ts +1 -0
  345. package/dist/server/grep-worker.js +12 -0
  346. package/dist/server/mcp-http.d.ts +8 -0
  347. package/dist/server/mcp-http.js +16 -1
  348. package/dist/server/update-controller.d.ts +29 -0
  349. package/dist/server/update-controller.js +82 -0
  350. package/dist/storage/file-lock.d.ts +15 -0
  351. package/dist/storage/file-lock.js +155 -0
  352. package/dist/storage/fs-atomic.d.ts +24 -0
  353. package/dist/storage/fs-atomic.js +86 -2
  354. package/dist/storage/global-dir.d.ts +2 -0
  355. package/dist/storage/global-dir.js +6 -0
  356. package/dist/storage/parse-cache.d.ts +45 -7
  357. package/dist/storage/parse-cache.js +189 -42
  358. package/dist/storage/parsedfile-store.d.ts +41 -60
  359. package/dist/storage/parsedfile-store.js +247 -171
  360. package/dist/storage/repo-manager.d.ts +10 -4
  361. package/dist/storage/repo-manager.js +35 -25
  362. package/dist/storage/repo-meta.d.ts +34 -0
  363. package/dist/storage/v8-sidecar.d.ts +48 -0
  364. package/dist/storage/v8-sidecar.js +347 -0
  365. package/dist/types/pipeline.d.ts +14 -0
  366. package/dist/utils/process-identity.d.ts +2 -0
  367. package/dist/utils/process-identity.js +35 -0
  368. package/package.json +10 -4
  369. package/scripts/assert-web-assets.mjs +35 -0
  370. package/scripts/build-web.js +72 -0
  371. package/scripts/build.js +31 -28
  372. package/scripts/cross-platform-tests.ts +5 -0
  373. package/skills/gitnexus-cli.md +5 -1
  374. package/web/assets/ProcessFlowModal-DAeX2BHW.js +24 -0
  375. package/web/assets/{abnfDiagram-QNN2D4NM-Cq2djWWe.js → abnfDiagram-PAR5IXJI-J3OdeHLl.js} +1 -1
  376. package/web/assets/{agent-BWAXPjC5.js → agent-BMRqXorA.js} +6 -4
  377. package/web/assets/architecture-7GRP2DOG-M7G3SWCO-G3sWyX6M.js +1 -0
  378. package/web/assets/architectureDiagram-2PNWDENW-ysKcRlGT.js +36 -0
  379. package/web/assets/blockDiagram-PUWGUBOA-oLXTa1lY.js +129 -0
  380. package/web/assets/c4Diagram-3NLNHZP5-D3iu4eC0.js +38 -0
  381. package/web/assets/{chunk-2SREHG4O-Dip42xOq.js → chunk-2NMSUPJH-B24w-wng.js} +1 -1
  382. package/web/assets/{chunk-VJFVEZTB-CX66F2ep.js → chunk-32MCUW2P-ByUyt0Sa.js} +3 -3
  383. package/web/assets/{chunk-4RFN2BYJ-DHHWuC-n.js → chunk-3NPNU5GP-DocZ07Sg.js} +1 -1
  384. package/web/assets/{chunk-XNMVGMAZ-Cg_LnPtj.js → chunk-3SMGFRGC-CAu4JRLv.js} +1 -1
  385. package/web/assets/{chunk-W44A43WB-BN8EQc_u.js → chunk-4CAJECRU-DRYMwG9z.js} +1 -1
  386. package/web/assets/chunk-4OADUZ5Q-CxrSvOwZ.js +10 -0
  387. package/web/assets/{chunk-DMV4VAQV-Bn6WXDSX.js → chunk-5IQCK5RY-CCMhl4so.js} +2 -2
  388. package/web/assets/{chunk-RRFMTAIC-CMN0wcYA.js → chunk-5MVFYWFU-Db9KvrYO.js} +1 -1
  389. package/web/assets/chunk-CLS4B6BI-Uvx5QNsu.js +168 -0
  390. package/web/assets/chunk-DZP67EKU-B3Mt380j.js +70 -0
  391. package/web/assets/{chunk-6L755F7B-CMhLBxi2.js → chunk-EICP7K5X-CqwE9hui.js} +1 -1
  392. package/web/assets/{chunk-BI6VK774-D0oaWk8V.js → chunk-GDY2UAB4-CtQQR0a8.js} +1 -1
  393. package/web/assets/chunk-H7VHZCWX-CE1nAmt6.js +1 -0
  394. package/web/assets/chunk-IJBDOHL6-BUKtY6oF.js +1 -0
  395. package/web/assets/{chunk-OIYT25JQ-CQ6KOZGX.js → chunk-JFFVMJIA-0fJenqia.js} +1 -1
  396. package/web/assets/{chunk-HNC4WDU7-DoU3KnA7.js → chunk-JZGDI5JK-BeTMSSkJ.js} +1 -1
  397. package/web/assets/{chunk-EQFTRU2I-1ix7TzW9.js → chunk-KD6SPIPN-Qp1Pflah.js} +1 -1
  398. package/web/assets/chunk-NF366YI6-CpSpya3n.js +231 -0
  399. package/web/assets/chunk-NUDPKBEF-CU1-BQIW.js +34 -0
  400. package/web/assets/{chunk-SATU7PGQ-B1zhgOvc.js → chunk-OJINMHTS-zOCQe4eI.js} +1 -1
  401. package/web/assets/{chunk-E5QJAATJ-dh-5CtIV.js → chunk-Q6NGPIAH-yyBomb63.js} +1 -1
  402. package/web/assets/chunk-QJSWEUOL-D_rf9gPD.js +128 -0
  403. package/web/assets/chunk-R7JDMUKV-CRoqKpD6.js +1 -0
  404. package/web/assets/{chunk-VE5CLXGZ-C1vB7wpY.js → chunk-RCXAM2GK-CUqrWQDV.js} +1 -1
  405. package/web/assets/{chunk-66DQ2XMT-CM0xjFAN.js → chunk-RT4MAWR3-zXy9-Lvt.js} +1 -1
  406. package/web/assets/{chunk-TZUXEDM2-B7p9AcTw.js → chunk-SFMINX4W-J2UrvMpD.js} +1 -1
  407. package/web/assets/{chunk-LORZLZWG-B57uGw2N.js → chunk-SLDQ463B-CHY6e_bQ.js} +2 -2
  408. package/web/assets/{chunk-FWYVLQTC-BgDItRzJ.js → chunk-TCEHX7R3-CXPDEVbm.js} +1 -1
  409. package/web/assets/{chunk-224SPVON-B8gxtXpy.js → chunk-VQC2EELJ-Dc8fIy4R.js} +1 -1
  410. package/web/assets/chunk-VRZKL3EH-DB3KixTQ.js +1 -0
  411. package/web/assets/{chunk-A7VWPJGB-B4O3oQdQ.js → chunk-VTHDQ7BV-b7FsPQZO.js} +1 -1
  412. package/web/assets/chunk-VY2OSTJR-Dkgc3Brg.js +2 -0
  413. package/web/assets/chunk-WUBWJARI-7FwILJ3k.js +2 -0
  414. package/web/assets/{chunk-D6VWDJW2-BxcBdZ9c.js → chunk-YBMQ5JGJ-BSnyWZmd.js} +1 -1
  415. package/web/assets/chunk-YDMLL4PJ-IHhMngTz.js +2 -0
  416. package/web/assets/chunk-Z6V44ARX-BjCEsF0Q.js +62 -0
  417. package/web/assets/chunk-ZJJ7VQJP-Dz089b-K.js +321 -0
  418. package/web/assets/chunk-ZNLK7HRR-B0iFpmW4.js +1 -0
  419. package/web/assets/chunk-ZQN7UHIL-Cy7KAjQt.js +1 -0
  420. package/web/assets/{chunk-L5GCOVLC-CE2w8Nm_.js → chunk-ZS6VBONO-DBjJO0w9.js} +2 -2
  421. package/web/assets/{chunk-NV3KIAZN-5yvcpMiu.js → chunk-ZUNWM646-DNRJyNPe.js} +1 -1
  422. package/web/assets/classDiagram-FGAMPOII-C5Ibyode.js +1 -0
  423. package/web/assets/classDiagram-v2-AI5RJEK4-C5Ibyode.js +1 -0
  424. package/web/assets/{cose-bilkent-RZA4XIPR-BHpK4Crp.js → cose-bilkent-Q72C2OJ3-DMeFXnK8.js} +1 -1
  425. package/web/assets/cynefin-OW5HDTMX-6OCTWOGG-vlqakYcO.js +1 -0
  426. package/web/assets/{cynefinDiagram-TR7LZUCG-C3Iar9lZ.js → cynefinDiagram-2OOWUY4M-BY6DZkTt.js} +1 -1
  427. package/web/assets/dagre-MPVFI544-DWO70fy_.js +4 -0
  428. package/web/assets/diagram-AE4NWXFW-f7JRVRUL.js +24 -0
  429. package/web/assets/{diagram-L7DVE3PN-GIRyVUtt.js → diagram-BUJID5UG-BXZv1rCS.js} +2 -2
  430. package/web/assets/{diagram-JCD7PQ7C-DC5-9FRr.js → diagram-C44CZ5NI-CRNfvjCq.js} +1 -1
  431. package/web/assets/diagram-EYOLEXXS-DZOjKQVE.js +30 -0
  432. package/web/assets/diagram-P7B6PWOJ-D3SQmw3_.js +3 -0
  433. package/web/assets/{ebnfDiagram-DRHRQJWO-D8_CI4ZF.js → ebnfDiagram-FTPZ3PV6-jKWpIDN2.js} +1 -1
  434. package/web/assets/erDiagram-H3ERR26V-BqJntEuJ.js +99 -0
  435. package/web/assets/eventmodeling-NTZA5JFV-WKSWQHFS-Bq4FqtBL.js +1 -0
  436. package/web/assets/flowDiagram-YHGXBVSY-Dnlk2bEu.js +1 -0
  437. package/web/assets/ganttDiagram-RWFTB7ED-Bl6Y1K5T.js +292 -0
  438. package/web/assets/gitGraph-4MIJSDKK-JH2HKYUO-B3yc4GkH.js +1 -0
  439. package/web/assets/gitGraphDiagram-MAR6GKOP-CkNvsrdw.js +106 -0
  440. package/web/assets/{index-CJDpyH-n.css → index-C2_aej58.css} +1 -1
  441. package/web/assets/{index-DDRie6_8.js → index-Dj3vQOHK.js} +51 -51
  442. package/web/assets/info-A6RAGUB7-3TX3OFWW-BM3N5_Dv.js +1 -0
  443. package/web/assets/infoDiagram-LV547AFH-COkFutAf.js +2 -0
  444. package/web/assets/{ishikawaDiagram-4JDSWWDM-DmqWtjkl.js → ishikawaDiagram-PUJQGNDS-Bb8FylSR.js} +2 -2
  445. package/web/assets/{journeyDiagram-QUS3FAKT-XAzVzrQW.js → journeyDiagram-EW5QJT4W-BzoHUBhM.js} +4 -4
  446. package/web/assets/{kanban-definition-D4L7SQKK-YurRnmYn.js → kanban-definition-JSQERAZB-Uy2Pi0yQ.js} +4 -4
  447. package/web/assets/katex-CZ4GXH2S-C0Fcou7U.js +257 -0
  448. package/web/assets/{mindmap-definition-66QP5TD6-ChrWWnil.js → mindmap-definition-VBJCLQLM-B-CnOnt_.js} +3 -3
  449. package/web/assets/packet-AYTQ26CC-MKII6P3Y-Bd52ZE3V.js +1 -0
  450. package/web/assets/{pegDiagram-YKIO5C6U-QGeQLEq2.js → pegDiagram-7WILOQTJ-D9sneKXg.js} +1 -1
  451. package/web/assets/pie-WAS4IAKB-6B4OPMJ5-DW5Xy69Z.js +1 -0
  452. package/web/assets/pieDiagram-EFA7CBSB-Dx2NQofL.js +39 -0
  453. package/web/assets/{quadrantDiagram-VO5QLCXA-B7098wSc.js → quadrantDiagram-WARVYA7P-DPKL9_0A.js} +3 -3
  454. package/web/assets/radar-RG4KPBEZ-UPTR3XUL-CkzibT2h.js +1 -0
  455. package/web/assets/railroad-74A4TZTK-MEFUPFMS-D9zF-mDN.js +1 -0
  456. package/web/assets/railroad-abnf-HS5TGJTU-R5YG2S5D-ruacjUbX.js +1 -0
  457. package/web/assets/railroad-ebnf-LZEXJU2U-G5QEX2L6-S1zc5OiO.js +1 -0
  458. package/web/assets/railroad-peg-WCYAUIDC-H5I2ZRY2-Cbcr0XzY.js +1 -0
  459. package/web/assets/{railroadDiagram-M77LTMSB-AXu4vxk_.js → railroadDiagram-5NIQVNLQ-DsweTo14.js} +1 -1
  460. package/web/assets/{requirementDiagram-RASO5HTO-B-l8InpK.js → requirementDiagram-SXREGOKZ-CXwdNSVR.js} +4 -4
  461. package/web/assets/{sankeyDiagram-F32LOJ6L-Kd7zjJgX.js → sankeyDiagram-VV74M2VP-CA6jtP9P.js} +3 -3
  462. package/web/assets/{sequenceDiagram-URATNSBD-BlcdY9DS.js → sequenceDiagram-52ZFIFDL-k_5rEbzm.js} +15 -15
  463. package/web/assets/src-Df5C1Nz4.js +1 -0
  464. package/web/assets/stateDiagram-3QAKPIVI-BejHTf31.js +1 -0
  465. package/web/assets/stateDiagram-v2-HN2P4RDZ-BrfoRuOc.js +1 -0
  466. package/web/assets/swimlanes-QLVP42QD-CQSVO5D6.js +1 -0
  467. package/web/assets/swimlanesDiagram-JKPDBFCN-DrWB9kcD.js +8 -0
  468. package/web/assets/{timeline-definition-WJXWRH4I-DUfp1R3E.js → timeline-definition-GS45XAXW-C0Ks8rPP.js} +4 -4
  469. package/web/assets/treeView-Q6P3EWNA-ZRJGANNQ-DmKOCo6L.js +1 -0
  470. package/web/assets/treemap-WGGIJYW6-K64VN5AS-DO3xY92U.js +1 -0
  471. package/web/assets/{vennDiagram-K3VCMQLI-Bj-FN-XS.js → vennDiagram-QIQU2ZRJ-rgPiS837.js} +3 -3
  472. package/web/assets/wardley-WFR3VGLG-D67OWCCH-DiDPBNft.js +1 -0
  473. package/web/assets/wardleyDiagram-U6YNM66N-DRVr7SPi.js +78 -0
  474. package/web/assets/{xychartDiagram-5VRUVLCW-CK_rbSWE.js → xychartDiagram-XJAZEDNM-COtlZzYb.js} +3 -3
  475. package/web/index.html +19 -17
  476. package/web/assets/ProcessFlowModal-Badh2Pbg.js +0 -24
  477. package/web/assets/architecture-TIHT7OUA-V6N4J3SY-fTePvLoe.js +0 -1
  478. package/web/assets/architectureDiagram-OT7VKSJ7-mKEXxkeF.js +0 -36
  479. package/web/assets/blockDiagram-54WLDVWM-Dg_hrRWE.js +0 -132
  480. package/web/assets/c4Diagram-X2LASM7V-CnhoT4aV.js +0 -10
  481. package/web/assets/chunk-2AEHWXPW-CV4r6-1I.js +0 -127
  482. package/web/assets/chunk-5VCL7Z4A-CQ-2dYWj.js +0 -1
  483. package/web/assets/chunk-6BELYETK-ChsYtfHW.js +0 -1
  484. package/web/assets/chunk-7CWYLC5S-DRgGkkpF.js +0 -2
  485. package/web/assets/chunk-7FYTHRHK-CsmYIswB.js +0 -32
  486. package/web/assets/chunk-KRXBNO2N-C-UNuKKf.js +0 -1
  487. package/web/assets/chunk-LAHSG3N2-Bz4hcn0N.js +0 -321
  488. package/web/assets/chunk-LIEV3EAG-pyKGssV3.js +0 -1
  489. package/web/assets/chunk-NLANEA3F-XpH_NPA5.js +0 -62
  490. package/web/assets/chunk-PE7DX7ZZ-CCvRVj43.js +0 -10
  491. package/web/assets/chunk-Q67WD55A-BWCH2SVz.js +0 -156
  492. package/web/assets/chunk-SZD42YQK-ijyKKVr1.js +0 -70
  493. package/web/assets/chunk-TFPGFQPA-Dj6a-0pC.js +0 -231
  494. package/web/assets/chunk-TGVD4F4B-BMc31Gj2.js +0 -2
  495. package/web/assets/chunk-UAT7B5JY-DW-A_fpI.js +0 -1
  496. package/web/assets/chunk-VY5UBI4V-yxPQAWxw.js +0 -1
  497. package/web/assets/chunk-WJBAP47W-BckNkuEN.js +0 -1
  498. package/web/assets/classDiagram-VPOSVPXH-BjOiXA_C.js +0 -1
  499. package/web/assets/classDiagram-v2-5VFNUXQA-BjOiXA_C.js +0 -1
  500. package/web/assets/cynefin-VYW2F7L2-2VAHYKR6-DAsF6GZv.js +0 -1
  501. package/web/assets/dagre-K64A6Z3X-CUwi79mA.js +0 -4
  502. package/web/assets/diagram-2TLGJV2N-C2F5Nfym.js +0 -30
  503. package/web/assets/diagram-KFBZAVDW-Dfmf_4ti.js +0 -3
  504. package/web/assets/diagram-YP3FGXHI-CyNFYtkd.js +0 -24
  505. package/web/assets/erDiagram-SZLJLQMB-CohQjznC.js +0 -85
  506. package/web/assets/eventmodeling-45OFAUF4-AOUJWSLX-BvTc6iQi.js +0 -1
  507. package/web/assets/flowDiagram-BWE6NHOH-B6RnEvD9.js +0 -1
  508. package/web/assets/ganttDiagram-2QTCJSNI-Cks_mEcF.js +0 -292
  509. package/web/assets/gitGraph-TEB2WS4Q-U5VWYIBC-Dy7CCSZ5.js +0 -1
  510. package/web/assets/gitGraphDiagram-VVZAVV5G-CG41cIVc.js +0 -106
  511. package/web/assets/info-DKCQHKI2-DX6KST54-BLJg1ikX.js +0 -1
  512. package/web/assets/infoDiagram-FY4PZKRE-BJ3vPVwW.js +0 -2
  513. package/web/assets/katex-C5OPUE3Q-Bnbavtmu.js +0 -257
  514. package/web/assets/packet-7NZHBO7P-5WG45QPS-KUxcuvaA.js +0 -1
  515. package/web/assets/pie-RZYD4A2V-2OCZ2VTD-DPs08kJY.js +0 -1
  516. package/web/assets/pieDiagram-YPY3TKG5-BuT3MYzm.js +0 -39
  517. package/web/assets/radar-I7S5WNFK-EPLSA35B-w5F0K3a7.js +0 -1
  518. package/web/assets/railroad-3IZDKUUU-PWFQV6Y6-Bu668yqY.js +0 -1
  519. package/web/assets/railroad-abnf-AHOZXSZD-JYYJX3BS-B6ghpETS.js +0 -1
  520. package/web/assets/railroad-ebnf-EBAXGLYW-RBZ4ONAP-D-XmHh3M.js +0 -1
  521. package/web/assets/railroad-peg-LSFZ7HO6-KHNPXGPG-mhkrhzEn.js +0 -1
  522. package/web/assets/src-C5sx_gMp.js +0 -1
  523. package/web/assets/stateDiagram-DODFXNG5-DTdHc-fF.js +0 -1
  524. package/web/assets/stateDiagram-v2-JNMM5JRA-DioNLDDo.js +0 -1
  525. package/web/assets/swimlanes-OSWOYLSK-CA0ofdwY.js +0 -2
  526. package/web/assets/swimlanesDiagram-RHGX2RDE-PkwUdckC.js +0 -8
  527. package/web/assets/treeView-QDETBFTQ-EHYTSQNO-C5lpBgPm.js +0 -1
  528. package/web/assets/treemap-6X3UGDF4-CCWCF7EK-BB0ZyO-B.js +0 -1
  529. package/web/assets/wardley-OPB4EBWU-6OWERDVT-CEA3Nq0j.js +0 -1
  530. package/web/assets/wardleyDiagram-XK6FUAWV-CVV0dHFr.js +0 -78
  531. /package/web/assets/{sizeCapture-5GORAGP4-BEqQzsxs.js → sizeCapture-FGA5AGYP-BEqQzsxs.js} +0 -0
@@ -0,0 +1,33 @@
1
+ export declare const AUTO_SYNC_CONFIG_FILE = "watch_config.yml";
2
+ export declare const DEFAULT_ANALYZE_FAILURE_THRESHOLD = 3;
3
+ export interface AutoSyncProjectConfig {
4
+ localPath: string;
5
+ groupName?: string;
6
+ overwriteLocalChanges: boolean;
7
+ branches: string[];
8
+ remoteUrls: string[];
9
+ }
10
+ export interface AutoSyncConfig {
11
+ configPath: string;
12
+ syncIntervalMinutes: number;
13
+ repoGitTimeoutMs: number;
14
+ analyzeTimeoutMs: number;
15
+ maxConcurrency: number;
16
+ analyzeFailureThreshold: number;
17
+ projects: AutoSyncProjectConfig[];
18
+ }
19
+ export type AutoSyncConfigLoadResult = {
20
+ ok: true;
21
+ config: AutoSyncConfig;
22
+ } | {
23
+ ok: false;
24
+ reason: 'missing' | 'unreadable' | 'invalid';
25
+ message: string;
26
+ };
27
+ export declare function getAutoSyncConfigPath(gitnexusDir?: string): string;
28
+ export declare function parseBranchCandidates(branchValue: unknown): string[];
29
+ export declare function loadAutoSyncConfig(configPath?: string): Promise<AutoSyncConfigLoadResult>;
30
+ export declare function parseAutoSyncConfig(content: string, configPath: string): AutoSyncConfig;
31
+ export declare function validateAutoSyncRemoteUrl(remoteUrl: string): void;
32
+ export declare function validateAutoSyncBranchName(branch: string): void;
33
+ export declare function parseDurationMs(value: unknown): number;
@@ -0,0 +1,320 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ import { getGlobalDir } from '../../storage/repo-manager.js';
5
+ import { normalizeConfiguredCloneRoot } from './path-security.js';
6
+ const _require = createRequire(import.meta.url);
7
+ const yaml = _require('js-yaml');
8
+ export const AUTO_SYNC_CONFIG_FILE = 'watch_config.yml';
9
+ const GROUP_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9_-]*$/;
10
+ const MIN_SYNC_INTERVAL_MINUTES = 5;
11
+ const MAX_TIMER_DELAY_MS = 2_147_483_647;
12
+ const MAX_SYNC_INTERVAL_MINUTES = Math.floor(MAX_TIMER_DELAY_MS / 60_000);
13
+ const DEFAULT_REPO_GIT_TIMEOUT_MS = 10_000;
14
+ const DEFAULT_MAX_CONCURRENCY = 1;
15
+ export const DEFAULT_ANALYZE_FAILURE_THRESHOLD = 3;
16
+ const MIN_ANALYZE_FAILURE_THRESHOLD = 2;
17
+ const ALLOWED_REMOTE_HOSTS = new Set(['github.com', 'gitlab.com', 'gitee.com']);
18
+ /**
19
+ * A single clone/pull must fit inside one sync interval and inside an hour.
20
+ * This is also the guard for the unit slip the bare-number rule invites:
21
+ * `repo_git_timeout: 600000` means 600000 SECONDS (~7 days), which clears the
22
+ * Node timer ceiling and would silently disable the timeout.
23
+ */
24
+ const MAX_REPO_GIT_TIMEOUT_MS = 3_600_000;
25
+ // Mirrors REPO_NAME_PATTERN in server/git-clone.ts. Deliberately duplicated
26
+ // rather than imported: git-clone.ts already imports from this module, so the
27
+ // reverse edge would be a cycle.
28
+ const REMOTE_REPO_NAME_PATTERN = /^[a-zA-Z0-9._-]+$/;
29
+ // Same charset for a namespace segment: GitLab subgroups allow exactly these,
30
+ // and excluding separators is what stops a segment smuggling in traversal.
31
+ const REMOTE_PATH_SEGMENT_PATTERN = REMOTE_REPO_NAME_PATTERN;
32
+ export function getAutoSyncConfigPath(gitnexusDir = getGlobalDir()) {
33
+ return path.join(gitnexusDir, AUTO_SYNC_CONFIG_FILE);
34
+ }
35
+ export function parseBranchCandidates(branchValue) {
36
+ const rawItems = Array.isArray(branchValue)
37
+ ? branchValue.flatMap((item) => String(item).split(','))
38
+ : String(branchValue ?? '').split(',');
39
+ const branches = [];
40
+ const seen = new Set();
41
+ for (const item of rawItems) {
42
+ const branch = item.trim();
43
+ if (!branch || seen.has(branch))
44
+ continue;
45
+ seen.add(branch);
46
+ branches.push(branch);
47
+ }
48
+ return branches;
49
+ }
50
+ export async function loadAutoSyncConfig(configPath = getAutoSyncConfigPath()) {
51
+ let content;
52
+ try {
53
+ content = await fs.readFile(configPath, 'utf-8');
54
+ }
55
+ catch (err) {
56
+ const code = err.code;
57
+ if (code === 'ENOENT') {
58
+ return {
59
+ ok: false,
60
+ reason: 'missing',
61
+ message: `[auto-sync] Missing config file: ${configPath}. Auto sync is skipped.`,
62
+ };
63
+ }
64
+ return {
65
+ ok: false,
66
+ reason: 'unreadable',
67
+ message: `[auto-sync] Unable to read config file: ${configPath}. Auto sync is skipped.`,
68
+ };
69
+ }
70
+ try {
71
+ return { ok: true, config: parseAutoSyncConfig(content, configPath) };
72
+ }
73
+ catch (err) {
74
+ return {
75
+ ok: false,
76
+ reason: 'invalid',
77
+ message: `[auto-sync] Invalid watch_config.yml: ${err.message}. Auto sync is skipped.`,
78
+ };
79
+ }
80
+ }
81
+ export function parseAutoSyncConfig(content, configPath) {
82
+ const raw = yaml.load(content, { schema: yaml.JSON_SCHEMA });
83
+ if (!raw || typeof raw !== 'object' || Array.isArray(raw)) {
84
+ throw new Error('expected a YAML object');
85
+ }
86
+ const errors = [];
87
+ const interval = Number(raw.sync_interval_minutes);
88
+ if (!Number.isInteger(interval) || interval <= 0) {
89
+ errors.push('sync_interval_minutes must be a positive integer');
90
+ }
91
+ else if (interval < MIN_SYNC_INTERVAL_MINUTES) {
92
+ errors.push(`sync_interval_minutes must be at least ${MIN_SYNC_INTERVAL_MINUTES}`);
93
+ }
94
+ else if (interval > MAX_SYNC_INTERVAL_MINUTES) {
95
+ errors.push(`sync_interval_minutes must not exceed ${MAX_SYNC_INTERVAL_MINUTES}`);
96
+ }
97
+ // YAML booleans survive JSON_SCHEMA (`true`/`false`). `Number(true) === 1`
98
+ // would otherwise pass the integer check and silently mean concurrency 1.
99
+ let maxConcurrency = DEFAULT_MAX_CONCURRENCY;
100
+ if (raw.max_concurrency !== undefined) {
101
+ if (typeof raw.max_concurrency !== 'number' || !Number.isInteger(raw.max_concurrency)) {
102
+ errors.push('max_concurrency must be a positive integer');
103
+ }
104
+ else if (raw.max_concurrency <= 0) {
105
+ errors.push('max_concurrency must be a positive integer');
106
+ }
107
+ else {
108
+ maxConcurrency = raw.max_concurrency;
109
+ }
110
+ }
111
+ const repoGitTimeoutMs = raw.repo_git_timeout === undefined
112
+ ? DEFAULT_REPO_GIT_TIMEOUT_MS
113
+ : parseDurationMs(raw.repo_git_timeout);
114
+ const maxRepoGitTimeoutMs = Number.isInteger(interval) &&
115
+ interval >= MIN_SYNC_INTERVAL_MINUTES &&
116
+ interval <= MAX_SYNC_INTERVAL_MINUTES
117
+ ? Math.min(interval * 60_000, MAX_REPO_GIT_TIMEOUT_MS)
118
+ : undefined;
119
+ if (!Number.isInteger(repoGitTimeoutMs) || repoGitTimeoutMs <= 0) {
120
+ errors.push('repo_git_timeout must be a positive duration such as 10s');
121
+ }
122
+ else if (repoGitTimeoutMs > MAX_TIMER_DELAY_MS) {
123
+ errors.push(`repo_git_timeout must not exceed ${MAX_TIMER_DELAY_MS}ms`);
124
+ }
125
+ else if (maxRepoGitTimeoutMs !== undefined && repoGitTimeoutMs > maxRepoGitTimeoutMs) {
126
+ errors.push(`repo_git_timeout must not exceed ${maxRepoGitTimeoutMs}ms (the lesser of 1h and ` +
127
+ `sync_interval_minutes); a bare number is interpreted as seconds, so use an explicit ` +
128
+ `unit such as 600000ms or 10m`);
129
+ }
130
+ const maxAnalyzeTimeoutMs = Number.isInteger(interval) &&
131
+ interval >= MIN_SYNC_INTERVAL_MINUTES &&
132
+ interval <= MAX_SYNC_INTERVAL_MINUTES
133
+ ? interval * 30_000
134
+ : undefined;
135
+ const analyzeTimeoutMs = raw.analyze_timeout === undefined
136
+ ? (maxAnalyzeTimeoutMs ?? 0)
137
+ : parseDurationMs(raw.analyze_timeout);
138
+ if (!Number.isInteger(analyzeTimeoutMs) || analyzeTimeoutMs <= 0) {
139
+ errors.push('analyze_timeout must be a positive duration such as 30m');
140
+ }
141
+ else if (maxAnalyzeTimeoutMs !== undefined && analyzeTimeoutMs > maxAnalyzeTimeoutMs) {
142
+ errors.push(`analyze_timeout must not exceed half of sync_interval_minutes (${maxAnalyzeTimeoutMs / 60_000}m)`);
143
+ }
144
+ const analyzeFailureThreshold = raw.analyze_failure_threshold === undefined
145
+ ? DEFAULT_ANALYZE_FAILURE_THRESHOLD
146
+ : Number(raw.analyze_failure_threshold);
147
+ if (!Number.isInteger(analyzeFailureThreshold) ||
148
+ analyzeFailureThreshold < MIN_ANALYZE_FAILURE_THRESHOLD) {
149
+ errors.push(`analyze_failure_threshold must be an integer >= ${MIN_ANALYZE_FAILURE_THRESHOLD}`);
150
+ }
151
+ const rawProjects = raw.projects;
152
+ if (!Array.isArray(rawProjects) || rawProjects.length === 0) {
153
+ errors.push('projects must contain at least one project');
154
+ }
155
+ const projects = [];
156
+ if (Array.isArray(rawProjects)) {
157
+ rawProjects.forEach((projectValue, index) => {
158
+ const project = projectValue;
159
+ if (!project || typeof project !== 'object' || Array.isArray(project)) {
160
+ errors.push(`projects[${index}] must be an object`);
161
+ return;
162
+ }
163
+ const localPath = typeof project.local_path === 'string' ? project.local_path.trim() : '';
164
+ if (!localPath) {
165
+ errors.push(`projects[${index}].local_path is required`);
166
+ }
167
+ else {
168
+ try {
169
+ normalizeConfiguredCloneRoot(localPath);
170
+ }
171
+ catch (err) {
172
+ errors.push(`projects[${index}].local_path ${err.message}`);
173
+ }
174
+ }
175
+ const remoteUrls = Array.isArray(project.remote_urls)
176
+ ? project.remote_urls.map((url) => String(url).trim()).filter(Boolean)
177
+ : [];
178
+ if (remoteUrls.length === 0) {
179
+ errors.push(`projects[${index}].remote_urls must contain at least one URL`);
180
+ }
181
+ for (let urlIndex = 0; urlIndex < remoteUrls.length; urlIndex += 1) {
182
+ try {
183
+ validateAutoSyncRemoteUrl(remoteUrls[urlIndex]);
184
+ }
185
+ catch (err) {
186
+ errors.push(`projects[${index}].remote_urls[${urlIndex}] ${err.message}`);
187
+ }
188
+ }
189
+ if (project.branch !== undefined && project.branches !== undefined) {
190
+ errors.push(`projects[${index}] must not set both branch and branches`);
191
+ }
192
+ const branches = parseBranchCandidates(project.branches !== undefined ? project.branches : project.branch);
193
+ if (branches.length === 0)
194
+ errors.push(`projects[${index}].branches is required`);
195
+ for (let branchIndex = 0; branchIndex < branches.length; branchIndex += 1) {
196
+ try {
197
+ validateAutoSyncBranchName(branches[branchIndex]);
198
+ }
199
+ catch (err) {
200
+ errors.push(`projects[${index}].branches[${branchIndex}] ${err.message}`);
201
+ }
202
+ }
203
+ const groupName = typeof project.group_name === 'string' && project.group_name.trim()
204
+ ? project.group_name.trim()
205
+ : undefined;
206
+ if (groupName && !GROUP_NAME_PATTERN.test(groupName)) {
207
+ errors.push(`projects[${index}].group_name is invalid`);
208
+ }
209
+ const overwriteLocalChanges = project.overwrite_local_changes === undefined ? false : project.overwrite_local_changes;
210
+ if (typeof overwriteLocalChanges !== 'boolean') {
211
+ errors.push(`projects[${index}].overwrite_local_changes must be a boolean`);
212
+ }
213
+ if (localPath && remoteUrls.length > 0 && branches.length > 0) {
214
+ projects.push({
215
+ localPath,
216
+ groupName,
217
+ overwriteLocalChanges: overwriteLocalChanges === true,
218
+ branches,
219
+ remoteUrls,
220
+ });
221
+ }
222
+ });
223
+ }
224
+ if (errors.length > 0)
225
+ throw new Error(errors.join('; '));
226
+ return {
227
+ configPath,
228
+ syncIntervalMinutes: interval,
229
+ repoGitTimeoutMs,
230
+ analyzeTimeoutMs,
231
+ maxConcurrency,
232
+ analyzeFailureThreshold,
233
+ projects,
234
+ };
235
+ }
236
+ export function validateAutoSyncRemoteUrl(remoteUrl) {
237
+ const trimmed = remoteUrl.trim();
238
+ if (trimmed.includes('?') || trimmed.includes('#')) {
239
+ throw new Error('must not include query strings or fragments');
240
+ }
241
+ const match = /^git@([^:\s/]+):([^\s]+)$/.exec(trimmed);
242
+ if (!match) {
243
+ throw new Error('must use an SSH URL on github.com, gitlab.com, or gitee.com');
244
+ }
245
+ const host = match[1].toLowerCase();
246
+ const repoPath = match[2];
247
+ if (!ALLOWED_REMOTE_HOSTS.has(host)) {
248
+ throw new Error('host must be one of github.com, gitlab.com, or gitee.com');
249
+ }
250
+ const pathParts = repoPath.split('/');
251
+ // Every segment becomes a directory component: the namespace segments build
252
+ // the clone path and the last one names the repo. So each is held to the same
253
+ // charset, which is what keeps a separator out of a segment — on Windows
254
+ // `..\..\outside` is traversal even though the segment is not literally `..`,
255
+ // and testing the raw string for `..` instead would reject an ordinary
256
+ // `foo..bar`. Traversal is a whole segment; a separator is a character.
257
+ const namespaceParts = pathParts.slice(0, -1);
258
+ if (repoPath.startsWith('/') ||
259
+ pathParts.length < 2 ||
260
+ pathParts.some((part) => !part || part === '.' || part === '..') ||
261
+ namespaceParts.some((part) => !REMOTE_PATH_SEGMENT_PATTERN.test(part))) {
262
+ throw new Error('path must include owner/repo without traversal');
263
+ }
264
+ // The final segment becomes the on-disk clone directory via `extractRepoName`,
265
+ // whose name rules are stricter than the path check above: a backslash — or
266
+ // anything outside `[A-Za-z0-9._-]` — passes here and then throws once per
267
+ // tick inside the sync loop instead of at config load. These rules are a
268
+ // strict superset, so anything accepted here is accepted there.
269
+ const lastSegment = pathParts[pathParts.length - 1];
270
+ const repoName = /\.git$/i.test(lastSegment) ? lastSegment.slice(0, -4) : lastSegment;
271
+ if (!repoName ||
272
+ repoName === '.' ||
273
+ repoName === '..' ||
274
+ repoName === 'unknown' ||
275
+ repoName.startsWith('-') ||
276
+ !REMOTE_REPO_NAME_PATTERN.test(repoName)) {
277
+ throw new Error('repository name must use only letters, digits, ".", "_", or "-" and must not be "unknown"');
278
+ }
279
+ }
280
+ export function validateAutoSyncBranchName(branch) {
281
+ if (!branch.trim())
282
+ throw new Error('must not be empty');
283
+ if (/[\s\0-\x1f\x7f]/.test(branch))
284
+ throw new Error('must not contain whitespace or control characters');
285
+ if (/[~^:?*[\\]/.test(branch))
286
+ throw new Error('contains characters not allowed in a git ref');
287
+ if (branch.startsWith('-'))
288
+ throw new Error('must not start with "-"');
289
+ if (branch.startsWith('/'))
290
+ throw new Error('must not start with "/"');
291
+ if (branch.includes('..'))
292
+ throw new Error('must not contain ".."');
293
+ if (branch.includes('`'))
294
+ throw new Error('must not contain backticks');
295
+ if (branch.endsWith('/') || branch.endsWith('.'))
296
+ throw new Error('must not end with "/" or "."');
297
+ if (branch.includes('//'))
298
+ throw new Error('must not contain consecutive slashes');
299
+ if (branch.includes('@{'))
300
+ throw new Error('must not contain "@{"');
301
+ if (branch
302
+ .split('/')
303
+ .some((component) => component.startsWith('.') || component.endsWith('.') || component.endsWith('.lock')))
304
+ throw new Error('must not contain hidden, trailing-dot, or .lock path components');
305
+ }
306
+ export function parseDurationMs(value) {
307
+ if (typeof value === 'number')
308
+ return value * 1_000;
309
+ const raw = String(value ?? '').trim();
310
+ const match = /^(\d+)(ms|s|m)?$/.exec(raw);
311
+ if (!match)
312
+ return Number.NaN;
313
+ const amount = Number(match[1]);
314
+ const unit = match[2] ?? 's';
315
+ if (unit === 'ms')
316
+ return amount;
317
+ if (unit === 's')
318
+ return amount * 1_000;
319
+ return amount * 60_000;
320
+ }
@@ -0,0 +1,6 @@
1
+ export { AUTO_SYNC_CONFIG_FILE, getAutoSyncConfigPath, loadAutoSyncConfig, parseAutoSyncConfig, parseBranchCandidates, parseDurationMs, validateAutoSyncBranchName, validateAutoSyncRemoteUrl, type AutoSyncConfig, type AutoSyncConfigLoadResult, type AutoSyncProjectConfig, } from './config.js';
2
+ export { buildStateKey, getAutoSyncMutexPath, getAutoSyncWatchDir, getAutoSyncStatePath, getProjectCommitInfoPath, loadAutoSyncState, resetAutoSyncState, saveAutoSyncState, shouldAnalyzeCommit, writeProjectCommitInfo, type AutoSyncAnalyzeStatus, type AutoSyncCommitState, type AutoSyncCommitStateEntry, type ProjectCommitInfoEntry, } from './state.js';
3
+ export { extractRepoNameFromRemoteUrl } from './repo.js';
4
+ export { normalizeConfiguredCloneRoot, quarantineAutoSyncPartial, resolveConfiguredCloneRoot, type AutoSyncCloneRoot, } from './path-security.js';
5
+ export { addRepoToGroup, getAutoSyncRepoIdentity, getConfiguredRepoPath, resolveActualConcurrency, runAutoSyncOnce, syncGroupByName, type AutoSyncLogger, type AutoSyncRunDeps, type AutoSyncRunResult, } from './runner.js';
6
+ export { getAutoSyncWatchPaths, readAutoSyncWatchStatus, startAutoSyncWatch, stopAutoSyncWatch, type AutoSyncStartHandle, type AutoSyncWatchStopResult, type AutoSyncWatchPaths, type WatchStatusRecord, } from './starter.js';
@@ -0,0 +1,6 @@
1
+ export { AUTO_SYNC_CONFIG_FILE, getAutoSyncConfigPath, loadAutoSyncConfig, parseAutoSyncConfig, parseBranchCandidates, parseDurationMs, validateAutoSyncBranchName, validateAutoSyncRemoteUrl, } from './config.js';
2
+ export { buildStateKey, getAutoSyncMutexPath, getAutoSyncWatchDir, getAutoSyncStatePath, getProjectCommitInfoPath, loadAutoSyncState, resetAutoSyncState, saveAutoSyncState, shouldAnalyzeCommit, writeProjectCommitInfo, } from './state.js';
3
+ export { extractRepoNameFromRemoteUrl } from './repo.js';
4
+ export { normalizeConfiguredCloneRoot, quarantineAutoSyncPartial, resolveConfiguredCloneRoot, } from './path-security.js';
5
+ export { addRepoToGroup, getAutoSyncRepoIdentity, getConfiguredRepoPath, resolveActualConcurrency, runAutoSyncOnce, syncGroupByName, } from './runner.js';
6
+ export { getAutoSyncWatchPaths, readAutoSyncWatchStatus, startAutoSyncWatch, stopAutoSyncWatch, } from './starter.js';
@@ -0,0 +1,9 @@
1
+ export interface AutoSyncCloneRoot {
2
+ root: string;
3
+ quarantineRoot: string;
4
+ quarantineRetentionDays: number;
5
+ }
6
+ export declare function resolveConfiguredCloneRoot(localPath: string): Promise<AutoSyncCloneRoot>;
7
+ export declare function normalizeConfiguredCloneRoot(localPath: string): string;
8
+ export declare function quarantineAutoSyncPartial(targetDir: string, quarantineRoot: string): Promise<string>;
9
+ export declare function assertDirectoryOwnerAndPermissions(root: string): Promise<void>;
@@ -0,0 +1,251 @@
1
+ import fs from 'node:fs/promises';
2
+ import { randomUUID } from 'node:crypto';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { getGlobalDir } from '../../storage/repo-manager.js';
6
+ import { getAutoSyncWatchDir } from './state.js';
7
+ const WINDOWS_DANGEROUS_ROOTS = process.platform === 'win32'
8
+ ? [
9
+ process.env.SystemRoot,
10
+ process.env.ProgramData,
11
+ process.env.ProgramFiles,
12
+ process.env['ProgramFiles(x86)'],
13
+ ].filter((entry) => Boolean(entry))
14
+ : [];
15
+ const DANGEROUS_ROOTS = new Set([
16
+ '/',
17
+ os.homedir(),
18
+ os.tmpdir(),
19
+ '/bin',
20
+ '/boot',
21
+ '/dev',
22
+ '/etc',
23
+ '/lib',
24
+ '/lib64',
25
+ '/opt',
26
+ '/proc',
27
+ '/private/tmp',
28
+ '/private/var',
29
+ '/root',
30
+ '/sbin',
31
+ '/sys',
32
+ '/tmp',
33
+ '/usr',
34
+ '/var',
35
+ ...WINDOWS_DANGEROUS_ROOTS,
36
+ ].map((entry) => path.resolve(entry)));
37
+ const DANGEROUS_PARENT_ROOTS = new Set([
38
+ os.tmpdir(),
39
+ '/bin',
40
+ '/boot',
41
+ '/dev',
42
+ '/etc',
43
+ '/lib',
44
+ '/lib64',
45
+ '/opt',
46
+ '/proc',
47
+ '/private/tmp',
48
+ '/private/var',
49
+ '/root',
50
+ '/sbin',
51
+ '/sys',
52
+ '/tmp',
53
+ '/usr',
54
+ '/var',
55
+ ...WINDOWS_DANGEROUS_ROOTS,
56
+ ].map((entry) => path.resolve(entry)));
57
+ const QUARANTINE_RETENTION_DAYS = 14;
58
+ const QUARANTINE_MAX_ENTRIES_PER_REPO = 5;
59
+ // `auto-sync-<stamp>-<pid>-<uuid>-<repo basename>` — see quarantineAutoSyncPartial.
60
+ // The UUID is the only fixed-shape field, so it anchors the grouping key, and
61
+ // everything after it is the basename (`[A-Za-z0-9._-]` by construction).
62
+ const QUARANTINE_ENTRY_PATTERN = /^auto-sync-.+-\d+-[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}-(.+)$/i;
63
+ export async function resolveConfiguredCloneRoot(localPath) {
64
+ const root = normalizeConfiguredCloneRoot(localPath);
65
+ assertNotDangerousRoot(root);
66
+ await assertNoSymlinkPath(root);
67
+ await fs.mkdir(root, { recursive: true });
68
+ await assertDirectoryOwnerAndPermissions(root);
69
+ const realRoot = await fs.realpath(root);
70
+ assertContainedOrSame(root, realRoot, 'Configured clone root realpath escaped its normalized path');
71
+ assertNotDangerousRoot(realRoot);
72
+ assertNotGitNexusInternalRoot(realRoot);
73
+ const quarantineRoot = path.join(getAutoSyncWatchDir(), 'quarantine');
74
+ await pruneQuarantineEntries(quarantineRoot);
75
+ return {
76
+ root: realRoot,
77
+ quarantineRoot,
78
+ quarantineRetentionDays: QUARANTINE_RETENTION_DAYS,
79
+ };
80
+ }
81
+ export function normalizeConfiguredCloneRoot(localPath) {
82
+ const value = localPath.trim();
83
+ if (!value)
84
+ throw new Error('local_path is required');
85
+ if (!path.isAbsolute(value))
86
+ throw new Error('local_path must be an absolute path');
87
+ if (value.split(path.sep).includes('..')) {
88
+ throw new Error('local_path must be normalized and must not contain traversal segments');
89
+ }
90
+ const resolved = path.resolve(value);
91
+ if (resolved !== path.normalize(value)) {
92
+ throw new Error('local_path must be normalized and must not contain traversal segments');
93
+ }
94
+ return resolved;
95
+ }
96
+ export async function quarantineAutoSyncPartial(targetDir, quarantineRoot) {
97
+ await fs.mkdir(quarantineRoot, { recursive: true, mode: 0o700 });
98
+ const base = path.basename(targetDir);
99
+ const stamp = new Date().toISOString().replace(/[:.]/g, '-');
100
+ const destination = path.join(quarantineRoot, `auto-sync-${stamp}-${process.pid}-${randomUUID()}-${base}`);
101
+ try {
102
+ await fs.rename(targetDir, destination);
103
+ }
104
+ catch (err) {
105
+ if (err.code !== 'EXDEV')
106
+ throw err;
107
+ await fs.cp(targetDir, destination, { recursive: true });
108
+ await fs.rm(targetDir, { recursive: true, force: true });
109
+ }
110
+ await fs.writeFile(`${destination}.README.txt`, [
111
+ 'GitNexus auto-sync isolated a partial or unsafe clone result.',
112
+ `Created at: ${new Date().toISOString()}`,
113
+ `Original path: ${targetDir}`,
114
+ `Retention: keep for ${QUARANTINE_RETENTION_DAYS} days unless an operator reviews and removes it earlier.`,
115
+ 'Cleanup: verify the original path and remote before manual deletion.',
116
+ '',
117
+ ].join('\n'), 'utf-8');
118
+ return destination;
119
+ }
120
+ async function pruneQuarantineEntries(quarantineRoot) {
121
+ const cutoff = Date.now() - QUARANTINE_RETENTION_DAYS * 24 * 60 * 60 * 1_000;
122
+ // readdir and stat both resolve through a link, so a symlinked quarantine
123
+ // root would age-sweep and delete entries somewhere else entirely.
124
+ const rootStat = await fs.lstat(quarantineRoot).catch(() => undefined);
125
+ if (rootStat?.isSymbolicLink()) {
126
+ throw new Error(`Refusing symlinked auto-sync quarantine root: ${quarantineRoot}`);
127
+ }
128
+ let entries;
129
+ try {
130
+ entries = await fs.readdir(quarantineRoot);
131
+ }
132
+ catch (err) {
133
+ if (err.code === 'ENOENT')
134
+ return;
135
+ throw err;
136
+ }
137
+ const survivors = (await Promise.all(entries
138
+ .filter((entry) => entry.startsWith('auto-sync-'))
139
+ .map(async (entry) => {
140
+ const entryPath = path.join(quarantineRoot, entry);
141
+ const stat = await fs.stat(entryPath).catch(() => undefined);
142
+ if (stat && stat.mtimeMs < cutoff) {
143
+ await fs.rm(entryPath, { recursive: true, force: true });
144
+ return undefined;
145
+ }
146
+ return entry;
147
+ }))).filter((entry) => entry !== undefined);
148
+ // Age alone never bounds a repo that fails on every tick: one partial clone
149
+ // per tick stays inside the retention window forever. Keep the newest few per
150
+ // repo. Entries that do not match the generated naming scheme (operator
151
+ // notes, names from another version) are left to the age sweep alone.
152
+ const byRepo = new Map();
153
+ for (const entry of survivors) {
154
+ if (entry.endsWith('.README.txt'))
155
+ continue;
156
+ const repo = QUARANTINE_ENTRY_PATTERN.exec(entry)?.[1];
157
+ if (!repo)
158
+ continue;
159
+ const group = byRepo.get(repo) ?? [];
160
+ group.push(entry);
161
+ byRepo.set(repo, group);
162
+ }
163
+ await Promise.all([...byRepo.values()].flatMap((group) => group
164
+ // The timestamp is the leading fixed-width field, so a descending
165
+ // string sort is newest-first.
166
+ .sort((a, b) => (a < b ? 1 : a > b ? -1 : 0))
167
+ .slice(QUARANTINE_MAX_ENTRIES_PER_REPO)
168
+ .map(async (entry) => {
169
+ await fs.rm(path.join(quarantineRoot, entry), { recursive: true, force: true });
170
+ await fs.rm(path.join(quarantineRoot, `${entry}.README.txt`), { force: true });
171
+ })));
172
+ }
173
+ function assertNotDangerousRoot(root) {
174
+ if (root === path.resolve(getGlobalDir(), 'repos'))
175
+ return;
176
+ if (DANGEROUS_ROOTS.has(root))
177
+ throw new Error(`Refusing unsafe auto-sync clone root: ${root}`);
178
+ for (const dangerousRoot of DANGEROUS_PARENT_ROOTS) {
179
+ const rel = path.relative(dangerousRoot, root);
180
+ if (rel && !rel.startsWith('..') && !path.isAbsolute(rel)) {
181
+ throw new Error(`Refusing unsafe auto-sync clone root under ${dangerousRoot}: ${root}`);
182
+ }
183
+ }
184
+ if (path.parse(root).root === root)
185
+ throw new Error(`Refusing filesystem root as clone root: ${root}`);
186
+ }
187
+ function assertNotGitNexusInternalRoot(root) {
188
+ const gitnexusDir = path.resolve(getGlobalDir());
189
+ const blocked = [
190
+ path.join(gitnexusDir, 'groups'),
191
+ path.join(gitnexusDir, 'indexes'),
192
+ path.join(gitnexusDir, 'quarantine'),
193
+ path.join(getAutoSyncWatchDir(gitnexusDir), 'quarantine'),
194
+ ];
195
+ for (const blockedRoot of blocked) {
196
+ const rel = path.relative(blockedRoot, root);
197
+ if (!rel || (!rel.startsWith('..') && !path.isAbsolute(rel))) {
198
+ throw new Error(`Refusing GitNexus internal directory as auto-sync clone root: ${root}`);
199
+ }
200
+ }
201
+ }
202
+ async function assertNoSymlinkPath(root) {
203
+ const parsed = path.parse(root);
204
+ let current = parsed.root;
205
+ const parts = root.slice(parsed.root.length).split(path.sep).filter(Boolean);
206
+ for (const part of parts) {
207
+ current = path.join(current, part);
208
+ let stat;
209
+ try {
210
+ stat = await fs.lstat(current);
211
+ }
212
+ catch (err) {
213
+ if (err.code === 'ENOENT')
214
+ break;
215
+ throw err;
216
+ }
217
+ if (stat.isSymbolicLink())
218
+ throw new Error(`Refusing symlink in auto-sync clone root path: ${current}`);
219
+ }
220
+ }
221
+ export async function assertDirectoryOwnerAndPermissions(root) {
222
+ const stat = await fs.stat(root);
223
+ if (!stat.isDirectory())
224
+ throw new Error(`auto-sync clone root is not a directory: ${root}`);
225
+ // POSIX uid/mode have no meaning on Windows, and this runs on every tick for
226
+ // every project, so throwing here failed 100% of repos forever while `watch
227
+ // status` still read `running`. Skip the ownership assertions rather than the
228
+ // whole feature: the caller's other guards — dangerous-root rejection
229
+ // (including the Windows system roots), symlink refusal, realpath containment
230
+ // and the GitNexus-internal-root check — all still apply, and managed git runs
231
+ // with `core.hooksPath` pinned to the null device.
232
+ if (process.platform === 'win32')
233
+ return;
234
+ if (typeof process.getuid === 'function' && stat.uid !== process.getuid()) {
235
+ throw new Error(`auto-sync clone root is owned by uid ${stat.uid}, not current process uid`);
236
+ }
237
+ const mode = stat.mode & 0o777;
238
+ const groupWritable = (mode & 0o020) !== 0;
239
+ const worldWritable = (mode & 0o002) !== 0;
240
+ if (worldWritable) {
241
+ throw new Error(`Refusing world-writable auto-sync clone root: ${root}`);
242
+ }
243
+ if (groupWritable) {
244
+ throw new Error(`Refusing group-writable auto-sync clone root: ${root}`);
245
+ }
246
+ }
247
+ function assertContainedOrSame(root, child, message) {
248
+ const rel = path.relative(root, child);
249
+ if (rel.startsWith('..') || path.isAbsolute(rel))
250
+ throw new Error(message);
251
+ }
@@ -0,0 +1 @@
1
+ export declare function extractRepoNameFromRemoteUrl(remoteUrl: string): string;
@@ -0,0 +1,6 @@
1
+ import { extractRepoName } from '../../server/git-clone.js';
2
+ import { validateAutoSyncRemoteUrl } from './config.js';
3
+ export function extractRepoNameFromRemoteUrl(remoteUrl) {
4
+ validateAutoSyncRemoteUrl(remoteUrl);
5
+ return extractRepoName(remoteUrl);
6
+ }