gitnexus 1.6.8-rc.8 → 1.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/_shared/graph/types.d.ts +31 -1
- package/dist/_shared/graph/types.d.ts.map +1 -1
- package/dist/_shared/language-detection.d.ts.map +1 -1
- package/dist/_shared/language-detection.js +11 -1
- package/dist/_shared/language-detection.js.map +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts +1 -1
- package/dist/_shared/lbug/schema-constants.d.ts.map +1 -1
- package/dist/_shared/lbug/schema-constants.js +6 -0
- package/dist/_shared/lbug/schema-constants.js.map +1 -1
- package/dist/_shared/scope-resolution/parsed-file.d.ts +21 -0
- package/dist/_shared/scope-resolution/parsed-file.d.ts.map +1 -1
- package/dist/_shared/scope-resolution/symbol-definition.d.ts +6 -0
- package/dist/_shared/scope-resolution/symbol-definition.d.ts.map +1 -1
- package/dist/cli/ai-context.d.ts +31 -1
- package/dist/cli/ai-context.js +18 -16
- package/dist/cli/analyze-config.js +11 -0
- package/dist/cli/analyze.d.ts +14 -0
- package/dist/cli/analyze.js +98 -0
- package/dist/cli/embedding-dims.d.ts +33 -0
- package/dist/cli/embedding-dims.js +39 -0
- package/dist/cli/eval-server.d.ts +8 -0
- package/dist/cli/eval-server.js +242 -2
- package/dist/cli/help-i18n.js +19 -0
- package/dist/cli/i18n/en.d.ts +15 -1
- package/dist/cli/i18n/en.js +15 -1
- package/dist/cli/i18n/resources.d.ts +29 -1
- package/dist/cli/i18n/zh-CN.d.ts +14 -0
- package/dist/cli/i18n/zh-CN.js +15 -1
- package/dist/cli/index.js +89 -1
- package/dist/cli/mcp.d.ts +6 -1
- package/dist/cli/mcp.js +26 -1
- package/dist/cli/optional-grammars.d.ts +6 -8
- package/dist/cli/optional-grammars.js +8 -11
- package/dist/cli/setup.d.ts +3 -1
- package/dist/cli/setup.js +67 -17
- package/dist/cli/skill-gen.js +2 -1
- package/dist/cli/tool.d.ts +19 -1
- package/dist/cli/tool.js +96 -3
- package/dist/core/embeddings/hf-env.d.ts +0 -66
- package/dist/core/embeddings/http-client.d.ts +7 -0
- package/dist/core/embeddings/http-client.js +5 -3
- package/dist/core/embeddings/server-mapping.d.ts +1 -1
- package/dist/core/embeddings/server-mapping.js +7 -3
- package/dist/core/graph/import-cycles.d.ts +10 -0
- package/dist/core/graph/import-cycles.js +103 -0
- package/dist/core/group/cross-impact.d.ts +2 -1
- package/dist/core/group/cross-impact.js +5 -1
- package/dist/core/group/extractors/grpc-patterns/proto.js +10 -6
- package/dist/core/group/extractors/http-patterns/kotlin.js +7 -7
- package/dist/core/group/extractors/http-route-extractor.d.ts +24 -0
- package/dist/core/group/extractors/http-route-extractor.js +22 -4
- package/dist/core/group/extractors/include-extractor.js +22 -13
- package/dist/core/group/sync.d.ts +27 -1
- package/dist/core/group/sync.js +0 -0
- package/dist/core/incremental/subgraph-extract.js +18 -1
- package/dist/core/ingestion/cfg/cfg-builder.d.ts +114 -0
- package/dist/core/ingestion/cfg/cfg-builder.js +196 -0
- package/dist/core/ingestion/cfg/collect.d.ts +51 -0
- package/dist/core/ingestion/cfg/collect.js +77 -0
- package/dist/core/ingestion/cfg/control-dependence.d.ts +58 -0
- package/dist/core/ingestion/cfg/control-dependence.js +182 -0
- package/dist/core/ingestion/cfg/control-flow-context.d.ts +106 -0
- package/dist/core/ingestion/cfg/control-flow-context.js +124 -0
- package/dist/core/ingestion/cfg/emit.d.ts +293 -0
- package/dist/core/ingestion/cfg/emit.js +646 -0
- package/dist/core/ingestion/cfg/post-dominators.d.ts +79 -0
- package/dist/core/ingestion/cfg/post-dominators.js +176 -0
- package/dist/core/ingestion/cfg/reaching-def-reason-codec.d.ts +106 -0
- package/dist/core/ingestion/cfg/reaching-def-reason-codec.js +133 -0
- package/dist/core/ingestion/cfg/reaching-defs-graph.d.ts +18 -0
- package/dist/core/ingestion/cfg/reaching-defs-graph.js +312 -0
- package/dist/core/ingestion/cfg/reaching-defs.d.ts +162 -0
- package/dist/core/ingestion/cfg/reaching-defs.js +785 -0
- package/dist/core/ingestion/cfg/synthetic-escape.d.ts +124 -0
- package/dist/core/ingestion/cfg/synthetic-escape.js +300 -0
- package/dist/core/ingestion/cfg/traversal-result.d.ts +20 -0
- package/dist/core/ingestion/cfg/traversal-result.js +2 -0
- package/dist/core/ingestion/cfg/types.d.ts +274 -0
- package/dist/core/ingestion/cfg/types.js +13 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.d.ts +111 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp-harvest.js +541 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp.d.ts +74 -0
- package/dist/core/ingestion/cfg/visitors/c-cpp.js +584 -0
- package/dist/core/ingestion/cfg/visitors/call-site-harvest.d.ts +195 -0
- package/dist/core/ingestion/cfg/visitors/call-site-harvest.js +350 -0
- package/dist/core/ingestion/cfg/visitors/csharp-harvest.d.ts +124 -0
- package/dist/core/ingestion/cfg/visitors/csharp-harvest.js +589 -0
- package/dist/core/ingestion/cfg/visitors/csharp.d.ts +87 -0
- package/dist/core/ingestion/cfg/visitors/csharp.js +867 -0
- package/dist/core/ingestion/cfg/visitors/dart-harvest.d.ts +278 -0
- package/dist/core/ingestion/cfg/visitors/dart-harvest.js +870 -0
- package/dist/core/ingestion/cfg/visitors/dart.d.ts +119 -0
- package/dist/core/ingestion/cfg/visitors/dart.js +836 -0
- package/dist/core/ingestion/cfg/visitors/go-harvest.d.ts +160 -0
- package/dist/core/ingestion/cfg/visitors/go-harvest.js +621 -0
- package/dist/core/ingestion/cfg/visitors/go.d.ts +108 -0
- package/dist/core/ingestion/cfg/visitors/go.js +638 -0
- package/dist/core/ingestion/cfg/visitors/java-harvest.d.ts +108 -0
- package/dist/core/ingestion/cfg/visitors/java-harvest.js +513 -0
- package/dist/core/ingestion/cfg/visitors/java.d.ts +101 -0
- package/dist/core/ingestion/cfg/visitors/java.js +812 -0
- package/dist/core/ingestion/cfg/visitors/kotlin-harvest.d.ts +253 -0
- package/dist/core/ingestion/cfg/visitors/kotlin-harvest.js +719 -0
- package/dist/core/ingestion/cfg/visitors/kotlin.d.ts +121 -0
- package/dist/core/ingestion/cfg/visitors/kotlin.js +809 -0
- package/dist/core/ingestion/cfg/visitors/php-harvest.d.ts +172 -0
- package/dist/core/ingestion/cfg/visitors/php-harvest.js +626 -0
- package/dist/core/ingestion/cfg/visitors/php.d.ts +96 -0
- package/dist/core/ingestion/cfg/visitors/php.js +721 -0
- package/dist/core/ingestion/cfg/visitors/python-harvest.d.ts +212 -0
- package/dist/core/ingestion/cfg/visitors/python-harvest.js +739 -0
- package/dist/core/ingestion/cfg/visitors/python.d.ts +103 -0
- package/dist/core/ingestion/cfg/visitors/python.js +558 -0
- package/dist/core/ingestion/cfg/visitors/ruby-harvest.d.ts +223 -0
- package/dist/core/ingestion/cfg/visitors/ruby-harvest.js +587 -0
- package/dist/core/ingestion/cfg/visitors/ruby.d.ts +112 -0
- package/dist/core/ingestion/cfg/visitors/ruby.js +756 -0
- package/dist/core/ingestion/cfg/visitors/rust-harvest.d.ts +324 -0
- package/dist/core/ingestion/cfg/visitors/rust-harvest.js +873 -0
- package/dist/core/ingestion/cfg/visitors/rust.d.ts +95 -0
- package/dist/core/ingestion/cfg/visitors/rust.js +558 -0
- package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.d.ts +87 -0
- package/dist/core/ingestion/cfg/visitors/scope-tree-harvest.js +116 -0
- package/dist/core/ingestion/cfg/visitors/swift-harvest.d.ts +255 -0
- package/dist/core/ingestion/cfg/visitors/swift-harvest.js +679 -0
- package/dist/core/ingestion/cfg/visitors/swift.d.ts +116 -0
- package/dist/core/ingestion/cfg/visitors/swift.js +787 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.d.ts +149 -0
- package/dist/core/ingestion/cfg/visitors/typescript-harvest.js +1056 -0
- package/dist/core/ingestion/cfg/visitors/typescript.d.ts +66 -0
- package/dist/core/ingestion/cfg/visitors/typescript.js +583 -0
- package/dist/core/ingestion/import-resolvers/utils.js +2 -0
- package/dist/core/ingestion/language-provider.d.ts +23 -2
- package/dist/core/ingestion/languages/c-cpp.js +17 -5
- package/dist/core/ingestion/languages/cobol.js +4 -0
- package/dist/core/ingestion/languages/cpp/arity-metadata.js +41 -2
- package/dist/core/ingestion/languages/cpp/captures.js +50 -1
- package/dist/core/ingestion/languages/cpp/conversion-rank.d.ts +2 -0
- package/dist/core/ingestion/languages/cpp/conversion-rank.js +89 -0
- package/dist/core/ingestion/languages/cpp/header-scan.d.ts +1 -1
- package/dist/core/ingestion/languages/cpp/header-scan.js +2 -2
- package/dist/core/ingestion/languages/cpp/inline-namespaces.js +7 -2
- package/dist/core/ingestion/languages/cpp/member-lookup.js +2 -1
- package/dist/core/ingestion/languages/cpp/query.js +23 -0
- package/dist/core/ingestion/languages/cpp/scope-resolver.js +2 -1
- package/dist/core/ingestion/languages/csharp.js +2 -0
- package/dist/core/ingestion/languages/dart.js +2 -0
- package/dist/core/ingestion/languages/go.js +2 -0
- package/dist/core/ingestion/languages/java.js +3 -0
- package/dist/core/ingestion/languages/kotlin/query.js +3 -2
- package/dist/core/ingestion/languages/kotlin.js +8 -1
- package/dist/core/ingestion/languages/php.js +2 -0
- package/dist/core/ingestion/languages/python.js +2 -0
- package/dist/core/ingestion/languages/ruby.js +2 -0
- package/dist/core/ingestion/languages/rust.js +2 -0
- package/dist/core/ingestion/languages/swift.js +2 -0
- package/dist/core/ingestion/languages/typescript.js +5 -0
- package/dist/core/ingestion/languages/vue.js +6 -0
- package/dist/core/ingestion/method-extractors/configs/c-cpp.js +6 -13
- package/dist/core/ingestion/method-extractors/generic.js +1 -0
- package/dist/core/ingestion/method-types.d.ts +2 -0
- package/dist/core/ingestion/model/symbol-table.d.ts +1 -0
- package/dist/core/ingestion/model/symbol-table.js +1 -0
- package/dist/core/ingestion/mro-processor.js +109 -0
- package/dist/core/ingestion/parsing-processor.js +44 -0
- package/dist/core/ingestion/pipeline-phases/call-summaries.d.ts +26 -0
- package/dist/core/ingestion/pipeline-phases/call-summaries.js +46 -0
- package/dist/core/ingestion/pipeline-phases/index.d.ts +2 -0
- package/dist/core/ingestion/pipeline-phases/index.js +2 -0
- package/dist/core/ingestion/pipeline-phases/parse-impl.d.ts +0 -21
- package/dist/core/ingestion/pipeline-phases/parse-impl.js +18 -1
- package/dist/core/ingestion/pipeline-phases/routes.d.ts +10 -0
- package/dist/core/ingestion/pipeline-phases/routes.js +30 -1
- package/dist/core/ingestion/pipeline-phases/taint-summaries.d.ts +30 -0
- package/dist/core/ingestion/pipeline-phases/taint-summaries.js +81 -0
- package/dist/core/ingestion/pipeline.d.ts +92 -10
- package/dist/core/ingestion/pipeline.js +11 -2
- package/dist/core/ingestion/scope-extractor.js +12 -2
- package/dist/core/ingestion/scope-resolution/contract/scope-resolver.d.ts +9 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/callee-id-sink.js +67 -0
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.d.ts +12 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/edges.js +14 -2
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.d.ts +6 -1
- package/dist/core/ingestion/scope-resolution/graph-bridge/references-to-edges.js +13 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.d.ts +9 -1
- package/dist/core/ingestion/scope-resolution/passes/free-call-fallback.js +28 -2
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.d.ts +4 -0
- package/dist/core/ingestion/scope-resolution/passes/overload-narrowing.js +9 -0
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.d.ts +6 -1
- package/dist/core/ingestion/scope-resolution/passes/receiver-bound-calls.js +174 -40
- package/dist/core/ingestion/scope-resolution/pipeline/phase.d.ts +24 -0
- package/dist/core/ingestion/scope-resolution/pipeline/phase.js +276 -190
- package/dist/core/ingestion/scope-resolution/pipeline/reconcile-ownership.js +43 -3
- package/dist/core/ingestion/scope-resolution/pipeline/run.d.ts +75 -0
- package/dist/core/ingestion/scope-resolution/pipeline/run.js +349 -1
- package/dist/core/ingestion/scope-resolution/resolution-outcome.d.ts +1 -1
- package/dist/core/ingestion/taint/call-summary-codec.d.ts +75 -0
- package/dist/core/ingestion/taint/call-summary-codec.js +128 -0
- package/dist/core/ingestion/taint/call-summary-emit.d.ts +43 -0
- package/dist/core/ingestion/taint/call-summary-emit.js +70 -0
- package/dist/core/ingestion/taint/call-summary-harvest.d.ts +77 -0
- package/dist/core/ingestion/taint/call-summary-harvest.js +179 -0
- package/dist/core/ingestion/taint/call-summary-model.d.ts +47 -0
- package/dist/core/ingestion/taint/call-summary-model.js +25 -0
- package/dist/core/ingestion/taint/emit.d.ts +125 -0
- package/dist/core/ingestion/taint/emit.js +208 -0
- package/dist/core/ingestion/taint/interproc-emit.d.ts +50 -0
- package/dist/core/ingestion/taint/interproc-emit.js +88 -0
- package/dist/core/ingestion/taint/interproc-solver.d.ts +110 -0
- package/dist/core/ingestion/taint/interproc-solver.js +312 -0
- package/dist/core/ingestion/taint/match.d.ts +153 -0
- package/dist/core/ingestion/taint/match.js +278 -0
- package/dist/core/ingestion/taint/path-codec.d.ts +134 -0
- package/dist/core/ingestion/taint/path-codec.js +190 -0
- package/dist/core/ingestion/taint/propagate.d.ts +216 -0
- package/dist/core/ingestion/taint/propagate.js +659 -0
- package/dist/core/ingestion/taint/site-safety.d.ts +29 -0
- package/dist/core/ingestion/taint/site-safety.js +98 -0
- package/dist/core/ingestion/taint/source-sink-config.d.ts +107 -23
- package/dist/core/ingestion/taint/source-sink-config.js +35 -12
- package/dist/core/ingestion/taint/source-sink-registry.d.ts +6 -4
- package/dist/core/ingestion/taint/source-sink-registry.js +6 -4
- package/dist/core/ingestion/taint/summary-harvest-driver.d.ts +80 -0
- package/dist/core/ingestion/taint/summary-harvest-driver.js +179 -0
- package/dist/core/ingestion/taint/summary-harvest.d.ts +89 -0
- package/dist/core/ingestion/taint/summary-harvest.js +537 -0
- package/dist/core/ingestion/taint/summary-model.d.ts +200 -0
- package/dist/core/ingestion/taint/summary-model.js +86 -0
- package/dist/core/ingestion/taint/typescript-model.d.ts +38 -0
- package/dist/core/ingestion/taint/typescript-model.js +102 -0
- package/dist/core/ingestion/type-extractors/c-cpp.js +21 -0
- package/dist/core/ingestion/utils/env.d.ts +7 -0
- package/dist/core/ingestion/utils/env.js +12 -0
- package/dist/core/ingestion/utils/method-props.js +1 -0
- package/dist/core/ingestion/workers/clone-safety.d.ts +109 -0
- package/dist/core/ingestion/workers/clone-safety.js +465 -0
- package/dist/core/ingestion/workers/parse-worker.d.ts +23 -0
- package/dist/core/ingestion/workers/parse-worker.js +81 -53
- package/dist/core/ingestion/workers/post-result.d.ts +22 -0
- package/dist/core/ingestion/workers/post-result.js +87 -0
- package/dist/core/ingestion/workers/result-merge.d.ts +20 -0
- package/dist/core/ingestion/workers/result-merge.js +43 -0
- package/dist/core/ingestion/workers/worker-pool.d.ts +15 -40
- package/dist/core/ingestion/workers/worker-pool.js +43 -16
- package/dist/core/lbug/csv-generator.d.ts +51 -3
- package/dist/core/lbug/csv-generator.js +369 -255
- package/dist/core/lbug/lbug-adapter.d.ts +58 -18
- package/dist/core/lbug/lbug-adapter.js +346 -84
- package/dist/core/lbug/lbug-config.d.ts +19 -0
- package/dist/core/lbug/lbug-config.js +29 -0
- package/dist/core/lbug/pdg-emit-sink.d.ts +127 -0
- package/dist/core/lbug/pdg-emit-sink.js +358 -0
- package/dist/core/lbug/pool-adapter.d.ts +35 -0
- package/dist/core/lbug/pool-adapter.js +90 -1
- package/dist/core/lbug/rel-pair-routing.d.ts +54 -0
- package/dist/core/lbug/rel-pair-routing.js +140 -0
- package/dist/core/lbug/schema.d.ts +2 -2
- package/dist/core/lbug/schema.js +3 -0
- package/dist/core/logger.d.ts +0 -5
- package/dist/core/run-analyze.d.ts +96 -0
- package/dist/core/run-analyze.js +207 -3
- package/dist/core/tree-sitter/parser-loader.js +5 -4
- package/dist/core/tree-sitter/safe-parse.d.ts +0 -5
- package/dist/core/tree-sitter/vendored-grammars.d.ts +39 -0
- package/dist/core/tree-sitter/vendored-grammars.js +57 -0
- package/dist/core/wiki/generator.js +3 -1
- package/dist/core/wiki/graph-queries.d.ts +6 -0
- package/dist/core/wiki/graph-queries.js +9 -1
- package/dist/mcp/http-transport.d.ts +134 -0
- package/dist/mcp/http-transport.js +503 -0
- package/dist/mcp/local/local-backend.d.ts +110 -24
- package/dist/mcp/local/local-backend.js +1225 -19
- package/dist/mcp/local/pdg-impact.d.ts +384 -0
- package/dist/mcp/local/pdg-impact.js +1972 -0
- package/dist/mcp/resources.js +13 -1
- package/dist/mcp/tools.d.ts +12 -0
- package/dist/mcp/tools.js +214 -7
- package/dist/server/analyze-launch.d.ts +29 -0
- package/dist/server/analyze-launch.js +137 -0
- package/dist/server/analyze-upload.d.ts +33 -0
- package/dist/server/analyze-upload.js +123 -0
- package/dist/server/analyze-worker-ipc.d.ts +58 -0
- package/dist/server/analyze-worker-ipc.js +16 -0
- package/dist/server/analyze-worker.d.ts +17 -1
- package/dist/server/analyze-worker.js +7 -1
- package/dist/server/api.d.ts +17 -0
- package/dist/server/api.js +132 -168
- package/dist/server/git-clone.d.ts +43 -2
- package/dist/server/git-clone.js +211 -16
- package/dist/server/mcp-http.d.ts +5 -6
- package/dist/server/mcp-http.js +9 -82
- package/dist/server/middleware.d.ts +40 -0
- package/dist/server/middleware.js +92 -0
- package/dist/server/private-ip.d.ts +1 -0
- package/dist/server/private-ip.js +15 -0
- package/dist/server/upload-ingest.d.ts +56 -0
- package/dist/server/upload-ingest.js +276 -0
- package/dist/server/upload-paths.d.ts +37 -0
- package/dist/server/upload-paths.js +57 -0
- package/dist/server/upload-sweep.d.ts +20 -0
- package/dist/server/upload-sweep.js +57 -0
- package/dist/storage/git.js +5 -8
- package/dist/storage/parse-cache.d.ts +22 -1
- package/dist/storage/parse-cache.js +44 -10
- package/dist/storage/repo-manager.d.ts +121 -10
- package/dist/storage/repo-manager.js +43 -5
- package/dist/types/pipeline.d.ts +9 -0
- package/hooks/antigravity/gitnexus-antigravity-hook.cjs +110 -10
- package/hooks/claude/gitnexus-hook.cjs +110 -13
- package/hooks/claude/hook-db-lock-probe.cjs +513 -26
- package/package.json +8 -2
- package/scripts/assert-publish-grammar-coverage.cjs +31 -0
- package/scripts/build-tree-sitter-grammars.cjs +16 -10
- package/scripts/cross-platform-tests.ts +1 -0
- package/skills/gitnexus-debugging.md +16 -4
- package/skills/gitnexus-exploring.md +3 -3
- package/skills/gitnexus-guide.md +33 -0
- package/skills/gitnexus-pdg-query.md +89 -0
- package/skills/gitnexus-refactoring.md +1 -1
- package/skills/gitnexus-taint-analysis.md +178 -0
- package/vendor/tree-sitter-c/package.json +1 -1
- package/vendor/tree-sitter-dart/package.json +1 -1
- package/vendor/tree-sitter-kotlin/package.json +1 -1
- package/vendor/tree-sitter-proto/package.json +1 -1
- package/vendor/tree-sitter-swift/package.json +1 -1
- package/web/assets/{agent-CKPMqImC.js → agent-D3Xd7zZE.js} +123 -123
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-DxJw65fT.js +1 -0
- package/web/assets/architectureDiagram-UL44E2DR-ChH9xW-N.js +36 -0
- package/web/assets/{blockDiagram-7IZFK4PR-B4niSLvR.js → blockDiagram-7IZFK4PR-BkWhpzWB.js} +1 -1
- package/web/assets/{c4Diagram-Y2BXMSZH-DIzEXalR.js → c4Diagram-Y2BXMSZH-DtBvpzWD.js} +1 -1
- package/web/assets/{chunk-3SSMPTDK-BzGeeheI.js → chunk-3SSMPTDK-B4_etUhr.js} +2 -2
- package/web/assets/{chunk-5IMINLNL-CnTLsXIV.js → chunk-5IMINLNL-DOdTeQri.js} +1 -1
- package/web/assets/{chunk-5VCL7Z4A-w1BCx98Z.js → chunk-5VCL7Z4A-CQ-2dYWj.js} +1 -1
- package/web/assets/chunk-6764PJDD-DpIS7hxD.js +1 -0
- package/web/assets/{chunk-67TQ5CYL-CChC2cAv.js → chunk-67TQ5CYL-4sNPJU1q.js} +3 -3
- package/web/assets/{chunk-7W6UQGC5-CgFyfZ6k.js → chunk-7W6UQGC5-KyEG0HQg.js} +1 -1
- package/web/assets/{chunk-AZZRMDJM-5E9ARdDQ.js → chunk-AZZRMDJM-y-xJBuMz.js} +1 -1
- package/web/assets/{chunk-INKRHTLW-ELpfuk9h.js → chunk-INKRHTLW-Nu4ri9P-.js} +1 -1
- package/web/assets/{chunk-JQRUD6KW-Dcq_Qnyy.js → chunk-JQRUD6KW-Dbva2Z17.js} +1 -1
- package/web/assets/{chunk-KGFNY3KK-W-VR57xb.js → chunk-KGFNY3KK-JGMJvKCK.js} +1 -1
- package/web/assets/chunk-KGYTTC2M-CWC_c3H9.js +161 -0
- package/web/assets/{chunk-KNLZD3CH-BuZBOcso.js → chunk-KNLZD3CH-BGlP6X9R.js} +1 -1
- package/web/assets/{chunk-KRXBNO2N-DH2NPM7I.js → chunk-KRXBNO2N-BW5o0KUN.js} +1 -1
- package/web/assets/{chunk-LCXTWHL2-Dfmux4m1.js → chunk-LCXTWHL2-zoomRtWX.js} +1 -1
- package/web/assets/{chunk-LII3EMHJ-3X33tCpU.js → chunk-LII3EMHJ-D-jm-dLa.js} +1 -1
- package/web/assets/{chunk-QA3QBVWF-D5Ha3GgB.js → chunk-QA3QBVWF-BgRW3SXd.js} +1 -1
- package/web/assets/{chunk-RG4AUYOV-CAkzcoRj.js → chunk-RG4AUYOV-DD0B2N2B.js} +1 -1
- package/web/assets/{chunk-T2UQINTJ-kGYG7ppP.js → chunk-T2UQINTJ-DSBqkzH7.js} +1 -1
- package/web/assets/chunk-T5OCTHI4-DdZvN-9i.js +1 -0
- package/web/assets/{chunk-UY5QBCOK-Du-c56gs.js → chunk-UY5QBCOK-DyF0vyGd.js} +1 -1
- package/web/assets/{chunk-W44A43WB-lrCpuupA.js → chunk-W44A43WB-JqMLIpR0.js} +1 -1
- package/web/assets/{chunk-ZXARS5L4-CSZRiyOf.js → chunk-ZXARS5L4-CxN8oiwI.js} +1 -1
- package/web/assets/classDiagram-KGZ6W3CR-DJm9BgF-.js +1 -0
- package/web/assets/classDiagram-v2-72OJOZXJ-DJm9BgF-.js +1 -0
- package/web/assets/{context-builder-_HS0v2ma.js → context-builder-BHiFUA8O.js} +10 -2
- package/web/assets/{cose-bilkent-UX7MHV2Q-BHv2bbtl.js → cose-bilkent-UX7MHV2Q-D71wNYRJ.js} +1 -1
- package/web/assets/dagre-ND4H6XIP-BIkxmhcu.js +4 -0
- package/web/assets/diagram-3NCE3AQN-BTa6iH0T.js +43 -0
- package/web/assets/diagram-GF46GFSD-DZDV0vBj.js +24 -0
- package/web/assets/{diagram-HNR7UZ2L-fUWreZzx.js → diagram-HNR7UZ2L-DFCfd5LI.js} +2 -2
- package/web/assets/diagram-QXG6HAR7-BzM--Jj0.js +24 -0
- package/web/assets/diagram-WEQXMOUZ-DFFwz0k4.js +10 -0
- package/web/assets/{erDiagram-L5TCEMPS-QN2eEP1e.js → erDiagram-L5TCEMPS-CfHMJJqo.js} +1 -1
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-BQm9QzEa.js +1 -0
- package/web/assets/{flowDiagram-H6V6AXG4-CXbXImlN.js → flowDiagram-H6V6AXG4-Be805QiJ.js} +3 -3
- package/web/assets/{ganttDiagram-JCBTUEKG-BHQssTz6.js → ganttDiagram-JCBTUEKG-D3iL2Aet.js} +1 -1
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-Cr_Bm2Nb.js +1 -0
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CPIDgxGm.js +106 -0
- package/web/assets/index-BvYev1UJ.js +635 -0
- package/web/assets/index-_lgn7hs5.css +2 -0
- package/web/assets/info-J43DQDTF-KCYPFFUO-Cs4p2oyk.js +1 -0
- package/web/assets/infoDiagram-3YFTVSEB-Y7BKSoke.js +2 -0
- package/web/assets/{ishikawaDiagram-BNXS4ZKH-6LKOvBfp.js → ishikawaDiagram-BNXS4ZKH-CT4SHFuI.js} +1 -1
- package/web/assets/{journeyDiagram-M6C3CM3L-DNPYCpvq.js → journeyDiagram-M6C3CM3L-CvArszLo.js} +1 -1
- package/web/assets/{kanban-definition-75IXJCU3-DgDi9oJT.js → kanban-definition-75IXJCU3-C6yDxr7N.js} +3 -3
- package/web/assets/{katex-K3KEBU37-CrVLz8l7.js → katex-K3KEBU37-CTc5BslQ.js} +1 -1
- package/web/assets/{mindmap-definition-2TDM6QVE-CwR5sBB-.js → mindmap-definition-2TDM6QVE-BOc2WEne.js} +1 -1
- package/web/assets/packet-YPE3B663-LP52Z2RK-CKCrztD2.js +1 -0
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BB7pHqoD.js +1 -0
- package/web/assets/{pieDiagram-CU6KROY3-By8g6f6B.js → pieDiagram-CU6KROY3-Cvc0f_Pd.js} +1 -1
- package/web/assets/{quadrantDiagram-VICAPDV7-bAm6Hi_5.js → quadrantDiagram-VICAPDV7-yXSZ2lLz.js} +1 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-DhoTOcuK.js +1 -0
- package/web/assets/{requirementDiagram-JXO7QTGE-7oDcJ1_J.js → requirementDiagram-JXO7QTGE-BRcZvHs4.js} +1 -1
- package/web/assets/{sankeyDiagram-URQDO5SZ-LFJz53u5.js → sankeyDiagram-URQDO5SZ-1qEjlFIB.js} +1 -1
- package/web/assets/{sequenceDiagram-VS2MUI6T-FySeKCUy.js → sequenceDiagram-VS2MUI6T-PcSCuvrB.js} +3 -3
- package/web/assets/stateDiagram-7D4R322I-D6qYSV1E.js +1 -0
- package/web/assets/stateDiagram-v2-36443NZ5-Bg-ImOSX.js +1 -0
- package/web/assets/{timeline-definition-O6YCAMPW-Ds2CnVZK.js → timeline-definition-O6YCAMPW-QWkh01xw.js} +1 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-ODehyGKL.js +1 -0
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-BfpgC-7o.js +1 -0
- package/web/assets/{vennDiagram-MWXL3ELB-DUIEwXWp.js → vennDiagram-MWXL3ELB-ak4DfwuM.js} +3 -3
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-CNeL6VHE.js +1 -0
- package/web/assets/{wardleyDiagram-CUQ6CDDI-DEiFPQih.js → wardleyDiagram-CUQ6CDDI-CTzR4Loq.js} +1 -1
- package/web/assets/{xychartDiagram-N2JHSOCM-BUbayhST.js → xychartDiagram-N2JHSOCM-DA1BEaQz.js} +1 -1
- package/web/index.html +13 -15
- package/scripts/materialize-vendor-grammars.cjs +0 -97
- package/web/assets/architecture-7EHR7CIX-6QZW5X65-BETj2x4g.js +0 -1
- package/web/assets/architectureDiagram-UL44E2DR-D1EXI0zA.js +0 -36
- package/web/assets/chunk-2T2R6R2M-n6s9JZqv.js +0 -4
- package/web/assets/chunk-2UTLFMKG-CMBB1TMN.js +0 -1
- package/web/assets/chunk-4R4BOZG6-mKhL59ul.js +0 -159
- package/web/assets/chunk-6764PJDD-ChwMM2z4.js +0 -1
- package/web/assets/chunk-7J6CGLKN-DT-b53FT.js +0 -10
- package/web/assets/chunk-C62D2QBJ-CDAWj26E.js +0 -1
- package/web/assets/chunk-CEXFNPSA-D68Tk6ls.js +0 -1
- package/web/assets/chunk-CilyBKbf.js +0 -1
- package/web/assets/chunk-J5EP6P6S-DxWW0yvu.js +0 -1
- package/web/assets/chunk-KGYTTC2M-C6PHeuay.js +0 -1
- package/web/assets/chunk-RERM46MO-DQNbXtfw.js +0 -1
- package/web/assets/chunk-RKZBBQEN-BmTPLSyv.js +0 -1
- package/web/assets/chunk-RLI5ZMPA-DExu2DOK.js +0 -1
- package/web/assets/chunk-T5OCTHI4--9wWpVws.js +0 -1
- package/web/assets/chunk-UP6H54XL-DsKdC6jC.js +0 -1
- package/web/assets/chunk-UXSXWOXI-DR81EqLr.js +0 -1
- package/web/assets/classDiagram-KGZ6W3CR-Bv93af_b.js +0 -1
- package/web/assets/classDiagram-v2-72OJOZXJ-CLfEqHUa.js +0 -1
- package/web/assets/dagre-ND4H6XIP-DsIr9MOv.js +0 -4
- package/web/assets/diagram-3NCE3AQN-BqAtKUpW.js +0 -43
- package/web/assets/diagram-GF46GFSD-CaBG6n6o.js +0 -24
- package/web/assets/diagram-QXG6HAR7-CZ-O3rcV.js +0 -24
- package/web/assets/diagram-WEQXMOUZ-P4lSL4GH.js +0 -10
- package/web/assets/eventmodeling-FCH6USID-MREXMVOE-B3myHUst.js +0 -1
- package/web/assets/gitGraph-WXDBUCRP-R675I2BI-DF3rtf4A.js +0 -1
- package/web/assets/gitGraphDiagram-S2ZK5IYY-CCwVLT__.js +0 -106
- package/web/assets/index-BKWA-m7o.css +0 -2
- package/web/assets/index-CG6q8eTs.js +0 -626
- package/web/assets/info-J43DQDTF-KCYPFFUO-R4XV_akJ.js +0 -1
- package/web/assets/infoDiagram-3YFTVSEB-DmLICZx1.js +0 -2
- package/web/assets/packet-YPE3B663-LP52Z2RK-CgQPdNZa.js +0 -1
- package/web/assets/pie-LRSECV5Y-TCRJHUBD-BzWOwc_c.js +0 -1
- package/web/assets/radar-GUYGQ44K-RDLRG3WG-B7KG0FN7.js +0 -1
- package/web/assets/stateDiagram-7D4R322I-CB2nABwH.js +0 -1
- package/web/assets/stateDiagram-v2-36443NZ5-COBGd2RL.js +0 -1
- package/web/assets/treeView-BLDUP644-QA4HXRO3-CRSxEwLO.js +0 -1
- package/web/assets/treemap-LRROVOQU-LLAWBHMP-a0PfKoVn.js +0 -1
- package/web/assets/wardley-L42UT6IY-5TKZOOLJ-BKQ6e5mC.js +0 -1
- /package/web/assets/{chunk-AQ6EADP3-CCFje6lL.js → chunk-AQ6EADP3-CZhslHi-.js} +0 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dedicated MCP HTTP server.
|
|
3
|
+
*
|
|
4
|
+
* Provides HTTP-based MCP transport supporting:
|
|
5
|
+
* - Modern Streamable HTTP: POST /mcp
|
|
6
|
+
* - Legacy SSE transport: GET /sse + POST /messages
|
|
7
|
+
*
|
|
8
|
+
* Started via `gitnexus mcp --http`.
|
|
9
|
+
* stdio remains the default mode for `gitnexus mcp` (no breaking change).
|
|
10
|
+
*
|
|
11
|
+
* Exports createStreamableHttpHandler and createSseHandlers so that
|
|
12
|
+
* server/mcp-http.ts (web-UI route mount) can reuse them without inverting
|
|
13
|
+
* the established server/ → mcp/ dependency direction.
|
|
14
|
+
*
|
|
15
|
+
* Security considerations:
|
|
16
|
+
* - Default binds to 127.0.0.1 (loopback only).
|
|
17
|
+
* - Use --auth-token to enable Bearer Token authentication.
|
|
18
|
+
* - Use --host 0.0.0.0 to expose to all interfaces (requires --auth-token — refuses to start otherwise).
|
|
19
|
+
* - CORS is restricted to loopback origins when no auth token is configured.
|
|
20
|
+
* - PNA (Private Network Access) header is emitted only in response to browser preflight requests.
|
|
21
|
+
*/
|
|
22
|
+
import { timingSafeEqual, randomUUID } from 'crypto';
|
|
23
|
+
import express from 'express';
|
|
24
|
+
import cors from 'cors';
|
|
25
|
+
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
|
|
26
|
+
import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
|
|
27
|
+
import { isInitializeRequest } from '@modelcontextprotocol/sdk/types.js';
|
|
28
|
+
import { createMCPServer, installSignalShutdown } from './server.js';
|
|
29
|
+
import { logger } from '../core/logger.js';
|
|
30
|
+
/** Sessions idle longer than this are evicted. */
|
|
31
|
+
const SESSION_TTL_MS = 30 * 60 * 1000;
|
|
32
|
+
/** Cleanup sweep runs every 5 minutes. */
|
|
33
|
+
const CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
|
|
34
|
+
/** Hard cap on concurrent sessions — guards against initialize-flood DoS. */
|
|
35
|
+
const MAX_SESSIONS = 1000;
|
|
36
|
+
/**
|
|
37
|
+
* Creates a Bearer Token authentication middleware.
|
|
38
|
+
*
|
|
39
|
+
* - When authToken is not set, all requests pass through.
|
|
40
|
+
* - When authToken is set, checks the Authorization: Bearer <token> header.
|
|
41
|
+
* - Uses constant-time comparison to prevent timing oracle attacks.
|
|
42
|
+
* - Returns a JSON-RPC formatted 401 on failure.
|
|
43
|
+
*/
|
|
44
|
+
export function createAuthMiddleware(authToken) {
|
|
45
|
+
return (req, res, next) => {
|
|
46
|
+
if (!authToken) {
|
|
47
|
+
next();
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const header = req.headers['authorization'];
|
|
51
|
+
const expected = `Bearer ${authToken}`;
|
|
52
|
+
// Constant-time comparison — prevents timing oracle on bearer token.
|
|
53
|
+
// Buffers must be the same byte-length for timingSafeEqual; mismatch means
|
|
54
|
+
// we create a same-length dummy so the comparison always runs in full.
|
|
55
|
+
let valid = false;
|
|
56
|
+
if (typeof header === 'string') {
|
|
57
|
+
const a = Buffer.from(header);
|
|
58
|
+
const b = Buffer.from(expected);
|
|
59
|
+
if (a.length === b.length) {
|
|
60
|
+
valid = timingSafeEqual(a, b);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
// Different lengths — run dummy comparison to preserve constant time.
|
|
64
|
+
timingSafeEqual(Buffer.alloc(b.length), b);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (valid) {
|
|
68
|
+
next();
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
res.status(401).json({
|
|
72
|
+
jsonrpc: '2.0',
|
|
73
|
+
error: { code: -32001, message: 'Unauthorized' },
|
|
74
|
+
id: null,
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Returns true when an Origin should be allowed by the no-auth (loopback-only)
|
|
80
|
+
* CORS policy — i.e. it is absent (non-browser caller) or a loopback origin.
|
|
81
|
+
*
|
|
82
|
+
* WHATWG URL keeps the brackets on IPv6 literals
|
|
83
|
+
* (`new URL('http://[::1]/').hostname === '[::1]'`) and canonicalizes the
|
|
84
|
+
* IPv4-mapped loopback to `[::ffff:7f00:1]`; loopback IPv4 is the whole
|
|
85
|
+
* 127.0.0.0/8 block — so all of those forms are matched explicitly.
|
|
86
|
+
*/
|
|
87
|
+
export function isLoopbackOrigin(origin) {
|
|
88
|
+
if (!origin)
|
|
89
|
+
return true; // no Origin → non-browser caller; CORS is not the control there
|
|
90
|
+
let hostname;
|
|
91
|
+
try {
|
|
92
|
+
({ hostname } = new URL(origin));
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
return (hostname === 'localhost' ||
|
|
98
|
+
hostname === '[::1]' ||
|
|
99
|
+
hostname === '[::ffff:7f00:1]' ||
|
|
100
|
+
/^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(hostname));
|
|
101
|
+
}
|
|
102
|
+
/** True for the exact loopback bind addresses. */
|
|
103
|
+
export function isLoopbackHost(host) {
|
|
104
|
+
return host === '127.0.0.1' || host === 'localhost' || host === '::1';
|
|
105
|
+
}
|
|
106
|
+
/** True for any-interface wildcard binds, whose externally-used Host is unknowable. */
|
|
107
|
+
export function isWildcardHost(host) {
|
|
108
|
+
return host === '0.0.0.0' || host === '::';
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Computes the SDK DNS-rebinding `allowedHosts` list (a Host-header allowlist) for a
|
|
112
|
+
* bind host/port, or `undefined` when protection should stay off.
|
|
113
|
+
*
|
|
114
|
+
* Wildcard binds (`0.0.0.0` / `::`) return `undefined` — the Host a client
|
|
115
|
+
* legitimately uses is unknowable, so the bearer token (required for non-loopback
|
|
116
|
+
* binds) is the control. Loopback binds allow all three loopback host forms
|
|
117
|
+
* (bare + `:port`); a specific host (e.g. `192.168.1.50`) allows that host
|
|
118
|
+
* (bare + `:port`), which is knowable and a free defence-in-depth win.
|
|
119
|
+
*/
|
|
120
|
+
export function computeAllowedHosts(host, port) {
|
|
121
|
+
if (isWildcardHost(host))
|
|
122
|
+
return undefined;
|
|
123
|
+
const hosts = isLoopbackHost(host) ? ['127.0.0.1', 'localhost', '[::1]'] : [host];
|
|
124
|
+
return hosts.flatMap((h) => [h, `${h}:${port}`]);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Resolves the MCP HTTP bearer token from the `--auth-token` flag or the
|
|
128
|
+
* `GITNEXUS_MCP_AUTH_TOKEN` env var (the flag wins). An empty or whitespace-only
|
|
129
|
+
* value is treated as "no token" so a blank env var cannot silently disable auth
|
|
130
|
+
* (and slip past the non-loopback hard-fail).
|
|
131
|
+
*/
|
|
132
|
+
export function resolveAuthToken(optToken, env) {
|
|
133
|
+
return (optToken ?? env.GITNEXUS_MCP_AUTH_TOKEN)?.trim() || undefined;
|
|
134
|
+
}
|
|
135
|
+
/** Builds the SDK transport DNS-rebinding options from a bind host/port. */
|
|
136
|
+
function dnsRebindingOptions(host, port) {
|
|
137
|
+
if (host === undefined || port === undefined)
|
|
138
|
+
return {};
|
|
139
|
+
const allowedHosts = computeAllowedHosts(host, port);
|
|
140
|
+
return allowedHosts ? { enableDnsRebindingProtection: true, allowedHosts } : {};
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Starts a periodic sweep that closes and evicts sessions idle longer than
|
|
144
|
+
* `ttlMs`, returning the (unref'd) timer. Shared by both transport factories to
|
|
145
|
+
* guard against network drops where the per-session onclose never fires.
|
|
146
|
+
*/
|
|
147
|
+
export function startIdleSweep(sessions, ttlMs, intervalMs) {
|
|
148
|
+
const timer = setInterval(() => {
|
|
149
|
+
const now = Date.now();
|
|
150
|
+
for (const [id, session] of sessions) {
|
|
151
|
+
if (now - session.lastActivity > ttlMs) {
|
|
152
|
+
try {
|
|
153
|
+
session.server.close();
|
|
154
|
+
}
|
|
155
|
+
catch { }
|
|
156
|
+
sessions.delete(id);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}, intervalMs);
|
|
160
|
+
if (timer && typeof timer === 'object' && 'unref' in timer) {
|
|
161
|
+
timer.unref();
|
|
162
|
+
}
|
|
163
|
+
return timer;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Creates a reusable StreamableHTTP request handler.
|
|
167
|
+
*
|
|
168
|
+
* Encapsulates the session map and request-dispatch logic as an independent
|
|
169
|
+
* factory, reused by both startMcpHttpServer (POST /mcp) and the web-UI server
|
|
170
|
+
* route mount in server/mcp-http.ts (/api/mcp).
|
|
171
|
+
*/
|
|
172
|
+
export function createStreamableHttpHandler(backend, opts = {}) {
|
|
173
|
+
// Seam: tests inject createServer to observe the per-session Server lifecycle.
|
|
174
|
+
const createServer = opts.createServer ?? (() => createMCPServer(backend));
|
|
175
|
+
// DNS-rebinding protection (Host-header allowlist) when the bind host is known.
|
|
176
|
+
const dnsRebinding = dnsRebindingOptions(opts.host, opts.port);
|
|
177
|
+
const sessions = new Map();
|
|
178
|
+
const cleanupTimer = startIdleSweep(sessions, SESSION_TTL_MS, CLEANUP_INTERVAL_MS);
|
|
179
|
+
const handler = async (req, res) => {
|
|
180
|
+
const sessionId = req.headers['mcp-session-id'];
|
|
181
|
+
if (sessionId && sessions.has(sessionId)) {
|
|
182
|
+
// Existing session — delegate to its transport and refresh activity timestamp.
|
|
183
|
+
// `has` just returned true and the map is not mutated before `get`, so the
|
|
184
|
+
// lookup is non-null.
|
|
185
|
+
const session = sessions.get(sessionId);
|
|
186
|
+
session.lastActivity = Date.now();
|
|
187
|
+
await session.transport.handleRequest(req, res, req.body);
|
|
188
|
+
}
|
|
189
|
+
else if (sessionId) {
|
|
190
|
+
// Unknown / expired session ID — tell the client to re-initialize (per MCP spec).
|
|
191
|
+
res.status(404).json({
|
|
192
|
+
jsonrpc: '2.0',
|
|
193
|
+
error: { code: -32001, message: 'Session not found. Re-initialize.' },
|
|
194
|
+
id: null,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
else if (req.method === 'POST') {
|
|
198
|
+
// No session ID — new client. Only accept initialize requests to avoid
|
|
199
|
+
// orphaned Server instances that can never be reclaimed by the TTL sweep.
|
|
200
|
+
// Use the SDK's isInitializeRequest so a single-element JSON-RPC batch is
|
|
201
|
+
// recognised too, rather than a brittle `body.method === 'initialize'` check.
|
|
202
|
+
const body = req.body;
|
|
203
|
+
const messages = Array.isArray(body) ? body : [body];
|
|
204
|
+
if (!messages.some(isInitializeRequest)) {
|
|
205
|
+
res.status(400).json({
|
|
206
|
+
jsonrpc: '2.0',
|
|
207
|
+
error: {
|
|
208
|
+
code: -32000,
|
|
209
|
+
message: 'First request must be initialize. No session ID provided.',
|
|
210
|
+
},
|
|
211
|
+
id: null,
|
|
212
|
+
});
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
// Reject when the session cap is reached — prevents memory exhaustion via
|
|
216
|
+
// an initialize flood (each session holds a live Server + Transport).
|
|
217
|
+
if (sessions.size >= MAX_SESSIONS) {
|
|
218
|
+
res.status(503).json({
|
|
219
|
+
jsonrpc: '2.0',
|
|
220
|
+
error: { code: -32000, message: 'Server at session capacity. Try again later.' },
|
|
221
|
+
id: null,
|
|
222
|
+
});
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
const transport = new StreamableHTTPServerTransport({
|
|
226
|
+
sessionIdGenerator: () => randomUUID(),
|
|
227
|
+
...dnsRebinding,
|
|
228
|
+
});
|
|
229
|
+
const server = createServer();
|
|
230
|
+
await server.connect(transport);
|
|
231
|
+
await transport.handleRequest(req, res, req.body);
|
|
232
|
+
if (transport.sessionId) {
|
|
233
|
+
sessions.set(transport.sessionId, { server, transport, lastActivity: Date.now() });
|
|
234
|
+
const sid = transport.sessionId;
|
|
235
|
+
transport.onclose = () => {
|
|
236
|
+
sessions.delete(sid);
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
// The SDK rejected this request (e.g. 406 on a missing/invalid Accept header,
|
|
241
|
+
// 415 on a bad Content-Type) before assigning a session id. The Server was
|
|
242
|
+
// already connected but will never be stored, so the TTL sweep and cleanup()
|
|
243
|
+
// can't reclaim it — close it now to avoid an orphaned-Server leak.
|
|
244
|
+
try {
|
|
245
|
+
await server.close();
|
|
246
|
+
}
|
|
247
|
+
catch { }
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
res.status(400).json({
|
|
252
|
+
jsonrpc: '2.0',
|
|
253
|
+
error: { code: -32000, message: 'No valid session. Send a POST to initialize.' },
|
|
254
|
+
id: null,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
const cleanup = async () => {
|
|
259
|
+
clearInterval(cleanupTimer);
|
|
260
|
+
const closers = [...sessions.values()].map(async (session) => {
|
|
261
|
+
try {
|
|
262
|
+
await Promise.resolve(session.server.close());
|
|
263
|
+
}
|
|
264
|
+
catch { }
|
|
265
|
+
});
|
|
266
|
+
sessions.clear();
|
|
267
|
+
await Promise.allSettled(closers);
|
|
268
|
+
};
|
|
269
|
+
return { handler, cleanup };
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Creates legacy SSE transport handlers.
|
|
273
|
+
*
|
|
274
|
+
* GET /sse (or custom path) establishes the SSE stream;
|
|
275
|
+
* POST /messages (or custom path) receives client JSON-RPC messages.
|
|
276
|
+
*
|
|
277
|
+
* Includes the same idle-TTL eviction as createStreamableHttpHandler to prevent
|
|
278
|
+
* memory leaks when clients drop without closing the SSE connection cleanly.
|
|
279
|
+
*
|
|
280
|
+
* @param backend LocalBackend instance
|
|
281
|
+
* @param messagesPath Path clients POST messages to (default: '/messages')
|
|
282
|
+
*/
|
|
283
|
+
export function createSseHandlers(backend, messagesPath = '/messages', opts = {}) {
|
|
284
|
+
const maxSessions = opts.maxSessions ?? MAX_SESSIONS;
|
|
285
|
+
// DNS-rebinding protection (Host-header allowlist) when the bind host is known.
|
|
286
|
+
const dnsRebinding = dnsRebindingOptions(opts.host, opts.port);
|
|
287
|
+
const sseSessions = new Map();
|
|
288
|
+
const cleanupTimer = startIdleSweep(sseSessions, SESSION_TTL_MS, CLEANUP_INTERVAL_MS);
|
|
289
|
+
const sseHandler = async (req, res) => {
|
|
290
|
+
// Cap concurrent SSE sessions — mirrors the streamable handler's MAX_SESSIONS
|
|
291
|
+
// guard so a flood of held-open GET /sse connections cannot allocate unbounded
|
|
292
|
+
// Server instances before the idle sweep reclaims them.
|
|
293
|
+
if (sseSessions.size >= maxSessions) {
|
|
294
|
+
res.status(503).json({
|
|
295
|
+
jsonrpc: '2.0',
|
|
296
|
+
error: { code: -32000, message: 'Server at session capacity. Try again later.' },
|
|
297
|
+
id: null,
|
|
298
|
+
});
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
// SSEServerTransport(endpoint, res, options): endpoint is the path clients POST to.
|
|
302
|
+
const transport = new SSEServerTransport(messagesPath, res, dnsRebinding);
|
|
303
|
+
const server = createMCPServer(backend);
|
|
304
|
+
sseSessions.set(transport.sessionId, { server, transport, lastActivity: Date.now() });
|
|
305
|
+
transport.onclose = () => {
|
|
306
|
+
sseSessions.delete(transport.sessionId);
|
|
307
|
+
};
|
|
308
|
+
res.on('close', () => {
|
|
309
|
+
sseSessions.delete(transport.sessionId);
|
|
310
|
+
try {
|
|
311
|
+
server.close();
|
|
312
|
+
}
|
|
313
|
+
catch { }
|
|
314
|
+
});
|
|
315
|
+
// connect() calls transport.start(), which sends the SSE 'endpoint' event.
|
|
316
|
+
await server.connect(transport);
|
|
317
|
+
};
|
|
318
|
+
const messageHandler = async (req, res) => {
|
|
319
|
+
const sessionId = req.query['sessionId'] ??
|
|
320
|
+
req.headers['mcp-session-id'];
|
|
321
|
+
const entry = sessionId ? sseSessions.get(sessionId) : undefined;
|
|
322
|
+
if (!entry) {
|
|
323
|
+
res.status(404).json({
|
|
324
|
+
jsonrpc: '2.0',
|
|
325
|
+
error: { code: -32001, message: 'SSE session not found. Reconnect to /sse.' },
|
|
326
|
+
id: null,
|
|
327
|
+
});
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
// Refresh activity timestamp so the TTL sweep does not evict an active session.
|
|
331
|
+
entry.lastActivity = Date.now();
|
|
332
|
+
// express.json() has already parsed the body — pass it as the third argument
|
|
333
|
+
// to avoid the SDK re-reading the already-consumed stream.
|
|
334
|
+
await entry.transport.handlePostMessage(req, res, req.body);
|
|
335
|
+
};
|
|
336
|
+
const cleanup = async () => {
|
|
337
|
+
clearInterval(cleanupTimer);
|
|
338
|
+
const closers = [...sseSessions.values()].map(async ({ server }) => {
|
|
339
|
+
try {
|
|
340
|
+
await Promise.resolve(server.close());
|
|
341
|
+
}
|
|
342
|
+
catch { }
|
|
343
|
+
});
|
|
344
|
+
sseSessions.clear();
|
|
345
|
+
await Promise.allSettled(closers);
|
|
346
|
+
};
|
|
347
|
+
return { sseHandler, messageHandler, cleanup };
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Creates and starts the dedicated MCP HTTP server.
|
|
351
|
+
*
|
|
352
|
+
* Mounts the following routes:
|
|
353
|
+
* - GET /health — health check (no auth required; for orchestrators/probes)
|
|
354
|
+
* - POST /mcp — Streamable HTTP (modern clients)
|
|
355
|
+
* - GET /sse — legacy SSE stream (old clients)
|
|
356
|
+
* - POST /messages — legacy SSE message endpoint
|
|
357
|
+
*
|
|
358
|
+
* @param backend LocalBackend instance
|
|
359
|
+
* @param options Server configuration
|
|
360
|
+
* @returns The listening http.Server
|
|
361
|
+
*/
|
|
362
|
+
export async function startMcpHttpServer(backend, options) {
|
|
363
|
+
const { port, host, authToken } = options;
|
|
364
|
+
// Refuse to start an unauthenticated server on a non-loopback interface — that
|
|
365
|
+
// would silently expose every indexed repo to anyone who can reach the host.
|
|
366
|
+
// Loopback binds stay open by default; non-loopback binds require a token.
|
|
367
|
+
if (!authToken && !isLoopbackHost(host)) {
|
|
368
|
+
throw new Error(`Refusing to start the MCP HTTP server on a non-loopback host (${host}) without ` +
|
|
369
|
+
'authentication — it would expose all indexed repos to anyone who can reach it. ' +
|
|
370
|
+
'Pass --auth-token (or set GITNEXUS_MCP_AUTH_TOKEN), or bind --host 127.0.0.1. ' +
|
|
371
|
+
'This applies to --host 0.0.0.0 and --host :: as well.');
|
|
372
|
+
}
|
|
373
|
+
const app = express();
|
|
374
|
+
// Suppress X-Powered-By to reduce information leakage.
|
|
375
|
+
app.disable('x-powered-by');
|
|
376
|
+
// PNA (Chrome 130+ Private Network Access) preflight support.
|
|
377
|
+
// The browser sends `Access-Control-Request-Private-Network: true` ONLY on the
|
|
378
|
+
// CORS preflight (an OPTIONS request); emit the matching allow header only then,
|
|
379
|
+
// never on actual GET/POST responses. Runs before cors() so the header survives
|
|
380
|
+
// onto the preflight response cors() short-circuits.
|
|
381
|
+
app.use((req, res, next) => {
|
|
382
|
+
if (req.method === 'OPTIONS' &&
|
|
383
|
+
req.headers['access-control-request-private-network'] === 'true') {
|
|
384
|
+
res.setHeader('Access-Control-Allow-Private-Network', 'true');
|
|
385
|
+
}
|
|
386
|
+
next();
|
|
387
|
+
});
|
|
388
|
+
// CORS policy:
|
|
389
|
+
// - With auth token: allow any origin (remote access is intentional and protected).
|
|
390
|
+
// - Without auth token: restrict to loopback origins only to prevent drive-by local exfiltration.
|
|
391
|
+
const corsOrigin = authToken
|
|
392
|
+
? true
|
|
393
|
+
: (origin, cb) => {
|
|
394
|
+
cb(null, isLoopbackOrigin(origin));
|
|
395
|
+
};
|
|
396
|
+
app.use(cors({
|
|
397
|
+
origin: corsOrigin,
|
|
398
|
+
credentials: false,
|
|
399
|
+
allowedHeaders: ['Content-Type', 'Authorization', 'mcp-session-id', 'last-event-id'],
|
|
400
|
+
exposedHeaders: ['mcp-session-id'],
|
|
401
|
+
}));
|
|
402
|
+
const auth = createAuthMiddleware(authToken);
|
|
403
|
+
// Body parser applied per-route after auth, so unauthenticated requests never
|
|
404
|
+
// trigger the 10 MB parse. Malformed/oversized JSON from authenticated clients
|
|
405
|
+
// is converted to a JSON-RPC error envelope by the terminal error handler
|
|
406
|
+
// registered after the routes (see below).
|
|
407
|
+
const jsonBody = express.json({ limit: '10mb' });
|
|
408
|
+
// Health check — no auth required; safe to expose for probes and orchestrators.
|
|
409
|
+
app.get('/health', (_req, res) => {
|
|
410
|
+
res.json({ status: 'ok' });
|
|
411
|
+
});
|
|
412
|
+
// Streamable HTTP (modern MCP clients) at POST /mcp.
|
|
413
|
+
const streamable = createStreamableHttpHandler(backend, { host, port });
|
|
414
|
+
app.all('/mcp', auth, jsonBody, (req, res) => {
|
|
415
|
+
void streamable.handler(req, res).catch((err) => {
|
|
416
|
+
logger.error({ err }, 'MCP /mcp request failed');
|
|
417
|
+
if (!res.headersSent) {
|
|
418
|
+
res.status(500).json({
|
|
419
|
+
jsonrpc: '2.0',
|
|
420
|
+
error: { code: -32000, message: 'Internal MCP server error' },
|
|
421
|
+
id: null,
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
// Legacy SSE: GET /sse opens the stream; POST /messages receives JSON-RPC messages.
|
|
427
|
+
const sse = createSseHandlers(backend, '/messages', { host, port });
|
|
428
|
+
app.get('/sse', auth, (req, res) => {
|
|
429
|
+
void sse.sseHandler(req, res).catch((err) => {
|
|
430
|
+
logger.error({ err }, 'MCP /sse failed');
|
|
431
|
+
});
|
|
432
|
+
});
|
|
433
|
+
app.post('/messages', auth, jsonBody, (req, res) => {
|
|
434
|
+
void sse.messageHandler(req, res).catch((err) => {
|
|
435
|
+
logger.error({ err }, 'MCP /messages failed');
|
|
436
|
+
if (!res.headersSent) {
|
|
437
|
+
res.status(500).json({
|
|
438
|
+
jsonrpc: '2.0',
|
|
439
|
+
error: { code: -32000, message: 'Internal error' },
|
|
440
|
+
id: null,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
// Terminal error handler: body-parser failures (malformed or oversized JSON)
|
|
446
|
+
// reach here via next(err). Without it, Express's default handler returns an
|
|
447
|
+
// HTML error page — leaking a stack trace and absolute install paths when
|
|
448
|
+
// NODE_ENV is unset (the default for a CLI) — instead of the JSON-RPC envelope
|
|
449
|
+
// every other path uses.
|
|
450
|
+
app.use((err, _req, res, _next) => {
|
|
451
|
+
const e = (err ?? {});
|
|
452
|
+
const isBodyParseError = e.type === 'entity.parse.failed' ||
|
|
453
|
+
e.type === 'entity.too.large' ||
|
|
454
|
+
err instanceof SyntaxError;
|
|
455
|
+
logger.error({ err }, 'MCP HTTP request error');
|
|
456
|
+
if (res.headersSent)
|
|
457
|
+
return;
|
|
458
|
+
if (isBodyParseError) {
|
|
459
|
+
res.status(e.status ?? e.statusCode ?? 400).json({
|
|
460
|
+
jsonrpc: '2.0',
|
|
461
|
+
error: { code: -32700, message: 'Parse error' },
|
|
462
|
+
id: null,
|
|
463
|
+
});
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
res.status(500).json({
|
|
467
|
+
jsonrpc: '2.0',
|
|
468
|
+
error: { code: -32000, message: 'Internal MCP server error' },
|
|
469
|
+
id: null,
|
|
470
|
+
});
|
|
471
|
+
});
|
|
472
|
+
return new Promise((resolve, reject) => {
|
|
473
|
+
const server = app.listen(port, host, () => {
|
|
474
|
+
const displayHost = host === '0.0.0.0' || host === '::' ? 'localhost' : host;
|
|
475
|
+
logger.info({ port, host }, `GitNexus MCP HTTP server listening on http://${displayHost}:${port} ` +
|
|
476
|
+
`(Streamable: POST /mcp · legacy SSE: GET /sse + POST /messages)`);
|
|
477
|
+
resolve(server);
|
|
478
|
+
});
|
|
479
|
+
server.on('error', (err) => {
|
|
480
|
+
if (err.code === 'EADDRINUSE') {
|
|
481
|
+
logger.error({ port, host }, `Port ${port} is already in use. ` +
|
|
482
|
+
`Stop the conflicting process or use a different port: gitnexus mcp --http --port <other>`);
|
|
483
|
+
process.exit(1);
|
|
484
|
+
}
|
|
485
|
+
reject(err);
|
|
486
|
+
});
|
|
487
|
+
const shutdown = async (exitCode) => {
|
|
488
|
+
server.close();
|
|
489
|
+
await streamable.cleanup();
|
|
490
|
+
await sse.cleanup();
|
|
491
|
+
try {
|
|
492
|
+
await backend.disconnect();
|
|
493
|
+
}
|
|
494
|
+
catch { }
|
|
495
|
+
const { flushLoggerSync } = await import('../core/logger.js');
|
|
496
|
+
flushLoggerSync();
|
|
497
|
+
process.exit(exitCode);
|
|
498
|
+
};
|
|
499
|
+
// Use the shared signal wiring so SIGINT exits 130 and SIGTERM exits 143
|
|
500
|
+
// (the repo's POSIX 128+signal convention), not a misleading exit(0).
|
|
501
|
+
installSignalShutdown((exitCode = 0) => void shutdown(exitCode));
|
|
502
|
+
});
|
|
503
|
+
}
|
|
@@ -140,30 +140,6 @@ export interface ListReposPagination {
|
|
|
140
140
|
/** Offset to request next; present only when `hasMore` is true. */
|
|
141
141
|
nextOffset?: number;
|
|
142
142
|
}
|
|
143
|
-
/**
|
|
144
|
-
* Validate and normalise `list_repos` pagination arguments.
|
|
145
|
-
*
|
|
146
|
-
* @internal Exported for unit testing; not part of the public API surface.
|
|
147
|
-
*
|
|
148
|
-
* There is NO MCP-SDK-level enforcement of a tool's advertised `inputSchema`
|
|
149
|
-
* (the SDK validates only the JSON-RPC envelope), and `callTool` is reachable
|
|
150
|
-
* directly, so the backend is the real validation boundary. Malformed values —
|
|
151
|
-
* non-number, `NaN`, non-integer, `limit < 1`, `limit > maxLimit`, or
|
|
152
|
-
* `offset < 0` — are REJECTED with a clear error. `limit` is bounded but NOT
|
|
153
|
-
* silently clamped: an over-max value throws (symmetric with the other bounds)
|
|
154
|
-
* so a client never receives a smaller page than it asked for without knowing.
|
|
155
|
-
* An omitted value (only `undefined`) falls back to the default.
|
|
156
|
-
*/
|
|
157
|
-
export declare function parseListReposPagination(params: {
|
|
158
|
-
limit?: unknown;
|
|
159
|
-
offset?: unknown;
|
|
160
|
-
} | null | undefined, opts: {
|
|
161
|
-
defaultLimit: number;
|
|
162
|
-
maxLimit: number;
|
|
163
|
-
}): {
|
|
164
|
-
limit: number;
|
|
165
|
-
offset: number;
|
|
166
|
-
};
|
|
167
143
|
export declare class LocalBackend {
|
|
168
144
|
private repos;
|
|
169
145
|
private contextCache;
|
|
@@ -337,6 +313,8 @@ export declare class LocalBackend {
|
|
|
337
313
|
*/
|
|
338
314
|
private maybeWarnSiblingDrift;
|
|
339
315
|
callTool(method: string, params: any): Promise<any>;
|
|
316
|
+
/** Check repository graph invariants that are suitable for CI gating. */
|
|
317
|
+
private check;
|
|
340
318
|
/**
|
|
341
319
|
* Query tool — process-grouped search.
|
|
342
320
|
*
|
|
@@ -422,6 +400,77 @@ export declare class LocalBackend {
|
|
|
422
400
|
*/
|
|
423
401
|
private context;
|
|
424
402
|
private _contextImpl;
|
|
403
|
+
/**
|
|
404
|
+
* Resolve a `target` (file path OR symbol/function name) into a BasicBlock
|
|
405
|
+
* SOURCE-block anchor, shared by `explain` (TAINTED) and `pdg_query`
|
|
406
|
+
* (CDG/REACHING_DEF) — both reconstruct the symbol↔block join the same way
|
|
407
|
+
* (there is no Function→BasicBlock edge). #2188 review: extracted from two
|
|
408
|
+
* near-identical copies that had DRIFTED — `_explainImpl` used a 0-based,
|
|
409
|
+
* un-widened span window that dropped a function's final-line block and could
|
|
410
|
+
* leak a neighbor's line-above block; this single resolver applies the correct
|
|
411
|
+
* `[symStart+1, symEnd+1]` window (1-based BasicBlock startLine vs 0-based
|
|
412
|
+
* symbol span) to BOTH callers.
|
|
413
|
+
*
|
|
414
|
+
* Returns a BARE `anchorClause` (no leading `AND`) so each caller composes its
|
|
415
|
+
* own `WHERE`; `early` carries the not-found/ambiguous payload (caller returns
|
|
416
|
+
* it verbatim). `target` / symbol names flow only through `queryParams` bind
|
|
417
|
+
* params — never interpolated into Cypher.
|
|
418
|
+
*/
|
|
419
|
+
private resolveBlockAnchor;
|
|
420
|
+
/**
|
|
421
|
+
* Explain tool (#2083 M3 U6) — persisted taint-finding explanation.
|
|
422
|
+
* WAL-aware wrapper mirroring `context`.
|
|
423
|
+
*/
|
|
424
|
+
private explain;
|
|
425
|
+
/**
|
|
426
|
+
* Taint findings are persisted as `TAINTED` rows in CodeRelation whose
|
|
427
|
+
* endpoints are BOTH BasicBlock nodes — the label anchor restricts every
|
|
428
|
+
* query here to the BasicBlock→BasicBlock partition of the rel table
|
|
429
|
+
* (which holds only the sparse, per-function-capped pdg layers), never a
|
|
430
|
+
* global symbol-space scan (the S1 verdict; LadybugDB has no rel-property
|
|
431
|
+
* index, so the label anchor IS the bound).
|
|
432
|
+
*
|
|
433
|
+
* Anchoring granularity:
|
|
434
|
+
* - file target → BasicBlock id prefix (`BasicBlock:<filePath>:` — the
|
|
435
|
+
* shared `basicBlockId` template) with an exact-or-suffix path match so
|
|
436
|
+
* `vuln.ts` finds `src/vuln.ts`.
|
|
437
|
+
* - symbol target → resolved via `resolveSymbolCandidates` (the context()
|
|
438
|
+
* path: ambiguous ⇒ ranked candidates, unknown ⇒ not-found), then the
|
|
439
|
+
* file id-prefix PLUS source-block startLine within the symbol's
|
|
440
|
+
* [startLine, endLine] span. Findings are intra-procedural, so filtering
|
|
441
|
+
* the SOURCE endpoint is sufficient — both endpoints share the function.
|
|
442
|
+
* Symbols without a line span degrade to the file-level filter.
|
|
443
|
+
*
|
|
444
|
+
* The per-finding `sinkKind` and hop path decode from the persisted
|
|
445
|
+
* `reason` via the SHARED `taint/path-codec.ts` (the U4 write path encodes
|
|
446
|
+
* with the same module — `;<kind>` header + ordered `variable:line` hops).
|
|
447
|
+
*/
|
|
448
|
+
private _explainImpl;
|
|
449
|
+
private pdgQuery;
|
|
450
|
+
/**
|
|
451
|
+
* Query the persisted PDG (#2086 M6) — the control/data-dependence analog of
|
|
452
|
+
* `explain`. `controls` reads CDG ("under what condition does X run?", branch
|
|
453
|
+
* sense 'T'|'F' in `reason`); `flows` reads REACHING_DEF (def→use, variable
|
|
454
|
+
* name in `reason`). Intra-procedural, basic-block granular.
|
|
455
|
+
*
|
|
456
|
+
* Bounded by construction: the BasicBlock→BasicBlock partition holds only the
|
|
457
|
+
* sparse, per-function-capped pdg layers, the query is anchored to one file/
|
|
458
|
+
* symbol, and the page is LIMIT-bounded (validated integer, interpolated
|
|
459
|
+
* because LadybugDB does not parameterize LIMIT). LadybugDB has no rel-
|
|
460
|
+
* property index, so the anchor IS the bound — there is no anchorless mode.
|
|
461
|
+
*
|
|
462
|
+
* Symbol↔block join: there is no Function→BasicBlock edge; the SOURCE block
|
|
463
|
+
* (`a` — controller for CDG, def for REACHING_DEF) is filtered by the
|
|
464
|
+
* BasicBlock id-prefix (`basicBlockId` template) plus its `startLine` within
|
|
465
|
+
* the symbol's span. BasicBlock `startLine` is 1-based while symbol-node
|
|
466
|
+
* `startLine`/`endLine` are 0-based, so BOTH bounds are shifted +1
|
|
467
|
+
* (`[symStart+1, symEnd+1]`) onto the block basis: the upper +1 keeps a
|
|
468
|
+
* guard/def/use on the function's final line, and the lower +1 excludes an
|
|
469
|
+
* adjacent function's block on the line directly above (#2188 review). Both
|
|
470
|
+
* endpoints share the function (intra-procedural), so filtering the source
|
|
471
|
+
* endpoint suffices.
|
|
472
|
+
*/
|
|
473
|
+
private _pdgQueryImpl;
|
|
425
474
|
/**
|
|
426
475
|
* Legacy explore — kept for backwards compatibility with resources.ts.
|
|
427
476
|
* Routes cluster/process types to direct graph queries.
|
|
@@ -438,8 +487,45 @@ export declare class LocalBackend {
|
|
|
438
487
|
* Additional refs found via text search are tagged "text_search" (lower confidence).
|
|
439
488
|
*/
|
|
440
489
|
private rename;
|
|
490
|
+
private trace;
|
|
491
|
+
private _traceImpl;
|
|
441
492
|
private impact;
|
|
442
493
|
private _impactImpl;
|
|
494
|
+
/**
|
|
495
|
+
* Union of the leaf callee names invoked across a set of dependence-slice
|
|
496
|
+
* blocks (`BasicBlock.callees`, space-joined at emit). Drives statement-precise
|
|
497
|
+
* inter-procedural evidence: a first-hop callee reached from the criterion is
|
|
498
|
+
* "proven" (callgraph-bridge) iff its name is in this set, else unproven-bridge.
|
|
499
|
+
* Empty when the slice blocks call nothing or carry no harvested callees
|
|
500
|
+
* (non-TS/JS or synthetic ENTRY/EXIT blocks) — the bridge then preserves
|
|
501
|
+
* callgraph reach. A query failure is logged and degrades to empty (no proof),
|
|
502
|
+
* never throws (the inter-procedural reach is still returned).
|
|
503
|
+
*/
|
|
504
|
+
private calleesOfBlocks;
|
|
505
|
+
/**
|
|
506
|
+
* Union of the RESOLVED callee symbol ids invoked across a set of
|
|
507
|
+
* dependence-slice blocks (`BasicBlock.calleeIds`, space-joined at emit —
|
|
508
|
+
* sibling of `callees`). This is the SOUND key the bridge prefers: a first-hop
|
|
509
|
+
* callee is proven statement-precise iff its resolved id is in this set, which
|
|
510
|
+
* eliminates the same-leaf-name collision (false-positive) and import-alias
|
|
511
|
+
* (false-negative) the name set cannot distinguish. Empty when the slice blocks
|
|
512
|
+
* carry no captured ids (pre-v3 index without the `calleeIds` column, or
|
|
513
|
+
* non-overloading/synthetic blocks) — the bridge then falls back to the
|
|
514
|
+
* leaf-name match per U5. A query failure is logged and degrades to empty (no
|
|
515
|
+
* proof), never throws (the inter-procedural reach is still returned). Mirrors
|
|
516
|
+
* `calleesOfBlocks` exactly — same shape, same swallow-on-error contract.
|
|
517
|
+
*/
|
|
518
|
+
private calleeIdsOfBlocks;
|
|
519
|
+
/**
|
|
520
|
+
* Delegates the PDG impact engine to `pdg-impact.ts`.
|
|
521
|
+
*
|
|
522
|
+
* The private method remains as the LocalBackend dispatch seam so existing
|
|
523
|
+
* tests can keep asserting that `mode:'pdg'` routes through the PDG
|
|
524
|
+
* statement engine before LocalBackend attaches interprocedural symbol reach.
|
|
525
|
+
* The traversal/projection/result assembly lives in the extracted helper
|
|
526
|
+
* module.
|
|
527
|
+
*/
|
|
528
|
+
private _runImpactPDG;
|
|
443
529
|
/**
|
|
444
530
|
* #1858 — epistemic lower-bound detection.
|
|
445
531
|
*
|