kirograph 0.12.1
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 +1171 -0
- package/dist/architecture/index.d.ts +11 -0
- package/dist/architecture/index.d.ts.map +1 -0
- package/dist/architecture/index.js +207 -0
- package/dist/architecture/index.js.map +7 -0
- package/dist/architecture/layers/csharp.d.ts +6 -0
- package/dist/architecture/layers/csharp.d.ts.map +1 -0
- package/dist/architecture/layers/csharp.js +100 -0
- package/dist/architecture/layers/csharp.js.map +7 -0
- package/dist/architecture/layers/elixir.js +116 -0
- package/dist/architecture/layers/elixir.js.map +7 -0
- package/dist/architecture/layers/go.d.ts +7 -0
- package/dist/architecture/layers/go.d.ts.map +1 -0
- package/dist/architecture/layers/go.js +117 -0
- package/dist/architecture/layers/go.js.map +7 -0
- package/dist/architecture/layers/index.d.ts +30 -0
- package/dist/architecture/layers/index.d.ts.map +1 -0
- package/dist/architecture/layers/index.js +100 -0
- package/dist/architecture/layers/index.js.map +7 -0
- package/dist/architecture/layers/java.d.ts +7 -0
- package/dist/architecture/layers/java.d.ts.map +1 -0
- package/dist/architecture/layers/java.js +119 -0
- package/dist/architecture/layers/java.js.map +7 -0
- package/dist/architecture/layers/python.d.ts +7 -0
- package/dist/architecture/layers/python.d.ts.map +1 -0
- package/dist/architecture/layers/python.js +111 -0
- package/dist/architecture/layers/python.js.map +7 -0
- package/dist/architecture/layers/ruby.d.ts +6 -0
- package/dist/architecture/layers/ruby.d.ts.map +1 -0
- package/dist/architecture/layers/ruby.js +95 -0
- package/dist/architecture/layers/ruby.js.map +7 -0
- package/dist/architecture/layers/rust.d.ts +6 -0
- package/dist/architecture/layers/rust.d.ts.map +1 -0
- package/dist/architecture/layers/rust.js +98 -0
- package/dist/architecture/layers/rust.js.map +7 -0
- package/dist/architecture/layers/types.d.ts +2 -0
- package/dist/architecture/layers/types.d.ts.map +1 -0
- package/dist/architecture/layers/types.js +17 -0
- package/dist/architecture/layers/types.js.map +7 -0
- package/dist/architecture/layers/typescript.d.ts +9 -0
- package/dist/architecture/layers/typescript.d.ts.map +1 -0
- package/dist/architecture/layers/typescript.js +143 -0
- package/dist/architecture/layers/typescript.js.map +7 -0
- package/dist/architecture/manifest/cargo.d.ts +3 -0
- package/dist/architecture/manifest/cargo.d.ts.map +1 -0
- package/dist/architecture/manifest/cargo.js +94 -0
- package/dist/architecture/manifest/cargo.js.map +7 -0
- package/dist/architecture/manifest/csproj.d.ts +3 -0
- package/dist/architecture/manifest/csproj.d.ts.map +1 -0
- package/dist/architecture/manifest/csproj.js +75 -0
- package/dist/architecture/manifest/csproj.js.map +7 -0
- package/dist/architecture/manifest/go.d.ts +3 -0
- package/dist/architecture/manifest/go.d.ts.map +1 -0
- package/dist/architecture/manifest/go.js +85 -0
- package/dist/architecture/manifest/go.js.map +7 -0
- package/dist/architecture/manifest/gradle.d.ts +3 -0
- package/dist/architecture/manifest/gradle.d.ts.map +1 -0
- package/dist/architecture/manifest/gradle.js +80 -0
- package/dist/architecture/manifest/gradle.js.map +7 -0
- package/dist/architecture/manifest/index.d.ts +12 -0
- package/dist/architecture/manifest/index.d.ts.map +1 -0
- package/dist/architecture/manifest/index.js +130 -0
- package/dist/architecture/manifest/index.js.map +7 -0
- package/dist/architecture/manifest/maven.d.ts +3 -0
- package/dist/architecture/manifest/maven.d.ts.map +1 -0
- package/dist/architecture/manifest/maven.js +76 -0
- package/dist/architecture/manifest/maven.js.map +7 -0
- package/dist/architecture/manifest/npm.d.ts +3 -0
- package/dist/architecture/manifest/npm.d.ts.map +1 -0
- package/dist/architecture/manifest/npm.js +103 -0
- package/dist/architecture/manifest/npm.js.map +7 -0
- package/dist/architecture/manifest/python.d.ts +3 -0
- package/dist/architecture/manifest/python.d.ts.map +1 -0
- package/dist/architecture/manifest/python.js +105 -0
- package/dist/architecture/manifest/python.js.map +7 -0
- package/dist/architecture/manifest/types.d.ts +2 -0
- package/dist/architecture/manifest/types.d.ts.map +1 -0
- package/dist/architecture/manifest/types.js +17 -0
- package/dist/architecture/manifest/types.js.map +7 -0
- package/dist/architecture/types.d.ts +91 -0
- package/dist/architecture/types.d.ts.map +1 -0
- package/dist/architecture/types.js +17 -0
- package/dist/architecture/types.js.map +7 -0
- package/dist/assets/logo.png +0 -0
- package/dist/banner.d.ts +6 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/banner.js +67 -0
- package/dist/banner.js.map +1 -0
- package/dist/bin/banner.d.ts +6 -0
- package/dist/bin/banner.d.ts.map +1 -0
- package/dist/bin/banner.js +88 -0
- package/dist/bin/banner.js.map +7 -0
- package/dist/bin/commands/affected.d.ts +3 -0
- package/dist/bin/commands/affected.d.ts.map +1 -0
- package/dist/bin/commands/affected.js +78 -0
- package/dist/bin/commands/affected.js.map +7 -0
- package/dist/bin/commands/architecture.d.ts +3 -0
- package/dist/bin/commands/architecture.d.ts.map +1 -0
- package/dist/bin/commands/architecture.js +125 -0
- package/dist/bin/commands/architecture.js.map +7 -0
- package/dist/bin/commands/caveman.js +136 -0
- package/dist/bin/commands/caveman.js.map +7 -0
- package/dist/bin/commands/context.d.ts +3 -0
- package/dist/bin/commands/context.d.ts.map +1 -0
- package/dist/bin/commands/context.js +81 -0
- package/dist/bin/commands/context.js.map +7 -0
- package/dist/bin/commands/coupling.d.ts +3 -0
- package/dist/bin/commands/coupling.d.ts.map +1 -0
- package/dist/bin/commands/coupling.js +164 -0
- package/dist/bin/commands/coupling.js.map +7 -0
- package/dist/bin/commands/dashboard.d.ts +3 -0
- package/dist/bin/commands/dashboard.d.ts.map +1 -0
- package/dist/bin/commands/dashboard.js +209 -0
- package/dist/bin/commands/dashboard.js.map +7 -0
- package/dist/bin/commands/dead-code.js +77 -0
- package/dist/bin/commands/dead-code.js.map +7 -0
- package/dist/bin/commands/export.js +2620 -0
- package/dist/bin/commands/export.js.map +7 -0
- package/dist/bin/commands/files.d.ts +3 -0
- package/dist/bin/commands/files.d.ts.map +1 -0
- package/dist/bin/commands/files.js +104 -0
- package/dist/bin/commands/files.js.map +7 -0
- package/dist/bin/commands/help.d.ts +4 -0
- package/dist/bin/commands/help.d.ts.map +1 -0
- package/dist/bin/commands/help.js +212 -0
- package/dist/bin/commands/help.js.map +7 -0
- package/dist/bin/commands/hotspots.js +77 -0
- package/dist/bin/commands/hotspots.js.map +7 -0
- package/dist/bin/commands/index.d.ts +3 -0
- package/dist/bin/commands/index.d.ts.map +1 -0
- package/dist/bin/commands/index.js +58 -0
- package/dist/bin/commands/index.js.map +7 -0
- package/dist/bin/commands/init.d.ts +3 -0
- package/dist/bin/commands/init.d.ts.map +1 -0
- package/dist/bin/commands/init.js +68 -0
- package/dist/bin/commands/init.js.map +7 -0
- package/dist/bin/commands/install.d.ts +3 -0
- package/dist/bin/commands/install.d.ts.map +1 -0
- package/dist/bin/commands/install.js +34 -0
- package/dist/bin/commands/install.js.map +7 -0
- package/dist/bin/commands/mark-dirty.d.ts +3 -0
- package/dist/bin/commands/mark-dirty.d.ts.map +1 -0
- package/dist/bin/commands/mark-dirty.js +51 -0
- package/dist/bin/commands/mark-dirty.js.map +7 -0
- package/dist/bin/commands/package.d.ts +3 -0
- package/dist/bin/commands/package.d.ts.map +1 -0
- package/dist/bin/commands/package.js +139 -0
- package/dist/bin/commands/package.js.map +7 -0
- package/dist/bin/commands/path.js +93 -0
- package/dist/bin/commands/path.js.map +7 -0
- package/dist/bin/commands/qdrant.d.ts +3 -0
- package/dist/bin/commands/qdrant.d.ts.map +1 -0
- package/dist/bin/commands/qdrant.js +159 -0
- package/dist/bin/commands/qdrant.js.map +1 -0
- package/dist/bin/commands/query.d.ts +3 -0
- package/dist/bin/commands/query.d.ts.map +1 -0
- package/dist/bin/commands/query.js +47 -0
- package/dist/bin/commands/query.js.map +7 -0
- package/dist/bin/commands/serve.d.ts +3 -0
- package/dist/bin/commands/serve.d.ts.map +1 -0
- package/dist/bin/commands/serve.js +59 -0
- package/dist/bin/commands/serve.js.map +7 -0
- package/dist/bin/commands/snapshot.js +122 -0
- package/dist/bin/commands/snapshot.js.map +7 -0
- package/dist/bin/commands/status.d.ts +3 -0
- package/dist/bin/commands/status.d.ts.map +1 -0
- package/dist/bin/commands/status.js +107 -0
- package/dist/bin/commands/status.js.map +7 -0
- package/dist/bin/commands/stop.d.ts +3 -0
- package/dist/bin/commands/stop.d.ts.map +1 -0
- package/dist/bin/commands/stop.js +81 -0
- package/dist/bin/commands/stop.js.map +1 -0
- package/dist/bin/commands/surprising.js +79 -0
- package/dist/bin/commands/surprising.js.map +7 -0
- package/dist/bin/commands/sync-if-dirty.d.ts +3 -0
- package/dist/bin/commands/sync-if-dirty.d.ts.map +1 -0
- package/dist/bin/commands/sync-if-dirty.js +67 -0
- package/dist/bin/commands/sync-if-dirty.js.map +7 -0
- package/dist/bin/commands/sync.d.ts +3 -0
- package/dist/bin/commands/sync.d.ts.map +1 -0
- package/dist/bin/commands/sync.js +81 -0
- package/dist/bin/commands/sync.js.map +7 -0
- package/dist/bin/commands/typesense.d.ts +3 -0
- package/dist/bin/commands/typesense.d.ts.map +1 -0
- package/dist/bin/commands/typesense.js +126 -0
- package/dist/bin/commands/typesense.js.map +1 -0
- package/dist/bin/commands/uninit.d.ts +4 -0
- package/dist/bin/commands/uninit.d.ts.map +1 -0
- package/dist/bin/commands/uninit.js +123 -0
- package/dist/bin/commands/uninit.js.map +7 -0
- package/dist/bin/commands/unlock.d.ts +3 -0
- package/dist/bin/commands/unlock.d.ts.map +1 -0
- package/dist/bin/commands/unlock.js +53 -0
- package/dist/bin/commands/unlock.js.map +7 -0
- package/dist/bin/commands/utils.d.ts +12 -0
- package/dist/bin/commands/utils.d.ts.map +1 -0
- package/dist/bin/commands/utils.js +56 -0
- package/dist/bin/commands/utils.js.map +7 -0
- package/dist/bin/installer/archive.js +230 -0
- package/dist/bin/installer/archive.js.map +7 -0
- package/dist/bin/installer/caveman.js +57 -0
- package/dist/bin/installer/caveman.js.map +7 -0
- package/dist/bin/installer/cli-agent.d.ts +15 -0
- package/dist/bin/installer/cli-agent.d.ts.map +1 -0
- package/dist/bin/installer/cli-agent.js +89 -0
- package/dist/bin/installer/cli-agent.js.map +7 -0
- package/dist/bin/installer/config-prompt.d.ts +13 -0
- package/dist/bin/installer/config-prompt.d.ts.map +1 -0
- package/dist/bin/installer/config-prompt.js +158 -0
- package/dist/bin/installer/config-prompt.js.map +7 -0
- package/dist/bin/installer/dashboard.d.ts +3 -0
- package/dist/bin/installer/dashboard.d.ts.map +1 -0
- package/dist/bin/installer/dashboard.js +149 -0
- package/dist/bin/installer/dashboard.js.map +7 -0
- package/dist/bin/installer/hooks.d.ts +5 -0
- package/dist/bin/installer/hooks.d.ts.map +1 -0
- package/dist/bin/installer/hooks.js +155 -0
- package/dist/bin/installer/hooks.js.map +7 -0
- package/dist/bin/installer/index.d.ts +11 -0
- package/dist/bin/installer/index.d.ts.map +1 -0
- package/dist/bin/installer/index.js +228 -0
- package/dist/bin/installer/index.js.map +7 -0
- package/dist/bin/installer/mcp.d.ts +5 -0
- package/dist/bin/installer/mcp.d.ts.map +1 -0
- package/dist/bin/installer/mcp.js +80 -0
- package/dist/bin/installer/mcp.js.map +7 -0
- package/dist/bin/installer/prompts.d.ts +28 -0
- package/dist/bin/installer/prompts.d.ts.map +1 -0
- package/dist/bin/installer/prompts.js +134 -0
- package/dist/bin/installer/prompts.js.map +7 -0
- package/dist/bin/installer/qdrant-dashboard.d.ts +4 -0
- package/dist/bin/installer/qdrant-dashboard.d.ts.map +1 -0
- package/dist/bin/installer/qdrant-dashboard.js +115 -0
- package/dist/bin/installer/qdrant-dashboard.js.map +7 -0
- package/dist/bin/installer/steering.d.ts +5 -0
- package/dist/bin/installer/steering.d.ts.map +1 -0
- package/dist/bin/installer/steering.js +283 -0
- package/dist/bin/installer/steering.js.map +7 -0
- package/dist/bin/kirograph.d.ts +6 -0
- package/dist/bin/kirograph.d.ts.map +1 -0
- package/dist/bin/kirograph.js +95 -0
- package/dist/bin/kirograph.js.map +7 -0
- package/dist/bin/progress.d.ts +14 -0
- package/dist/bin/progress.d.ts.map +1 -0
- package/dist/bin/progress.js +201 -0
- package/dist/bin/progress.js.map +7 -0
- package/dist/bin/ui.d.ts +11 -0
- package/dist/bin/ui.d.ts.map +1 -0
- package/dist/bin/ui.js +71 -0
- package/dist/bin/ui.js.map +7 -0
- package/dist/config.d.ts +48 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +273 -0
- package/dist/config.js.map +7 -0
- package/dist/context/index.d.ts +61 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +224 -0
- package/dist/context/index.js.map +7 -0
- package/dist/core/file-tree.d.ts +15 -0
- package/dist/core/file-tree.d.ts.map +1 -0
- package/dist/core/file-tree.js +69 -0
- package/dist/core/file-tree.js.map +7 -0
- package/dist/core/lock-manager.d.ts +20 -0
- package/dist/core/lock-manager.d.ts.map +1 -0
- package/dist/core/lock-manager.js +120 -0
- package/dist/core/lock-manager.js.map +7 -0
- package/dist/core/pipeline.d.ts +37 -0
- package/dist/core/pipeline.d.ts.map +1 -0
- package/dist/core/pipeline.js +375 -0
- package/dist/core/pipeline.js.map +7 -0
- package/dist/core/snapshot.js +141 -0
- package/dist/core/snapshot.js.map +7 -0
- package/dist/db/database.d.ts +133 -0
- package/dist/db/database.d.ts.map +1 -0
- package/dist/db/database.js +929 -0
- package/dist/db/database.js.map +7 -0
- package/dist/db/schema.sql +174 -0
- package/dist/errors.d.ts +49 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +160 -0
- package/dist/errors.js.map +7 -0
- package/dist/extraction/extractor.d.ts +29 -0
- package/dist/extraction/extractor.d.ts.map +1 -0
- package/dist/extraction/extractor.js +764 -0
- package/dist/extraction/extractor.js.map +7 -0
- package/dist/extraction/grammars.d.ts +48 -0
- package/dist/extraction/grammars.d.ts.map +1 -0
- package/dist/extraction/grammars.js +166 -0
- package/dist/extraction/grammars.js.map +7 -0
- package/dist/extraction/languages.d.ts +9 -0
- package/dist/extraction/languages.d.ts.map +1 -0
- package/dist/extraction/languages.js +103 -0
- package/dist/extraction/languages.js.map +7 -0
- package/dist/extraction/wasm/tree-sitter-pascal.wasm +0 -0
- package/dist/frameworks/csharp.d.ts +8 -0
- package/dist/frameworks/csharp.d.ts.map +1 -0
- package/dist/frameworks/csharp.js +93 -0
- package/dist/frameworks/csharp.js.map +7 -0
- package/dist/frameworks/elixir.js +142 -0
- package/dist/frameworks/elixir.js.map +7 -0
- package/dist/frameworks/express.d.ts +8 -0
- package/dist/frameworks/express.d.ts.map +1 -0
- package/dist/frameworks/express.js +143 -0
- package/dist/frameworks/express.js.map +7 -0
- package/dist/frameworks/go.d.ts +8 -0
- package/dist/frameworks/go.d.ts.map +1 -0
- package/dist/frameworks/go.js +85 -0
- package/dist/frameworks/go.js.map +7 -0
- package/dist/frameworks/index.d.ts +30 -0
- package/dist/frameworks/index.d.ts.map +1 -0
- package/dist/frameworks/index.js +243 -0
- package/dist/frameworks/index.js.map +7 -0
- package/dist/frameworks/java.d.ts +8 -0
- package/dist/frameworks/java.d.ts.map +1 -0
- package/dist/frameworks/java.js +87 -0
- package/dist/frameworks/java.js.map +7 -0
- package/dist/frameworks/laravel.d.ts +9 -0
- package/dist/frameworks/laravel.d.ts.map +1 -0
- package/dist/frameworks/laravel.js +115 -0
- package/dist/frameworks/laravel.js.map +7 -0
- package/dist/frameworks/python.d.ts +10 -0
- package/dist/frameworks/python.d.ts.map +1 -0
- package/dist/frameworks/python.js +158 -0
- package/dist/frameworks/python.js.map +7 -0
- package/dist/frameworks/react.d.ts +9 -0
- package/dist/frameworks/react.d.ts.map +1 -0
- package/dist/frameworks/react.js +230 -0
- package/dist/frameworks/react.js.map +7 -0
- package/dist/frameworks/ruby.d.ts +8 -0
- package/dist/frameworks/ruby.d.ts.map +1 -0
- package/dist/frameworks/ruby.js +136 -0
- package/dist/frameworks/ruby.js.map +7 -0
- package/dist/frameworks/rust.d.ts +8 -0
- package/dist/frameworks/rust.d.ts.map +1 -0
- package/dist/frameworks/rust.js +82 -0
- package/dist/frameworks/rust.js.map +7 -0
- package/dist/frameworks/svelte.d.ts +8 -0
- package/dist/frameworks/svelte.d.ts.map +1 -0
- package/dist/frameworks/svelte.js +174 -0
- package/dist/frameworks/svelte.js.map +7 -0
- package/dist/frameworks/swift.d.ts +10 -0
- package/dist/frameworks/swift.d.ts.map +1 -0
- package/dist/frameworks/swift.js +151 -0
- package/dist/frameworks/swift.js.map +7 -0
- package/dist/frameworks/types.d.ts +37 -0
- package/dist/frameworks/types.d.ts.map +1 -0
- package/dist/frameworks/types.js +17 -0
- package/dist/frameworks/types.js.map +7 -0
- package/dist/graph/queries.d.ts +53 -0
- package/dist/graph/queries.d.ts.map +1 -0
- package/dist/graph/queries.js +224 -0
- package/dist/graph/queries.js.map +7 -0
- package/dist/graph/traversal.d.ts +35 -0
- package/dist/graph/traversal.d.ts.map +1 -0
- package/dist/graph/traversal.js +148 -0
- package/dist/graph/traversal.js.map +7 -0
- package/dist/index.d.ts +102 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +303 -0
- package/dist/index.js.map +7 -0
- package/dist/installer/index.d.ts +10 -0
- package/dist/installer/index.d.ts.map +1 -0
- package/dist/installer/index.js +526 -0
- package/dist/installer/index.js.map +1 -0
- package/dist/mcp/server.d.ts +16 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +116 -0
- package/dist/mcp/server.js.map +7 -0
- package/dist/mcp/tools.d.ts +37 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +779 -0
- package/dist/mcp/tools.js.map +7 -0
- package/dist/mcp/transport.d.ts +29 -0
- package/dist/mcp/transport.d.ts.map +1 -0
- package/dist/mcp/transport.js +70 -0
- package/dist/mcp/transport.js.map +7 -0
- package/dist/resolution/index.d.ts +56 -0
- package/dist/resolution/index.d.ts.map +1 -0
- package/dist/resolution/index.js +384 -0
- package/dist/resolution/index.js.map +7 -0
- package/dist/resolution/name-matcher.d.ts +25 -0
- package/dist/resolution/name-matcher.d.ts.map +1 -0
- package/dist/resolution/name-matcher.js +60 -0
- package/dist/resolution/name-matcher.js.map +7 -0
- package/dist/scripts/postinstall.js +64 -0
- package/dist/search/query-utils.d.ts +21 -0
- package/dist/search/query-utils.d.ts.map +1 -0
- package/dist/search/query-utils.js +219 -0
- package/dist/search/query-utils.js.map +7 -0
- package/dist/search/searcher.d.ts +15 -0
- package/dist/search/searcher.d.ts.map +1 -0
- package/dist/search/searcher.js +49 -0
- package/dist/search/searcher.js.map +7 -0
- package/dist/sync/index.d.ts +33 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +200 -0
- package/dist/sync/index.js.map +7 -0
- package/dist/types.d.ts +131 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +37 -0
- package/dist/types.js.map +7 -0
- package/dist/utils.d.ts +52 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +254 -0
- package/dist/utils.js.map +7 -0
- package/dist/vectors/index.d.ts +71 -0
- package/dist/vectors/index.d.ts.map +1 -0
- package/dist/vectors/index.js +480 -0
- package/dist/vectors/index.js.map +7 -0
- package/dist/vectors/lancedb-index.d.ts +50 -0
- package/dist/vectors/lancedb-index.d.ts.map +1 -0
- package/dist/vectors/lancedb-index.js +153 -0
- package/dist/vectors/lancedb-index.js.map +7 -0
- package/dist/vectors/orama-index.d.ts +54 -0
- package/dist/vectors/orama-index.d.ts.map +1 -0
- package/dist/vectors/orama-index.js +213 -0
- package/dist/vectors/orama-index.js.map +7 -0
- package/dist/vectors/pglite-index.d.ts +53 -0
- package/dist/vectors/pglite-index.d.ts.map +1 -0
- package/dist/vectors/pglite-index.js +194 -0
- package/dist/vectors/pglite-index.js.map +7 -0
- package/dist/vectors/qdrant-index.d.ts +70 -0
- package/dist/vectors/qdrant-index.d.ts.map +1 -0
- package/dist/vectors/qdrant-index.js +364 -0
- package/dist/vectors/qdrant-index.js.map +7 -0
- package/dist/vectors/typesense-index.d.ts +75 -0
- package/dist/vectors/typesense-index.d.ts.map +1 -0
- package/dist/vectors/typesense-index.js +453 -0
- package/dist/vectors/typesense-index.js.map +7 -0
- package/dist/vectors/vec-index.d.ts +52 -0
- package/dist/vectors/vec-index.d.ts.map +1 -0
- package/dist/vectors/vec-index.js +198 -0
- package/dist/vectors/vec-index.js.map +7 -0
- package/package.json +67 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/vectors/qdrant-index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Qdrant Index\n *\n * ANN vector search backed by Qdrant running in embedded mode.\n * The engine spawns the Qdrant binary as a managed child process, persisting\n * data to .kirograph/qdrant/ via QDRANT__STORAGE__STORAGE_PATH.\n *\n * Opt-in: set config.semanticEngine = 'qdrant'\n * Required optional dependency (not installed by default):\n * npm install qdrant-local\n *\n * Uses @qdrant/qdrant-js (QdrantClient) for the REST API \u2014 available as a\n * transitive dependency of qdrant-local.\n *\n * Key characteristics:\n * - Full Qdrant feature set: filtering, payload indexing, HNSW ANN search\n * - Data persisted to disk across restarts\n * - Async startup check via /readyz (no blocking sleep)\n * - Cosine distance metric\n * - Node IDs stored in payload; deterministic UUIDs used as Qdrant point IDs\n */\n\nimport * as path from 'path';\nimport * as crypto from 'crypto';\nimport * as http from 'http';\nimport * as fs from 'fs';\nimport { logDebug, logWarn, logError } from '../errors';\nimport type { Node } from '../types';\n\n// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst DEFAULT_DIM = 768;\nconst STORAGE_DIR = 'qdrant';\nconst COLLECTION = 'kg_nodes';\nconst READYZ_TIMEOUT_MS = 10_000;\nconst READYZ_POLL_MS = 100;\nconst SERVER_STATE_FILE = 'qdrant-server.json';\nexport const DASHBOARD_SUBDIR = 'qdrant/dashboard'; // relative to kirographDir\n\ninterface ServerState { pid: number; port: number; }\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Derive a deterministic UUID from an arbitrary string (e.g. \"function:abc123\"). */\nfunction toUuid(nodeId: string): string {\n const h = crypto.createHash('md5').update(nodeId).digest('hex');\n return `${h.slice(0,8)}-${h.slice(8,12)}-${h.slice(12,16)}-${h.slice(16,20)}-${h.slice(20,32)}`;\n}\n\n/** Find a random free TCP port. */\nfunction getFreePort(): Promise<number> {\n return new Promise((resolve, reject) => {\n const srv = http.createServer().listen(0, '127.0.0.1', () => {\n const port = (srv.address() as { port: number }).port;\n srv.close(() => resolve(port));\n });\n srv.on('error', reject);\n });\n}\n\n/** Poll GET http://127.0.0.1:{port}/readyz until 200 or timeout. */\nfunction waitReady(port: number, timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const deadline = Date.now() + timeoutMs;\n\n function check() {\n const req = http.get(`http://127.0.0.1:${port}/readyz`, res => {\n if (res.statusCode === 200) return resolve();\n scheduleRetry();\n });\n req.on('error', scheduleRetry);\n req.setTimeout(200, () => { req.destroy(); scheduleRetry(); });\n }\n\n function scheduleRetry() {\n if (Date.now() >= deadline) return reject(new Error('Qdrant readyz timeout'));\n setTimeout(check, READYZ_POLL_MS);\n }\n\n check();\n });\n}\n\n// \u2500\u2500 QdrantIndex \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class QdrantIndex {\n private client: any = null;\n private child: any = null;\n private _available = false;\n private _ownedProcess = false;\n private storagePath: string;\n private stateFile: string;\n\n constructor(\n private readonly kirographDir: string,\n private readonly dim = DEFAULT_DIM,\n ) {\n this.storagePath = path.join(kirographDir, STORAGE_DIR);\n this.stateFile = path.join(kirographDir, SERVER_STATE_FILE);\n }\n\n isAvailable(): boolean { return this._available; }\n\n private readState(): ServerState | null {\n try { return JSON.parse(fs.readFileSync(this.stateFile, 'utf8')) as ServerState; }\n catch { return null; }\n }\n private writeState(s: ServerState): void {\n try { fs.writeFileSync(this.stateFile, JSON.stringify(s)); } catch { /* ignore */ }\n }\n private clearState(): void {\n try { fs.unlinkSync(this.stateFile); } catch { /* ignore */ }\n }\n private isProcessAlive(pid: number): boolean {\n try { process.kill(pid, 0); return true; } catch { return false; }\n }\n\n /** The HTTP port of the running server, or null if not available. */\n getPort(): number | null {\n if (this._available) {\n const s = this.readState();\n return s?.port ?? null;\n }\n const s = this.readState();\n if (s && this.isProcessAlive(s.pid)) return s.port;\n return null;\n }\n\n /**\n * Locate the Qdrant binary (via qdrant-local), spawn it with a project-scoped\n * storage path, wait until /readyz responds, then ensure the collection exists.\n * Silent no-op when qdrant-local is not installed.\n */\n async initialize(): Promise<void> {\n if (this._available) return;\n\n // \u2500\u2500 1. Locate binary via qdrant-local \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let binPath: string;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const pkgPath = require.resolve('qdrant-local/package.json');\n const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));\n const { directory, name } = pkg.qdrantBinary as { directory: string; name: string };\n const exeName = ['win32', 'cygwin'].includes(process.platform) ? `${name}.exe` : name;\n binPath = path.join(path.dirname(pkgPath), directory, exeName);\n if (!fs.existsSync(binPath)) {\n logDebug('QdrantIndex: qdrant binary not found \u2014 run: npm install qdrant-local');\n return;\n }\n } catch {\n logDebug('QdrantIndex: qdrant-local not installed \u2014 Qdrant engine unavailable');\n return;\n }\n\n // \u2500\u2500 2. Load @qdrant/qdrant-js client \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let QdrantClient: any;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n QdrantClient = require('@qdrant/qdrant-js').QdrantClient;\n } catch {\n logDebug('QdrantIndex: @qdrant/qdrant-js not available \u2014 Qdrant engine unavailable');\n return;\n }\n\n // \u2500\u2500 3. Try to reuse an already-running server \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const saved = this.readState();\n if (saved) {\n if (this.isProcessAlive(saved.pid)) {\n try {\n await waitReady(saved.port, 3_000);\n this.client = new QdrantClient({ host: '127.0.0.1', port: saved.port, checkCompatibility: false });\n await this.ensureCollection();\n this._available = true;\n this._ownedProcess = false;\n logDebug('QdrantIndex: reused running server', { pid: saved.pid, port: saved.port });\n return;\n } catch {\n // Stale \u2014 kill and respawn\n try { process.kill(saved.pid, 'SIGKILL'); } catch { /* ignore */ }\n await new Promise(r => setTimeout(r, 300));\n }\n }\n this.clearState();\n }\n\n // \u2500\u2500 4. Spawn Qdrant binary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n try {\n fs.mkdirSync(this.storagePath, { recursive: true });\n\n const port = await getFreePort();\n\n // Auto-serve dashboard if UI files are present\n const dashboardDir = path.join(this.kirographDir, DASHBOARD_SUBDIR);\n const staticEnv: Record<string, string> = fs.existsSync(dashboardDir)\n ? { QDRANT__SERVICE__STATIC_CONTENT_DIR: dashboardDir }\n : {};\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { spawn } = require('child_process');\n this.child = spawn(binPath, [], {\n stdio: 'ignore',\n env: {\n ...process.env,\n QDRANT__SERVICE__HTTP_PORT: String(port),\n QDRANT__STORAGE__STORAGE_PATH: this.storagePath,\n QDRANT__LOG_LEVEL: 'WARN',\n ...staticEnv,\n },\n });\n\n this.child.unref();\n\n // \u2500\u2500 5. Wait until Qdrant is ready \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n await waitReady(port, READYZ_TIMEOUT_MS);\n\n this.writeState({ pid: this.child.pid, port });\n\n this.client = new QdrantClient({ host: '127.0.0.1', port, checkCompatibility: false });\n\n // \u2500\u2500 6. Ensure collection exists \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n await this.ensureCollection();\n\n this._available = true;\n this._ownedProcess = true;\n logDebug('QdrantIndex: ready', { storagePath: this.storagePath, port, dim: this.dim });\n } catch (err) {\n logError('QdrantIndex: initialization failed', { error: String(err) });\n }\n }\n\n private async ensureCollection(): Promise<void> {\n const existsResult = await this.client.collectionExists(COLLECTION);\n const exists = typeof existsResult === 'object' ? existsResult.exists : existsResult;\n if (!exists) {\n await this.client.createCollection(COLLECTION, {\n vectors: { size: this.dim, distance: 'Cosine' },\n });\n }\n }\n\n /**\n * Upsert a node's embedding with retry logic.\n * Qdrant's upsert is idempotent \u2014 the same point ID is overwritten if it already exists.\n * Retries up to 3 times with exponential backoff to handle transient connection failures.\n */\n async upsert(node: Node, embedding: Float32Array): Promise<void> {\n if (!this._available || !this.client) return;\n\n const point = {\n id: toUuid(node.id),\n vector: Array.from(embedding),\n payload: {\n node_id: node.id,\n name: node.name,\n kind: node.kind,\n file_path: node.filePath,\n signature: node.signature ?? '',\n },\n };\n\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n await this.client.upsert(COLLECTION, { points: [point] });\n return;\n } catch (err) {\n if (attempt < 2) {\n await new Promise(r => setTimeout(r, 100 * (attempt + 1)));\n } else {\n logWarn('QdrantIndex: upsert failed', { nodeId: node.id, error: String(err) });\n }\n }\n }\n }\n\n /**\n * Batch upsert multiple nodes' embeddings in a single HTTP request.\n * Much more efficient than individual upserts for bulk indexing.\n * Retries up to 3 times with exponential backoff.\n */\n async bulkUpsert(nodes: Node[], embeddings: Float32Array[]): Promise<void> {\n if (!this._available || !this.client) return;\n if (nodes.length === 0) return;\n\n const points = nodes.map((node, i) => ({\n id: toUuid(node.id),\n vector: Array.from(embeddings[i]!),\n payload: {\n node_id: node.id,\n name: node.name,\n kind: node.kind,\n file_path: node.filePath,\n signature: node.signature ?? '',\n },\n }));\n\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n await this.client.upsert(COLLECTION, { points });\n return;\n } catch (err) {\n if (attempt < 2) {\n await new Promise(r => setTimeout(r, 200 * (attempt + 1)));\n } else {\n logWarn('QdrantIndex: bulk upsert failed', { count: nodes.length, error: String(err) });\n }\n }\n }\n }\n\n /**\n * Remove a point from the collection with retry logic.\n */\n async delete(nodeId: string): Promise<void> {\n if (!this._available || !this.client) return;\n\n for (let attempt = 0; attempt < 3; attempt++) {\n try {\n await this.client.delete(COLLECTION, { points: [toUuid(nodeId)] });\n return;\n } catch (err) {\n if (attempt < 2) {\n await new Promise(r => setTimeout(r, 100 * (attempt + 1)));\n } else {\n logWarn('QdrantIndex: delete failed', { nodeId, error: String(err) });\n }\n }\n }\n }\n\n /**\n * ANN vector search. Returns node IDs ordered by cosine similarity (descending).\n */\n async search(queryVec: Float32Array, topN = 10): Promise<string[]> {\n if (!this._available || !this.client) return [];\n\n try {\n const results = await this.client.search(COLLECTION, {\n vector: Array.from(queryVec),\n limit: topN,\n with_payload: ['node_id'],\n });\n return results.map((r: any) => r.payload.node_id as string);\n } catch (err) {\n logWarn('QdrantIndex: search failed', { error: String(err) });\n return [];\n }\n }\n\n /**\n * Return all node IDs currently stored in the collection (paginated scroll).\n */\n async getEmbeddedNodeIds(): Promise<string[]> {\n if (!this._available || !this.client) return [];\n\n const ids: string[] = [];\n let offset: string | number | null = null;\n\n try {\n do {\n const page: { points: any[]; next_page_offset: string | number | null } =\n await this.client.scroll(COLLECTION, {\n with_payload: ['node_id'],\n with_vector: false,\n limit: 1000,\n ...(offset !== null ? { offset } : {}),\n });\n for (const pt of page.points) {\n if (pt.payload?.node_id) ids.push(pt.payload.node_id as string);\n }\n offset = page.next_page_offset ?? null;\n } while (offset !== null);\n } catch {\n return ids;\n }\n\n return ids;\n }\n\n /** Disconnect the client. The Qdrant server keeps running as a daemon. */\n close(): void {\n this._available = false;\n this.child = null;\n this.client = null;\n }\n\n /** Number of points currently in the collection. */\n async count(): Promise<number> {\n if (!this._available || !this.client) return 0;\n try {\n const result = await this.client.count(COLLECTION, { exact: true });\n return result.count;\n } catch {\n return 0;\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["s"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KiroGraph Typesense Index
|
|
3
|
+
*
|
|
4
|
+
* ANN vector search backed by Typesense running in embedded mode.
|
|
5
|
+
* The engine downloads the Typesense binary on first use (cached at
|
|
6
|
+
* ~/.kirograph/bin/), then spawns it as a managed child process persisting
|
|
7
|
+
* data to .kirograph/typesense/.
|
|
8
|
+
*
|
|
9
|
+
* Opt-in: set config.semanticEngine = 'typesense'
|
|
10
|
+
* Required optional dependency (not installed by default):
|
|
11
|
+
* npm install typesense
|
|
12
|
+
*
|
|
13
|
+
* Key characteristics:
|
|
14
|
+
* - HNSW ANN vector search with cosine distance
|
|
15
|
+
* - Binary auto-downloaded on first use (~37MB, cached globally)
|
|
16
|
+
* - No separate server setup required
|
|
17
|
+
* - Async startup check via /health
|
|
18
|
+
* - Node IDs used directly as Typesense document IDs (colon sanitised)
|
|
19
|
+
*/
|
|
20
|
+
import type { Node } from '../types';
|
|
21
|
+
export declare class TypesenseIndex {
|
|
22
|
+
private readonly kirographDir;
|
|
23
|
+
private readonly dim;
|
|
24
|
+
private client;
|
|
25
|
+
private child;
|
|
26
|
+
private _available;
|
|
27
|
+
private _ownedProcess;
|
|
28
|
+
private _failReason;
|
|
29
|
+
private storagePath;
|
|
30
|
+
private stateFile;
|
|
31
|
+
constructor(kirographDir: string, dim?: number);
|
|
32
|
+
private readState;
|
|
33
|
+
private writeState;
|
|
34
|
+
private clearState;
|
|
35
|
+
private isProcessAlive;
|
|
36
|
+
isAvailable(): boolean;
|
|
37
|
+
getFailReason(): string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Download the Typesense binary if needed, spawn it with a project-scoped
|
|
40
|
+
* data directory, wait until /health responds, then ensure the collection
|
|
41
|
+
* exists. Silent no-op when `typesense` npm package is not installed.
|
|
42
|
+
*/
|
|
43
|
+
initialize(): Promise<void>;
|
|
44
|
+
private ensureCollection;
|
|
45
|
+
/**
|
|
46
|
+
* Insert or update a node's embedding. Typesense's upsert overwrites the
|
|
47
|
+
* document if the `id` already exists.
|
|
48
|
+
*/
|
|
49
|
+
upsert(node: Node, embedding: Float32Array): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Bulk upsert a batch of nodes. Uses Typesense's import endpoint (single
|
|
52
|
+
* HTTP request per batch) to avoid connection churn on large codebases.
|
|
53
|
+
*/
|
|
54
|
+
bulkUpsert(nodes: Node[], embeddings: Float32Array[]): Promise<void>;
|
|
55
|
+
/**
|
|
56
|
+
* Remove a document from the collection.
|
|
57
|
+
*/
|
|
58
|
+
delete(nodeId: string): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* ANN vector search. Returns node IDs ordered by cosine similarity (descending).
|
|
61
|
+
*/
|
|
62
|
+
search(queryVec: Float32Array, topN?: number): Promise<string[]>;
|
|
63
|
+
/**
|
|
64
|
+
* Return all node IDs currently stored in the collection via paginated export.
|
|
65
|
+
*/
|
|
66
|
+
getEmbeddedNodeIds(): Promise<string[]>;
|
|
67
|
+
/** Number of documents currently in the collection. */
|
|
68
|
+
count(): Promise<number>;
|
|
69
|
+
/**
|
|
70
|
+
* Disconnect the client. The Typesense server keeps running as a background
|
|
71
|
+
* daemon so the next command can reconnect instantly via the state file.
|
|
72
|
+
*/
|
|
73
|
+
close(): void;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=typesense-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typesense-index.d.ts","sourceRoot":"","sources":["../../src/vectors/typesense-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAUH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAyLrC,qBAAa,cAAc;IAUvB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,GAAG;IAVtB,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,KAAK,CAAsB;IACnC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,aAAa,CAAe;IACpC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,SAAS,CAAW;gBAGT,YAAY,EAAE,MAAM,EACpB,GAAG,SAAc;IAMpC,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,cAAc;IAItB,WAAW,IAAI,OAAO;IAItB,aAAa,IAAI,MAAM,GAAG,IAAI;IAI9B;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAkInB,gBAAgB;IAiB9B;;;OAGG;IACG,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBhE;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,UAAU,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB1E;;OAEG;IACG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAa3C;;OAEG;IACG,MAAM,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,SAAK,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IA0BlE;;OAEG;IACG,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAwB7C,uDAAuD;IACjD,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC;IAY9B;;;OAGG;IACH,KAAK,IAAI,IAAI;CAKd"}
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var typesense_index_exports = {};
|
|
30
|
+
__export(typesense_index_exports, {
|
|
31
|
+
TypesenseIndex: () => TypesenseIndex
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(typesense_index_exports);
|
|
34
|
+
var path = __toESM(require("path"));
|
|
35
|
+
var fs = __toESM(require("fs"));
|
|
36
|
+
var http = __toESM(require("http"));
|
|
37
|
+
var import_os = require("os");
|
|
38
|
+
var import_errors = require("../errors");
|
|
39
|
+
var import_archive = require("../bin/installer/archive");
|
|
40
|
+
const DEFAULT_DIM = 768;
|
|
41
|
+
const STORAGE_DIR = "typesense";
|
|
42
|
+
const COLLECTION = "kg_nodes";
|
|
43
|
+
const API_KEY = "kirograph-local";
|
|
44
|
+
const TYPESENSE_VERSION = "28.0";
|
|
45
|
+
const BIN_CACHE_DIR = path.join((0, import_os.homedir)(), ".kirograph", "bin");
|
|
46
|
+
const HEALTH_TIMEOUT_MS = 6e4;
|
|
47
|
+
const HEALTH_POLL_MS = 200;
|
|
48
|
+
const SERVER_STATE_FILE = "typesense-server.json";
|
|
49
|
+
function getBinaryUrl() {
|
|
50
|
+
const v = TYPESENSE_VERSION;
|
|
51
|
+
const { platform, arch } = process;
|
|
52
|
+
if (platform === "darwin") {
|
|
53
|
+
const a = arch === "arm64" ? "darwin-arm64" : "darwin-amd64";
|
|
54
|
+
return `https://dl.typesense.org/releases/${v}/typesense-server-${v}-${a}.tar.gz`;
|
|
55
|
+
}
|
|
56
|
+
if (platform === "linux") {
|
|
57
|
+
const a = arch === "arm64" ? "linux-arm64" : "linux-amd64";
|
|
58
|
+
return `https://dl.typesense.org/releases/${v}/typesense-server-${v}-${a}.tar.gz`;
|
|
59
|
+
}
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
function getBinPath() {
|
|
63
|
+
return path.join(BIN_CACHE_DIR, `typesense-server-${TYPESENSE_VERSION}`);
|
|
64
|
+
}
|
|
65
|
+
async function downloadBinary(url, destPath) {
|
|
66
|
+
fs.mkdirSync(BIN_CACHE_DIR, { recursive: true });
|
|
67
|
+
await (0, import_archive.extractBinaryFromTarGz)(url, "typesense-server", destPath);
|
|
68
|
+
}
|
|
69
|
+
function toDocId(nodeId) {
|
|
70
|
+
return nodeId.replace(/:/g, "__");
|
|
71
|
+
}
|
|
72
|
+
function getFreePorts(count) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
const ports = [];
|
|
75
|
+
const servers = [];
|
|
76
|
+
let done = 0;
|
|
77
|
+
for (let i = 0; i < count; i++) {
|
|
78
|
+
const srv = http.createServer();
|
|
79
|
+
servers.push(srv);
|
|
80
|
+
srv.listen(0, "127.0.0.1", () => {
|
|
81
|
+
ports.push(srv.address().port);
|
|
82
|
+
done++;
|
|
83
|
+
if (done === count) {
|
|
84
|
+
let closed = 0;
|
|
85
|
+
for (const s of servers) s.close(() => {
|
|
86
|
+
if (++closed === count) resolve(ports);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
srv.on("error", reject);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function waitReady(port, timeoutMs) {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
const deadline = Date.now() + timeoutMs;
|
|
97
|
+
function check() {
|
|
98
|
+
const req = http.get(`http://127.0.0.1:${port}/health`, (res) => {
|
|
99
|
+
let body = "";
|
|
100
|
+
res.on("data", (c) => body += c);
|
|
101
|
+
res.on("end", () => {
|
|
102
|
+
try {
|
|
103
|
+
if (JSON.parse(body).ok === true) return resolve();
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
scheduleRetry();
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
req.on("error", scheduleRetry);
|
|
110
|
+
req.setTimeout(300, () => {
|
|
111
|
+
req.destroy();
|
|
112
|
+
scheduleRetry();
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function scheduleRetry() {
|
|
116
|
+
if (Date.now() >= deadline) return reject(new Error("Typesense health timeout"));
|
|
117
|
+
setTimeout(check, HEALTH_POLL_MS);
|
|
118
|
+
}
|
|
119
|
+
check();
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
const _silentLogger = { log: () => {
|
|
123
|
+
}, warn: () => {
|
|
124
|
+
}, error: () => {
|
|
125
|
+
}, debug: () => {
|
|
126
|
+
}, info: () => {
|
|
127
|
+
}, trace: () => {
|
|
128
|
+
}, setLevel: () => {
|
|
129
|
+
} };
|
|
130
|
+
function makeClientConfig(port) {
|
|
131
|
+
return {
|
|
132
|
+
nodes: [{ host: "127.0.0.1", port, protocol: "http" }],
|
|
133
|
+
apiKey: API_KEY,
|
|
134
|
+
connectionTimeoutSeconds: 10,
|
|
135
|
+
retryIntervalSeconds: 0.1,
|
|
136
|
+
numRetries: 3,
|
|
137
|
+
logger: _silentLogger,
|
|
138
|
+
logLevel: "silent"
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
class TypesenseIndex {
|
|
142
|
+
constructor(kirographDir, dim = DEFAULT_DIM) {
|
|
143
|
+
this.kirographDir = kirographDir;
|
|
144
|
+
this.dim = dim;
|
|
145
|
+
this.client = null;
|
|
146
|
+
this.child = null;
|
|
147
|
+
this._available = false;
|
|
148
|
+
this._ownedProcess = false;
|
|
149
|
+
// true only when we spawned the child ourselves
|
|
150
|
+
this._failReason = null;
|
|
151
|
+
this.storagePath = path.join(kirographDir, STORAGE_DIR);
|
|
152
|
+
this.stateFile = path.join(kirographDir, SERVER_STATE_FILE);
|
|
153
|
+
}
|
|
154
|
+
readState() {
|
|
155
|
+
try {
|
|
156
|
+
return JSON.parse(fs.readFileSync(this.stateFile, "utf8"));
|
|
157
|
+
} catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
writeState(state) {
|
|
162
|
+
try {
|
|
163
|
+
fs.writeFileSync(this.stateFile, JSON.stringify(state));
|
|
164
|
+
} catch {
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
clearState() {
|
|
168
|
+
try {
|
|
169
|
+
fs.unlinkSync(this.stateFile);
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
isProcessAlive(pid) {
|
|
174
|
+
try {
|
|
175
|
+
process.kill(pid, 0);
|
|
176
|
+
return true;
|
|
177
|
+
} catch {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
isAvailable() {
|
|
182
|
+
return this._available;
|
|
183
|
+
}
|
|
184
|
+
getFailReason() {
|
|
185
|
+
return this._failReason;
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Download the Typesense binary if needed, spawn it with a project-scoped
|
|
189
|
+
* data directory, wait until /health responds, then ensure the collection
|
|
190
|
+
* exists. Silent no-op when `typesense` npm package is not installed.
|
|
191
|
+
*/
|
|
192
|
+
async initialize() {
|
|
193
|
+
if (this._available) return;
|
|
194
|
+
let TypesenseClient;
|
|
195
|
+
try {
|
|
196
|
+
TypesenseClient = require("typesense").Client;
|
|
197
|
+
} catch {
|
|
198
|
+
(0, import_errors.logDebug)("TypesenseIndex: typesense not installed \u2014 Typesense engine unavailable");
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const binPath = getBinPath();
|
|
202
|
+
if (!fs.existsSync(binPath)) {
|
|
203
|
+
const url = getBinaryUrl();
|
|
204
|
+
if (!url) {
|
|
205
|
+
(0, import_errors.logDebug)("TypesenseIndex: no Typesense binary available for this platform");
|
|
206
|
+
this._failReason = "Typesense binary not available for this platform";
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
process.stdout.write(` Downloading Typesense ${TYPESENSE_VERSION} binary (~37 MB, cached for future use)\u2026
|
|
210
|
+
`);
|
|
211
|
+
try {
|
|
212
|
+
await downloadBinary(url, binPath);
|
|
213
|
+
process.stdout.write(` Typesense binary ready.
|
|
214
|
+
`);
|
|
215
|
+
} catch (err) {
|
|
216
|
+
process.stdout.write(` Typesense binary download failed: ${String(err)}
|
|
217
|
+
`);
|
|
218
|
+
(0, import_errors.logError)("TypesenseIndex: binary download failed", { error: String(err) });
|
|
219
|
+
this._failReason = `binary download failed: ${String(err)}`;
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const saved = this.readState();
|
|
224
|
+
if (saved) {
|
|
225
|
+
const alive = this.isProcessAlive(saved.pid);
|
|
226
|
+
if (alive) {
|
|
227
|
+
try {
|
|
228
|
+
await waitReady(saved.apiPort, 5e3);
|
|
229
|
+
this.client = new TypesenseClient(makeClientConfig(saved.apiPort));
|
|
230
|
+
await this.ensureCollection();
|
|
231
|
+
this._available = true;
|
|
232
|
+
this._ownedProcess = false;
|
|
233
|
+
(0, import_errors.logDebug)("TypesenseIndex: reused running server", { pid: saved.pid, port: saved.apiPort });
|
|
234
|
+
return;
|
|
235
|
+
} catch {
|
|
236
|
+
(0, import_errors.logDebug)("TypesenseIndex: stale process detected, killing", { pid: saved.pid });
|
|
237
|
+
try {
|
|
238
|
+
process.kill(saved.pid, "SIGKILL");
|
|
239
|
+
} catch {
|
|
240
|
+
}
|
|
241
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
this.clearState();
|
|
245
|
+
}
|
|
246
|
+
let startupLog = "";
|
|
247
|
+
try {
|
|
248
|
+
fs.mkdirSync(this.storagePath, { recursive: true });
|
|
249
|
+
for (const sub of ["db", "meta"]) {
|
|
250
|
+
const lockFile = path.join(this.storagePath, sub, "LOCK");
|
|
251
|
+
try {
|
|
252
|
+
fs.unlinkSync(lockFile);
|
|
253
|
+
} catch {
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
const [apiPort, peeringPort] = await getFreePorts(2);
|
|
257
|
+
const { spawn } = require("child_process");
|
|
258
|
+
this.child = spawn(binPath, [
|
|
259
|
+
`--data-dir=${this.storagePath}`,
|
|
260
|
+
`--api-key=${API_KEY}`,
|
|
261
|
+
`--api-port=${apiPort}`,
|
|
262
|
+
`--peering-port=${peeringPort}`,
|
|
263
|
+
"--enable-cors"
|
|
264
|
+
], { stdio: ["ignore", "pipe", "pipe"] });
|
|
265
|
+
const onLog = (chunk) => {
|
|
266
|
+
startupLog += chunk.toString("utf8");
|
|
267
|
+
};
|
|
268
|
+
this.child.stdout?.on("data", onLog);
|
|
269
|
+
this.child.stderr?.on("data", onLog);
|
|
270
|
+
this.child.unref();
|
|
271
|
+
process.stdout.write(` Starting Typesense server on port ${apiPort}\u2026
|
|
272
|
+
`);
|
|
273
|
+
const exitPromise = new Promise((_, reject) => {
|
|
274
|
+
this.child.once("exit", (code, signal) => {
|
|
275
|
+
this.clearState();
|
|
276
|
+
const out = startupLog.trim();
|
|
277
|
+
const detail = out ? `
|
|
278
|
+
${out}` : "";
|
|
279
|
+
reject(new Error(`Typesense process exited (code=${code}, signal=${signal})${detail}`));
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
await Promise.race([waitReady(apiPort, HEALTH_TIMEOUT_MS), exitPromise]);
|
|
283
|
+
this.child.stdout?.destroy();
|
|
284
|
+
this.child.stderr?.destroy();
|
|
285
|
+
process.stdout.write(` Typesense server ready.
|
|
286
|
+
`);
|
|
287
|
+
this.writeState({ pid: this.child.pid, apiPort, peeringPort });
|
|
288
|
+
this.client = new TypesenseClient(makeClientConfig(apiPort));
|
|
289
|
+
await this.ensureCollection();
|
|
290
|
+
this._available = true;
|
|
291
|
+
this._ownedProcess = true;
|
|
292
|
+
(0, import_errors.logDebug)("TypesenseIndex: ready", { storagePath: this.storagePath, port: apiPort, dim: this.dim });
|
|
293
|
+
} catch (err) {
|
|
294
|
+
const msg = String(err);
|
|
295
|
+
const log = (startupLog ?? "").trim();
|
|
296
|
+
const detail = log ? `
|
|
297
|
+
Server output:
|
|
298
|
+
${log.split("\n").map((l) => ` ${l}`).join("\n")}` : "";
|
|
299
|
+
process.stdout.write(` Typesense initialization failed: ${msg}${detail}
|
|
300
|
+
`);
|
|
301
|
+
(0, import_errors.logError)("TypesenseIndex: initialization failed", { error: msg });
|
|
302
|
+
this._failReason = msg;
|
|
303
|
+
try {
|
|
304
|
+
this.child?.kill();
|
|
305
|
+
} catch {
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
async ensureCollection() {
|
|
310
|
+
const exists = await this.client.collections(COLLECTION).exists();
|
|
311
|
+
if (!exists) {
|
|
312
|
+
await this.client.collections().create({
|
|
313
|
+
name: COLLECTION,
|
|
314
|
+
fields: [
|
|
315
|
+
{ name: "node_id", type: "string" },
|
|
316
|
+
{ name: "name", type: "string" },
|
|
317
|
+
{ name: "kind", type: "string", facet: true },
|
|
318
|
+
{ name: "file_path", type: "string" },
|
|
319
|
+
{ name: "signature", type: "string" },
|
|
320
|
+
{ name: "vector", type: "float[]", num_dim: this.dim }
|
|
321
|
+
]
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Insert or update a node's embedding. Typesense's upsert overwrites the
|
|
327
|
+
* document if the `id` already exists.
|
|
328
|
+
*/
|
|
329
|
+
async upsert(node, embedding) {
|
|
330
|
+
if (!this._available || !this.client) return;
|
|
331
|
+
try {
|
|
332
|
+
await this.client.collections(COLLECTION).documents().upsert({
|
|
333
|
+
id: toDocId(node.id),
|
|
334
|
+
node_id: node.id,
|
|
335
|
+
name: node.name,
|
|
336
|
+
kind: node.kind,
|
|
337
|
+
file_path: node.filePath,
|
|
338
|
+
signature: node.signature ?? "",
|
|
339
|
+
vector: Array.from(embedding)
|
|
340
|
+
});
|
|
341
|
+
} catch (err) {
|
|
342
|
+
(0, import_errors.logWarn)("TypesenseIndex: upsert failed", { nodeId: node.id, error: String(err) });
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Bulk upsert a batch of nodes. Uses Typesense's import endpoint (single
|
|
347
|
+
* HTTP request per batch) to avoid connection churn on large codebases.
|
|
348
|
+
*/
|
|
349
|
+
async bulkUpsert(nodes, embeddings) {
|
|
350
|
+
if (!this._available || !this.client || nodes.length === 0) return;
|
|
351
|
+
const docs = nodes.map((node, i) => ({
|
|
352
|
+
id: toDocId(node.id),
|
|
353
|
+
node_id: node.id,
|
|
354
|
+
name: node.name,
|
|
355
|
+
kind: node.kind,
|
|
356
|
+
file_path: node.filePath,
|
|
357
|
+
signature: node.signature ?? "",
|
|
358
|
+
vector: Array.from(embeddings[i])
|
|
359
|
+
}));
|
|
360
|
+
try {
|
|
361
|
+
await this.client.collections(COLLECTION).documents().import(docs, { action: "upsert" });
|
|
362
|
+
} catch (err) {
|
|
363
|
+
(0, import_errors.logWarn)("TypesenseIndex: bulkUpsert failed", { count: docs.length, error: String(err) });
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Remove a document from the collection.
|
|
368
|
+
*/
|
|
369
|
+
async delete(nodeId) {
|
|
370
|
+
if (!this._available || !this.client) return;
|
|
371
|
+
try {
|
|
372
|
+
await this.client.collections(COLLECTION).documents(toDocId(nodeId)).delete();
|
|
373
|
+
} catch (err) {
|
|
374
|
+
const msg = String(err);
|
|
375
|
+
if (!msg.includes("404") && !msg.includes("ObjectNotFound")) {
|
|
376
|
+
(0, import_errors.logWarn)("TypesenseIndex: delete failed", { nodeId, error: msg });
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* ANN vector search. Returns node IDs ordered by cosine similarity (descending).
|
|
382
|
+
*/
|
|
383
|
+
async search(queryVec, topN = 10) {
|
|
384
|
+
if (!this._available || !this.client) return [];
|
|
385
|
+
try {
|
|
386
|
+
const result = await this.client.multiSearch.perform(
|
|
387
|
+
{
|
|
388
|
+
searches: [{
|
|
389
|
+
collection: COLLECTION,
|
|
390
|
+
q: "*",
|
|
391
|
+
vector_query: `vector:([${Array.from(queryVec).join(",")}], k:${topN})`,
|
|
392
|
+
per_page: topN,
|
|
393
|
+
include_fields: "node_id"
|
|
394
|
+
}]
|
|
395
|
+
},
|
|
396
|
+
{}
|
|
397
|
+
);
|
|
398
|
+
const hits = result?.results?.[0]?.hits ?? [];
|
|
399
|
+
return hits.map((h) => h.document.node_id);
|
|
400
|
+
} catch (err) {
|
|
401
|
+
(0, import_errors.logWarn)("TypesenseIndex: search failed", { error: String(err) });
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Return all node IDs currently stored in the collection via paginated export.
|
|
407
|
+
*/
|
|
408
|
+
async getEmbeddedNodeIds() {
|
|
409
|
+
if (!this._available || !this.client) return [];
|
|
410
|
+
const ids = [];
|
|
411
|
+
try {
|
|
412
|
+
const jsonl = await this.client.collections(COLLECTION).documents().export({ include_fields: "node_id", batch_size: 1e3 });
|
|
413
|
+
for (const line of jsonl.split("\n")) {
|
|
414
|
+
if (!line.trim()) continue;
|
|
415
|
+
try {
|
|
416
|
+
const doc = JSON.parse(line);
|
|
417
|
+
if (doc.node_id) ids.push(doc.node_id);
|
|
418
|
+
} catch {
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
} catch {
|
|
422
|
+
return ids;
|
|
423
|
+
}
|
|
424
|
+
return ids;
|
|
425
|
+
}
|
|
426
|
+
/** Number of documents currently in the collection. */
|
|
427
|
+
async count() {
|
|
428
|
+
if (!this._available || !this.client) return 0;
|
|
429
|
+
try {
|
|
430
|
+
const result = await this.client.collections(COLLECTION).documents().search({
|
|
431
|
+
q: "*",
|
|
432
|
+
per_page: 0
|
|
433
|
+
});
|
|
434
|
+
return result.found ?? 0;
|
|
435
|
+
} catch {
|
|
436
|
+
return 0;
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Disconnect the client. The Typesense server keeps running as a background
|
|
441
|
+
* daemon so the next command can reconnect instantly via the state file.
|
|
442
|
+
*/
|
|
443
|
+
close() {
|
|
444
|
+
this._available = false;
|
|
445
|
+
this.child = null;
|
|
446
|
+
this.client = null;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
450
|
+
0 && (module.exports = {
|
|
451
|
+
TypesenseIndex
|
|
452
|
+
});
|
|
453
|
+
//# sourceMappingURL=typesense-index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/vectors/typesense-index.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * KiroGraph Typesense Index\n *\n * ANN vector search backed by Typesense running in embedded mode.\n * The engine downloads the Typesense binary on first use (cached at\n * ~/.kirograph/bin/), then spawns it as a managed child process persisting\n * data to .kirograph/typesense/.\n *\n * Opt-in: set config.semanticEngine = 'typesense'\n * Required optional dependency (not installed by default):\n * npm install typesense\n *\n * Key characteristics:\n * - HNSW ANN vector search with cosine distance\n * - Binary auto-downloaded on first use (~37MB, cached globally)\n * - No separate server setup required\n * - Async startup check via /health\n * - Node IDs used directly as Typesense document IDs (colon sanitised)\n */\n\nimport * as path from 'path';\nimport * as fs from 'fs';\nimport * as https from 'https';\nimport * as http from 'http';\nimport * as crypto from 'crypto';\nimport { homedir } from 'os';\nimport { logDebug, logWarn, logError } from '../errors';\nimport type { Node } from '../types';\nimport { extractBinaryFromTarGz } from '../bin/installer/archive';\n\n// \u2500\u2500 Constants \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst DEFAULT_DIM = 768;\nconst STORAGE_DIR = 'typesense';\nconst COLLECTION = 'kg_nodes';\nconst API_KEY = 'kirograph-local';\nconst TYPESENSE_VERSION = '28.0';\nconst BIN_CACHE_DIR = path.join(homedir(), '.kirograph', 'bin');\nconst HEALTH_TIMEOUT_MS = 60_000;\nconst HEALTH_POLL_MS = 200;\nconst SERVER_STATE_FILE = 'typesense-server.json';\n\ninterface ServerState { pid: number; apiPort: number; peeringPort: number; }\n\n// \u2500\u2500 Binary management \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction getBinaryUrl(): string | null {\n const v = TYPESENSE_VERSION;\n const { platform, arch } = process;\n\n if (platform === 'darwin') {\n const a = arch === 'arm64' ? 'darwin-arm64' : 'darwin-amd64';\n return `https://dl.typesense.org/releases/${v}/typesense-server-${v}-${a}.tar.gz`;\n }\n if (platform === 'linux') {\n const a = arch === 'arm64' ? 'linux-arm64' : 'linux-amd64';\n return `https://dl.typesense.org/releases/${v}/typesense-server-${v}-${a}.tar.gz`;\n }\n return null; // Windows not supported via auto-download\n}\n\nfunction getBinPath(): string {\n return path.join(BIN_CACHE_DIR, `typesense-server-${TYPESENSE_VERSION}`);\n}\n\n/**\n * Download the Typesense binary tarball and extract it using the shared archive utility.\n * The binary is cached at ~/.kirograph/bin/typesense-server-{version}.\n */\nasync function downloadBinary(url: string, destPath: string): Promise<void> {\n fs.mkdirSync(BIN_CACHE_DIR, { recursive: true });\n await extractBinaryFromTarGz(url, 'typesense-server', destPath);\n}\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** Sanitise node_id for use as a Typesense document ID (no colons allowed). */\nfunction toDocId(nodeId: string): string {\n return nodeId.replace(/:/g, '__');\n}\n\n/** Find two random free TCP ports. */\nfunction getFreePorts(count: number): Promise<number[]> {\n return new Promise((resolve, reject) => {\n const ports: number[] = [];\n const servers: ReturnType<typeof http.createServer>[] = [];\n let done = 0;\n for (let i = 0; i < count; i++) {\n const srv = http.createServer();\n servers.push(srv);\n srv.listen(0, '127.0.0.1', () => {\n ports.push((srv.address() as { port: number }).port);\n done++;\n if (done === count) {\n // Close all servers first, then resolve\n let closed = 0;\n for (const s of servers) s.close(() => { if (++closed === count) resolve(ports); });\n }\n });\n srv.on('error', reject);\n }\n });\n}\n\n/** Poll GET http://127.0.0.1:{port}/health until ok:true or timeout. */\nfunction waitReady(port: number, timeoutMs: number): Promise<void> {\n return new Promise((resolve, reject) => {\n const deadline = Date.now() + timeoutMs;\n\n function check() {\n const req = http.get(`http://127.0.0.1:${port}/health`, res => {\n let body = '';\n res.on('data', (c: string) => body += c);\n res.on('end', () => {\n try {\n if (JSON.parse(body).ok === true) return resolve();\n } catch { /* ignore */ }\n scheduleRetry();\n });\n });\n req.on('error', scheduleRetry);\n req.setTimeout(300, () => { req.destroy(); scheduleRetry(); });\n }\n\n function scheduleRetry() {\n if (Date.now() >= deadline) return reject(new Error('Typesense health timeout'));\n setTimeout(check, HEALTH_POLL_MS);\n }\n\n check();\n });\n}\n\n// \u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nconst _silentLogger = { log: () => {}, warn: () => {}, error: () => {}, debug: () => {}, info: () => {}, trace: () => {}, setLevel: () => {} };\n\nfunction makeClientConfig(port: number) {\n return {\n nodes: [{ host: '127.0.0.1', port, protocol: 'http' }],\n apiKey: API_KEY,\n connectionTimeoutSeconds: 10,\n retryIntervalSeconds: 0.1,\n numRetries: 3,\n logger: _silentLogger,\n logLevel: 'silent' as const,\n };\n}\n\n// \u2500\u2500 TypesenseIndex \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class TypesenseIndex {\n private client: any = null;\n private child: any = null;\n private _available = false;\n private _ownedProcess = false; // true only when we spawned the child ourselves\n private _failReason: string | null = null;\n private storagePath: string;\n private stateFile: string;\n\n constructor(\n private readonly kirographDir: string,\n private readonly dim = DEFAULT_DIM,\n ) {\n this.storagePath = path.join(kirographDir, STORAGE_DIR);\n this.stateFile = path.join(kirographDir, SERVER_STATE_FILE);\n }\n\n private readState(): ServerState | null {\n try {\n return JSON.parse(fs.readFileSync(this.stateFile, 'utf8')) as ServerState;\n } catch { return null; }\n }\n\n private writeState(state: ServerState): void {\n try { fs.writeFileSync(this.stateFile, JSON.stringify(state)); } catch { /* ignore */ }\n }\n\n private clearState(): void {\n try { fs.unlinkSync(this.stateFile); } catch { /* ignore */ }\n }\n\n private isProcessAlive(pid: number): boolean {\n try { process.kill(pid, 0); return true; } catch { return false; }\n }\n\n isAvailable(): boolean {\n return this._available;\n }\n\n getFailReason(): string | null {\n return this._failReason;\n }\n\n /**\n * Download the Typesense binary if needed, spawn it with a project-scoped\n * data directory, wait until /health responds, then ensure the collection\n * exists. Silent no-op when `typesense` npm package is not installed.\n */\n async initialize(): Promise<void> {\n if (this._available) return;\n\n // \u2500\u2500 1. Load typesense client \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let TypesenseClient: any;\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n TypesenseClient = require('typesense').Client;\n } catch {\n logDebug('TypesenseIndex: typesense not installed \u2014 Typesense engine unavailable');\n return;\n }\n\n // \u2500\u2500 2. Locate or download binary \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const binPath = getBinPath();\n if (!fs.existsSync(binPath)) {\n const url = getBinaryUrl();\n if (!url) {\n logDebug('TypesenseIndex: no Typesense binary available for this platform');\n this._failReason = 'Typesense binary not available for this platform';\n return;\n }\n process.stdout.write(` Downloading Typesense ${TYPESENSE_VERSION} binary (~37 MB, cached for future use)\u2026\\n`);\n try {\n await downloadBinary(url, binPath);\n process.stdout.write(` Typesense binary ready.\\n`);\n } catch (err) {\n process.stdout.write(` Typesense binary download failed: ${String(err)}\\n`);\n logError('TypesenseIndex: binary download failed', { error: String(err) });\n this._failReason = `binary download failed: ${String(err)}`;\n return;\n }\n }\n\n // \u2500\u2500 3. Try to reuse an already-running server \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n const saved = this.readState();\n if (saved) {\n const alive = this.isProcessAlive(saved.pid);\n if (alive) {\n try {\n await waitReady(saved.apiPort, 5_000);\n // Reconnect to the existing process\n this.client = new TypesenseClient(makeClientConfig(saved.apiPort));\n await this.ensureCollection();\n this._available = true;\n this._ownedProcess = false;\n logDebug('TypesenseIndex: reused running server', { pid: saved.pid, port: saved.apiPort });\n return;\n } catch {\n // Process alive but not healthy \u2014 kill it so we can acquire the lock\n logDebug('TypesenseIndex: stale process detected, killing', { pid: saved.pid });\n try { process.kill(saved.pid, 'SIGKILL'); } catch { /* ignore */ }\n // Wait briefly for the lock to be released\n await new Promise(r => setTimeout(r, 500));\n }\n }\n this.clearState();\n }\n\n // \u2500\u2500 4. Spawn a new Typesense process \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n let startupLog = '';\n try {\n fs.mkdirSync(this.storagePath, { recursive: true });\n\n // Remove stale RocksDB lock files that prevent a new process from starting\n for (const sub of ['db', 'meta']) {\n const lockFile = path.join(this.storagePath, sub, 'LOCK');\n try { fs.unlinkSync(lockFile); } catch { /* doesn't exist, fine */ }\n }\n\n const [apiPort, peeringPort] = await getFreePorts(2);\n\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { spawn } = require('child_process');\n this.child = spawn(binPath, [\n `--data-dir=${this.storagePath}`,\n `--api-key=${API_KEY}`,\n `--api-port=${apiPort}`,\n `--peering-port=${peeringPort}`,\n '--enable-cors',\n ], { stdio: ['ignore', 'pipe', 'pipe'] });\n\n const onLog = (chunk: Buffer) => { startupLog += chunk.toString('utf8'); };\n this.child.stdout?.on('data', onLog);\n this.child.stderr?.on('data', onLog);\n this.child.unref();\n\n // Only kill the server on user interruption, not on normal command exit\n // (we want the server to persist as a background daemon across commands)\n // \u2500\u2500 5. Wait until ready \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n process.stdout.write(` Starting Typesense server on port ${apiPort}\u2026\\n`);\n\n const exitPromise = new Promise<never>((_, reject) => {\n this.child.once('exit', (code: number | null, signal: string | null) => {\n this.clearState();\n const out = startupLog.trim();\n const detail = out ? `\\n${out}` : '';\n reject(new Error(`Typesense process exited (code=${code}, signal=${signal})${detail}`));\n });\n });\n\n await Promise.race([waitReady(apiPort, HEALTH_TIMEOUT_MS), exitPromise]);\n\n // Detach stdout/stderr so the pipe handles don't keep the parent event\n // loop alive after the command finishes.\n this.child.stdout?.destroy();\n this.child.stderr?.destroy();\n\n process.stdout.write(` Typesense server ready.\\n`);\n\n this.writeState({ pid: this.child.pid, apiPort, peeringPort });\n\n this.client = new TypesenseClient(makeClientConfig(apiPort));\n\n await this.ensureCollection();\n\n this._available = true;\n this._ownedProcess = true;\n logDebug('TypesenseIndex: ready', { storagePath: this.storagePath, port: apiPort, dim: this.dim });\n } catch (err) {\n const msg = String(err);\n const log = (startupLog ?? '').trim();\n const detail = log ? `\\n Server output:\\n${log.split('\\n').map(l => ` ${l}`).join('\\n')}` : '';\n process.stdout.write(` Typesense initialization failed: ${msg}${detail}\\n`);\n logError('TypesenseIndex: initialization failed', { error: msg });\n this._failReason = msg;\n try { this.child?.kill(); } catch { /* ignore */ }\n }\n }\n\n private async ensureCollection(): Promise<void> {\n const exists = await this.client.collections(COLLECTION).exists();\n if (!exists) {\n await this.client.collections().create({\n name: COLLECTION,\n fields: [\n { name: 'node_id', type: 'string' },\n { name: 'name', type: 'string' },\n { name: 'kind', type: 'string', facet: true },\n { name: 'file_path', type: 'string' },\n { name: 'signature', type: 'string' },\n { name: 'vector', type: 'float[]', num_dim: this.dim },\n ],\n });\n }\n }\n\n /**\n * Insert or update a node's embedding. Typesense's upsert overwrites the\n * document if the `id` already exists.\n */\n async upsert(node: Node, embedding: Float32Array): Promise<void> {\n if (!this._available || !this.client) return;\n\n try {\n await this.client.collections(COLLECTION).documents().upsert({\n id: toDocId(node.id),\n node_id: node.id,\n name: node.name,\n kind: node.kind,\n file_path: node.filePath,\n signature: node.signature ?? '',\n vector: Array.from(embedding),\n });\n } catch (err) {\n logWarn('TypesenseIndex: upsert failed', { nodeId: node.id, error: String(err) });\n }\n }\n\n /**\n * Bulk upsert a batch of nodes. Uses Typesense's import endpoint (single\n * HTTP request per batch) to avoid connection churn on large codebases.\n */\n async bulkUpsert(nodes: Node[], embeddings: Float32Array[]): Promise<void> {\n if (!this._available || !this.client || nodes.length === 0) return;\n\n const docs = nodes.map((node, i) => ({\n id: toDocId(node.id),\n node_id: node.id,\n name: node.name,\n kind: node.kind,\n file_path: node.filePath,\n signature: node.signature ?? '',\n vector: Array.from(embeddings[i]!),\n }));\n\n try {\n await this.client.collections(COLLECTION).documents().import(docs, { action: 'upsert' });\n } catch (err) {\n logWarn('TypesenseIndex: bulkUpsert failed', { count: docs.length, error: String(err) });\n }\n }\n\n /**\n * Remove a document from the collection.\n */\n async delete(nodeId: string): Promise<void> {\n if (!this._available || !this.client) return;\n\n try {\n await this.client.collections(COLLECTION).documents(toDocId(nodeId)).delete();\n } catch (err) {\n const msg = String(err);\n if (!msg.includes('404') && !msg.includes('ObjectNotFound')) {\n logWarn('TypesenseIndex: delete failed', { nodeId, error: msg });\n }\n }\n }\n\n /**\n * ANN vector search. Returns node IDs ordered by cosine similarity (descending).\n */\n async search(queryVec: Float32Array, topN = 10): Promise<string[]> {\n if (!this._available || !this.client) return [];\n\n try {\n // Use multiSearch (POST) to avoid the 4000-char GET query string limit\n // that would be exceeded by inlining 768-dimensional vectors.\n const result = await this.client.multiSearch.perform(\n {\n searches: [{\n collection: COLLECTION,\n q: '*',\n vector_query: `vector:([${Array.from(queryVec).join(',')}], k:${topN})`,\n per_page: topN,\n include_fields: 'node_id',\n }],\n },\n {},\n );\n const hits = result?.results?.[0]?.hits ?? [];\n return hits.map((h: any) => h.document.node_id as string);\n } catch (err) {\n logWarn('TypesenseIndex: search failed', { error: String(err) });\n return [];\n }\n }\n\n /**\n * Return all node IDs currently stored in the collection via paginated export.\n */\n async getEmbeddedNodeIds(): Promise<string[]> {\n if (!this._available || !this.client) return [];\n\n const ids: string[] = [];\n try {\n const jsonl: string = await this.client\n .collections(COLLECTION)\n .documents()\n .export({ include_fields: 'node_id', batch_size: 1000 });\n\n for (const line of jsonl.split('\\n')) {\n if (!line.trim()) continue;\n try {\n const doc = JSON.parse(line);\n if (doc.node_id) ids.push(doc.node_id as string);\n } catch { /* skip malformed line */ }\n }\n } catch {\n return ids;\n }\n\n return ids;\n }\n\n /** Number of documents currently in the collection. */\n async count(): Promise<number> {\n if (!this._available || !this.client) return 0;\n try {\n const result = await this.client.collections(COLLECTION).documents().search({\n q: '*', per_page: 0,\n });\n return result.found ?? 0;\n } catch {\n return 0;\n }\n }\n\n /**\n * Disconnect the client. The Typesense server keeps running as a background\n * daemon so the next command can reconnect instantly via the state file.\n */\n close(): void {\n this._available = false;\n this.child = null;\n this.client = null;\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|