codevault 1.8.4 → 1.8.5
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 -2
- package/dist/chunking/token-counter.d.ts.map +1 -1
- package/dist/chunking/token-counter.js +16 -10
- package/dist/chunking/token-counter.js.map +1 -1
- package/dist/cli/commands/index-cmd.d.ts.map +1 -1
- package/dist/cli/commands/index-cmd.js +108 -97
- package/dist/cli/commands/index-cmd.js.map +1 -1
- package/dist/cli/commands/interactive-config.d.ts.map +1 -1
- package/dist/cli/commands/interactive-config.js +40 -3
- package/dist/cli/commands/interactive-config.js.map +1 -1
- package/dist/cli/commands/search-cmd.d.ts.map +1 -1
- package/dist/cli/commands/search-cmd.js +11 -7
- package/dist/cli/commands/search-cmd.js.map +1 -1
- package/dist/cli/commands/search-with-code-cmd.d.ts.map +1 -1
- package/dist/cli/commands/search-with-code-cmd.js +3 -1
- package/dist/cli/commands/search-with-code-cmd.js.map +1 -1
- package/dist/cli/utils.d.ts +56 -0
- package/dist/cli/utils.d.ts.map +1 -0
- package/dist/cli/utils.js +98 -0
- package/dist/cli/utils.js.map +1 -0
- package/dist/config/constants.d.ts +4 -0
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/constants.js +2 -0
- package/dist/config/constants.js.map +1 -1
- package/dist/context/packs.d.ts.map +1 -1
- package/dist/context/packs.js +3 -1
- package/dist/context/packs.js.map +1 -1
- package/dist/core/IndexerEngine.d.ts +2 -0
- package/dist/core/IndexerEngine.d.ts.map +1 -1
- package/dist/core/IndexerEngine.js +34 -26
- package/dist/core/IndexerEngine.js.map +1 -1
- package/dist/core/SearchService.d.ts +1 -0
- package/dist/core/SearchService.d.ts.map +1 -1
- package/dist/core/SearchService.js +18 -12
- package/dist/core/SearchService.js.map +1 -1
- package/dist/core/batch-indexer.d.ts.map +1 -1
- package/dist/core/batch-indexer.js +5 -13
- package/dist/core/batch-indexer.js.map +1 -1
- package/dist/core/indexing/IndexFinalizationStage.d.ts.map +1 -1
- package/dist/core/indexing/IndexFinalizationStage.js +21 -2
- package/dist/core/indexing/IndexFinalizationStage.js.map +1 -1
- package/dist/core/indexing/IndexState.d.ts +3 -8
- package/dist/core/indexing/IndexState.d.ts.map +1 -1
- package/dist/core/indexing/IndexState.js.map +1 -1
- package/dist/core/indexing/PersistManager.d.ts +1 -1
- package/dist/core/indexing/PersistManager.d.ts.map +1 -1
- package/dist/core/indexing/PersistManager.js +17 -17
- package/dist/core/indexing/PersistManager.js.map +1 -1
- package/dist/core/search/HybridFusion.d.ts +0 -1
- package/dist/core/search/HybridFusion.d.ts.map +1 -1
- package/dist/core/search/HybridFusion.js +2 -14
- package/dist/core/search/HybridFusion.js.map +1 -1
- package/dist/core/search/ResultMapper.d.ts +0 -1
- package/dist/core/search/ResultMapper.d.ts.map +1 -1
- package/dist/core/search/ResultMapper.js +2 -13
- package/dist/core/search/ResultMapper.js.map +1 -1
- package/dist/core/search/SearchContextManager.d.ts +3 -0
- package/dist/core/search/SearchContextManager.d.ts.map +1 -1
- package/dist/core/search/SearchContextManager.js +15 -2
- package/dist/core/search/SearchContextManager.js.map +1 -1
- package/dist/core/search.d.ts.map +1 -1
- package/dist/core/search.js +9 -4
- package/dist/core/search.js.map +1 -1
- package/dist/languages/rules.d.ts.map +1 -1
- package/dist/languages/rules.js +14 -5
- package/dist/languages/rules.js.map +1 -1
- package/dist/mcp/schemas.d.ts +2 -2
- package/dist/mcp-server.d.ts +1 -0
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +32 -21
- package/dist/mcp-server.js.map +1 -1
- package/dist/providers/base.d.ts +3 -2
- package/dist/providers/base.d.ts.map +1 -1
- package/dist/providers/base.js +3 -1
- package/dist/providers/base.js.map +1 -1
- package/dist/providers/chat-llm.d.ts +2 -2
- package/dist/providers/chat-llm.d.ts.map +1 -1
- package/dist/providers/chat-llm.js +4 -1
- package/dist/providers/chat-llm.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +4 -1
- package/dist/providers/openai.js.map +1 -1
- package/dist/ranking/api-reranker.d.ts.map +1 -1
- package/dist/ranking/api-reranker.js +27 -8
- package/dist/ranking/api-reranker.js.map +1 -1
- package/dist/ranking/symbol-boost.d.ts.map +1 -1
- package/dist/ranking/symbol-boost.js +4 -11
- package/dist/ranking/symbol-boost.js.map +1 -1
- package/dist/search/bm25.d.ts +10 -0
- package/dist/search/bm25.d.ts.map +1 -1
- package/dist/search/bm25.js +16 -0
- package/dist/search/bm25.js.map +1 -1
- package/dist/storage/encrypted-chunks.d.ts +3 -0
- package/dist/storage/encrypted-chunks.d.ts.map +1 -1
- package/dist/storage/encrypted-chunks.js +108 -16
- package/dist/storage/encrypted-chunks.js.map +1 -1
- package/dist/synthesis/conversational-synthesizer.d.ts +2 -1
- package/dist/synthesis/conversational-synthesizer.d.ts.map +1 -1
- package/dist/synthesis/conversational-synthesizer.js +6 -1
- package/dist/synthesis/conversational-synthesizer.js.map +1 -1
- package/dist/synthesis/prompt-builder.d.ts.map +1 -1
- package/dist/synthesis/prompt-builder.js +40 -13
- package/dist/synthesis/prompt-builder.js.map +1 -1
- package/dist/synthesis/synthesizer.d.ts.map +1 -1
- package/dist/synthesis/synthesizer.js +14 -2
- package/dist/synthesis/synthesizer.js.map +1 -1
- package/dist/tests/api-reranker.test.d.ts +2 -0
- package/dist/tests/api-reranker.test.d.ts.map +1 -0
- package/dist/tests/api-reranker.test.js +575 -0
- package/dist/tests/api-reranker.test.js.map +1 -0
- package/dist/tests/bm25.test.d.ts +2 -0
- package/dist/tests/bm25.test.d.ts.map +1 -0
- package/dist/tests/bm25.test.js +340 -0
- package/dist/tests/bm25.test.js.map +1 -0
- package/dist/tests/chunking/file-grouper.test.d.ts +2 -0
- package/dist/tests/chunking/file-grouper.test.d.ts.map +1 -0
- package/dist/tests/chunking/file-grouper.test.js +495 -0
- package/dist/tests/chunking/file-grouper.test.js.map +1 -0
- package/dist/tests/chunking/semantic-chunker.test.d.ts +2 -0
- package/dist/tests/chunking/semantic-chunker.test.d.ts.map +1 -0
- package/dist/tests/chunking/semantic-chunker.test.js +509 -0
- package/dist/tests/chunking/semantic-chunker.test.js.map +1 -0
- package/dist/tests/chunking/token-counter.test.d.ts +2 -0
- package/dist/tests/chunking/token-counter.test.d.ts.map +1 -0
- package/dist/tests/chunking/token-counter.test.js +441 -0
- package/dist/tests/chunking/token-counter.test.js.map +1 -0
- package/dist/tests/cli/ask-cmd.test.d.ts +2 -0
- package/dist/tests/cli/ask-cmd.test.d.ts.map +1 -0
- package/dist/tests/cli/ask-cmd.test.js +152 -0
- package/dist/tests/cli/ask-cmd.test.js.map +1 -0
- package/dist/tests/cli/chat-cmd.test.d.ts +2 -0
- package/dist/tests/cli/chat-cmd.test.d.ts.map +1 -0
- package/dist/tests/cli/chat-cmd.test.js +118 -0
- package/dist/tests/cli/chat-cmd.test.js.map +1 -0
- package/dist/tests/cli/config-cmd.test.d.ts +2 -0
- package/dist/tests/cli/config-cmd.test.d.ts.map +1 -0
- package/dist/tests/cli/config-cmd.test.js +226 -0
- package/dist/tests/cli/config-cmd.test.js.map +1 -0
- package/dist/tests/cli/context.test.d.ts +2 -0
- package/dist/tests/cli/context.test.d.ts.map +1 -0
- package/dist/tests/cli/context.test.js +158 -0
- package/dist/tests/cli/context.test.js.map +1 -0
- package/dist/tests/cli/index-cmd.test.d.ts +2 -0
- package/dist/tests/cli/index-cmd.test.d.ts.map +1 -0
- package/dist/tests/cli/index-cmd.test.js +89 -0
- package/dist/tests/cli/index-cmd.test.js.map +1 -0
- package/dist/tests/cli/index.test.d.ts +2 -0
- package/dist/tests/cli/index.test.d.ts.map +1 -0
- package/dist/tests/cli/index.test.js +167 -0
- package/dist/tests/cli/index.test.js.map +1 -0
- package/dist/tests/cli/info-cmd.test.d.ts +2 -0
- package/dist/tests/cli/info-cmd.test.d.ts.map +1 -0
- package/dist/tests/cli/info-cmd.test.js +47 -0
- package/dist/tests/cli/info-cmd.test.js.map +1 -0
- package/dist/tests/cli/interactive-config.test.d.ts +2 -0
- package/dist/tests/cli/interactive-config.test.d.ts.map +1 -0
- package/dist/tests/cli/interactive-config.test.js +30 -0
- package/dist/tests/cli/interactive-config.test.js.map +1 -0
- package/dist/tests/cli/mcp-cmd.test.d.ts +2 -0
- package/dist/tests/cli/mcp-cmd.test.d.ts.map +1 -0
- package/dist/tests/cli/mcp-cmd.test.js +47 -0
- package/dist/tests/cli/mcp-cmd.test.js.map +1 -0
- package/dist/tests/cli/search-cmd.test.d.ts +2 -0
- package/dist/tests/cli/search-cmd.test.d.ts.map +1 -0
- package/dist/tests/cli/search-cmd.test.js +120 -0
- package/dist/tests/cli/search-cmd.test.js.map +1 -0
- package/dist/tests/cli/search-with-code-cmd.test.d.ts +2 -0
- package/dist/tests/cli/search-with-code-cmd.test.d.ts.map +1 -0
- package/dist/tests/cli/search-with-code-cmd.test.js +140 -0
- package/dist/tests/cli/search-with-code-cmd.test.js.map +1 -0
- package/dist/tests/cli/update-cmd.test.d.ts +2 -0
- package/dist/tests/cli/update-cmd.test.d.ts.map +1 -0
- package/dist/tests/cli/update-cmd.test.js +75 -0
- package/dist/tests/cli/update-cmd.test.js.map +1 -0
- package/dist/tests/cli/utils.test.d.ts +2 -0
- package/dist/tests/cli/utils.test.d.ts.map +1 -0
- package/dist/tests/cli/utils.test.js +119 -0
- package/dist/tests/cli/utils.test.js.map +1 -0
- package/dist/tests/cli/watch-cmd.test.d.ts +2 -0
- package/dist/tests/cli/watch-cmd.test.d.ts.map +1 -0
- package/dist/tests/cli/watch-cmd.test.js +84 -0
- package/dist/tests/cli/watch-cmd.test.js.map +1 -0
- package/dist/tests/cli-ui.test.d.ts +2 -0
- package/dist/tests/cli-ui.test.d.ts.map +1 -0
- package/dist/tests/cli-ui.test.js +608 -0
- package/dist/tests/cli-ui.test.js.map +1 -0
- package/dist/tests/codemap-io.test.d.ts +2 -0
- package/dist/tests/codemap-io.test.d.ts.map +1 -0
- package/dist/tests/codemap-io.test.js +992 -0
- package/dist/tests/codemap-io.test.js.map +1 -0
- package/dist/tests/config/apply-env.test.d.ts +2 -0
- package/dist/tests/config/apply-env.test.d.ts.map +1 -0
- package/dist/tests/config/apply-env.test.js +717 -0
- package/dist/tests/config/apply-env.test.js.map +1 -0
- package/dist/tests/config/constants.test.d.ts +2 -0
- package/dist/tests/config/constants.test.d.ts.map +1 -0
- package/dist/tests/config/constants.test.js +406 -0
- package/dist/tests/config/constants.test.js.map +1 -0
- package/dist/tests/config/loader.test.d.ts +2 -0
- package/dist/tests/config/loader.test.d.ts.map +1 -0
- package/dist/tests/config/loader.test.js +716 -0
- package/dist/tests/config/loader.test.js.map +1 -0
- package/dist/tests/config/resolver.test.d.ts +2 -0
- package/dist/tests/config/resolver.test.d.ts.map +1 -0
- package/dist/tests/config/resolver.test.js +402 -0
- package/dist/tests/config/resolver.test.js.map +1 -0
- package/dist/tests/config/types.test.d.ts +2 -0
- package/dist/tests/config/types.test.d.ts.map +1 -0
- package/dist/tests/config/types.test.js +460 -0
- package/dist/tests/config/types.test.js.map +1 -0
- package/dist/tests/context-packs.test.d.ts +2 -0
- package/dist/tests/context-packs.test.d.ts.map +1 -0
- package/dist/tests/context-packs.test.js +826 -0
- package/dist/tests/context-packs.test.js.map +1 -0
- package/dist/tests/conversational-synthesizer.test.d.ts +2 -0
- package/dist/tests/conversational-synthesizer.test.d.ts.map +1 -0
- package/dist/tests/conversational-synthesizer.test.js +595 -0
- package/dist/tests/conversational-synthesizer.test.js.map +1 -0
- package/dist/tests/database.test.d.ts +2 -0
- package/dist/tests/database.test.d.ts.map +1 -0
- package/dist/tests/database.test.js +965 -0
- package/dist/tests/database.test.js.map +1 -0
- package/dist/tests/encrypted-chunks.test.d.ts +2 -0
- package/dist/tests/encrypted-chunks.test.d.ts.map +1 -0
- package/dist/tests/encrypted-chunks.test.js +1470 -0
- package/dist/tests/encrypted-chunks.test.js.map +1 -0
- package/dist/tests/hybrid.test.d.ts +2 -0
- package/dist/tests/hybrid.test.d.ts.map +1 -0
- package/dist/tests/hybrid.test.js +456 -0
- package/dist/tests/hybrid.test.js.map +1 -0
- package/dist/tests/indexer/ChangeQueue.test.d.ts +12 -0
- package/dist/tests/indexer/ChangeQueue.test.d.ts.map +1 -0
- package/dist/tests/indexer/ChangeQueue.test.js +441 -0
- package/dist/tests/indexer/ChangeQueue.test.js.map +1 -0
- package/dist/tests/indexer/ProviderManager.test.d.ts +12 -0
- package/dist/tests/indexer/ProviderManager.test.d.ts.map +1 -0
- package/dist/tests/indexer/ProviderManager.test.js +290 -0
- package/dist/tests/indexer/ProviderManager.test.js.map +1 -0
- package/dist/tests/indexer/WatchService.test.d.ts +14 -0
- package/dist/tests/indexer/WatchService.test.d.ts.map +1 -0
- package/dist/tests/indexer/WatchService.test.js +667 -0
- package/dist/tests/indexer/WatchService.test.js.map +1 -0
- package/dist/tests/indexer/merkle.test.d.ts +11 -0
- package/dist/tests/indexer/merkle.test.d.ts.map +1 -0
- package/dist/tests/indexer/merkle.test.js +497 -0
- package/dist/tests/indexer/merkle.test.js.map +1 -0
- package/dist/tests/indexer/update.test.d.ts +10 -0
- package/dist/tests/indexer/update.test.d.ts.map +1 -0
- package/dist/tests/indexer/update.test.js +317 -0
- package/dist/tests/indexer/update.test.js.map +1 -0
- package/dist/tests/indexer/watch.test.d.ts +8 -0
- package/dist/tests/indexer/watch.test.d.ts.map +1 -0
- package/dist/tests/indexer/watch.test.js +95 -0
- package/dist/tests/indexer/watch.test.js.map +1 -0
- package/dist/tests/integration/index-search.integration.test.js +1 -0
- package/dist/tests/integration/index-search.integration.test.js.map +1 -1
- package/dist/tests/languages.test.d.ts +2 -0
- package/dist/tests/languages.test.d.ts.map +1 -0
- package/dist/tests/languages.test.js +575 -0
- package/dist/tests/languages.test.js.map +1 -0
- package/dist/tests/logger-redaction.test.d.ts +2 -0
- package/dist/tests/logger-redaction.test.d.ts.map +1 -0
- package/dist/tests/logger-redaction.test.js +48 -0
- package/dist/tests/logger-redaction.test.js.map +1 -0
- package/dist/tests/logger.test.d.ts +2 -0
- package/dist/tests/logger.test.d.ts.map +1 -0
- package/dist/tests/logger.test.js +468 -0
- package/dist/tests/logger.test.js.map +1 -0
- package/dist/tests/markdown-formatter.test.d.ts +2 -0
- package/dist/tests/markdown-formatter.test.d.ts.map +1 -0
- package/dist/tests/markdown-formatter.test.js +453 -0
- package/dist/tests/markdown-formatter.test.js.map +1 -0
- package/dist/tests/mcp/tools/use-context-pack.test.d.ts +7 -0
- package/dist/tests/mcp/tools/use-context-pack.test.d.ts.map +1 -0
- package/dist/tests/mcp/tools/use-context-pack.test.js +505 -0
- package/dist/tests/mcp/tools/use-context-pack.test.js.map +1 -0
- package/dist/tests/mutex.test.d.ts +2 -0
- package/dist/tests/mutex.test.d.ts.map +1 -0
- package/dist/tests/mutex.test.js +489 -0
- package/dist/tests/mutex.test.js.map +1 -0
- package/dist/tests/path-helpers.test.d.ts +2 -0
- package/dist/tests/path-helpers.test.d.ts.map +1 -0
- package/dist/tests/path-helpers.test.js +332 -0
- package/dist/tests/path-helpers.test.js.map +1 -0
- package/dist/tests/prompt-builder.test.d.ts +2 -0
- package/dist/tests/prompt-builder.test.d.ts.map +1 -0
- package/dist/tests/prompt-builder.test.js +417 -0
- package/dist/tests/prompt-builder.test.js.map +1 -0
- package/dist/tests/providers/base.test.d.ts +2 -0
- package/dist/tests/providers/base.test.d.ts.map +1 -0
- package/dist/tests/providers/base.test.js +299 -0
- package/dist/tests/providers/base.test.js.map +1 -0
- package/dist/tests/providers/chat-llm.test.d.ts +2 -0
- package/dist/tests/providers/chat-llm.test.d.ts.map +1 -0
- package/dist/tests/providers/chat-llm.test.js +435 -0
- package/dist/tests/providers/chat-llm.test.js.map +1 -0
- package/dist/tests/providers/index.test.d.ts +2 -0
- package/dist/tests/providers/index.test.d.ts.map +1 -0
- package/dist/tests/providers/index.test.js +204 -0
- package/dist/tests/providers/index.test.js.map +1 -0
- package/dist/tests/providers/mock.test.d.ts +2 -0
- package/dist/tests/providers/mock.test.d.ts.map +1 -0
- package/dist/tests/providers/mock.test.js +225 -0
- package/dist/tests/providers/mock.test.js.map +1 -0
- package/dist/tests/providers/openai.test.d.ts +2 -0
- package/dist/tests/providers/openai.test.d.ts.map +1 -0
- package/dist/tests/providers/openai.test.js +408 -0
- package/dist/tests/providers/openai.test.js.map +1 -0
- package/dist/tests/providers/token-counter.test.d.ts +2 -0
- package/dist/tests/providers/token-counter.test.d.ts.map +1 -0
- package/dist/tests/providers/token-counter.test.js +247 -0
- package/dist/tests/providers/token-counter.test.js.map +1 -0
- package/dist/tests/rate-limiter.test.js +392 -1
- package/dist/tests/rate-limiter.test.js.map +1 -1
- package/dist/tests/scope.test.d.ts +2 -0
- package/dist/tests/scope.test.d.ts.map +1 -0
- package/dist/tests/scope.test.js +529 -0
- package/dist/tests/scope.test.js.map +1 -0
- package/dist/tests/simple-lru.test.js +377 -0
- package/dist/tests/simple-lru.test.js.map +1 -1
- package/dist/tests/symbol-boost.test.js +730 -10
- package/dist/tests/symbol-boost.test.js.map +1 -1
- package/dist/tests/symbols-extract.test.d.ts +2 -0
- package/dist/tests/symbols-extract.test.d.ts.map +1 -0
- package/dist/tests/symbols-extract.test.js +536 -0
- package/dist/tests/symbols-extract.test.js.map +1 -0
- package/dist/tests/symbols-graph.test.d.ts +2 -0
- package/dist/tests/symbols-graph.test.d.ts.map +1 -0
- package/dist/tests/symbols-graph.test.js +656 -0
- package/dist/tests/symbols-graph.test.js.map +1 -0
- package/dist/tests/synthesizer.test.d.ts +2 -0
- package/dist/tests/synthesizer.test.d.ts.map +1 -0
- package/dist/tests/synthesizer.test.js +381 -0
- package/dist/tests/synthesizer.test.js.map +1 -0
- package/dist/types/context-pack.d.ts +3 -3
- package/dist/utils/logger.d.ts +5 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/logger.js +149 -4
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/mutex.d.ts +7 -2
- package/dist/utils/mutex.d.ts.map +1 -1
- package/dist/utils/mutex.js +31 -7
- package/dist/utils/mutex.js.map +1 -1
- package/dist/utils/path-helpers.d.ts.map +1 -1
- package/dist/utils/path-helpers.js +5 -2
- package/dist/utils/path-helpers.js.map +1 -1
- package/dist/utils/simple-lru.d.ts +6 -0
- package/dist/utils/simple-lru.d.ts.map +1 -1
- package/dist/utils/simple-lru.js +26 -0
- package/dist/utils/simple-lru.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,716 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import os from 'os';
|
|
6
|
+
import { readGlobalConfig, readProjectConfig, readEnvConfig, loadConfig, getConfigSources, saveGlobalConfig, saveProjectConfig, hasGlobalConfig, hasProjectConfig, getGlobalConfigPath, getProjectConfigPath, } from '../../config/loader.js';
|
|
7
|
+
/**
|
|
8
|
+
* Helper to create a temporary directory for tests
|
|
9
|
+
*/
|
|
10
|
+
function createTempDir() {
|
|
11
|
+
return fs.mkdtempSync(path.join(os.tmpdir(), 'codevault-test-'));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Helper to clean up temporary directory
|
|
15
|
+
*/
|
|
16
|
+
function cleanupTempDir(dir) {
|
|
17
|
+
if (fs.existsSync(dir)) {
|
|
18
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Helper to save original env and restore later
|
|
23
|
+
*/
|
|
24
|
+
function saveEnv() {
|
|
25
|
+
const envVars = [
|
|
26
|
+
'CODEVAULT_EMBEDDING_API_KEY',
|
|
27
|
+
'OPENAI_API_KEY',
|
|
28
|
+
'CODEVAULT_EMBEDDING_BASE_URL',
|
|
29
|
+
'OPENAI_BASE_URL',
|
|
30
|
+
'CODEVAULT_EMBEDDING_MODEL',
|
|
31
|
+
'CODEVAULT_OPENAI_EMBEDDING_MODEL',
|
|
32
|
+
'OPENAI_MODEL',
|
|
33
|
+
'CODEVAULT_EMBEDDING_DIMENSIONS',
|
|
34
|
+
'CODEVAULT_DIMENSIONS',
|
|
35
|
+
'CODEVAULT_EMBEDDING_MAX_TOKENS',
|
|
36
|
+
'CODEVAULT_MAX_TOKENS',
|
|
37
|
+
'CODEVAULT_EMBEDDING_RATE_LIMIT_RPM',
|
|
38
|
+
'CODEVAULT_RATE_LIMIT_RPM',
|
|
39
|
+
'CODEVAULT_RATE_LIMIT',
|
|
40
|
+
'CODEVAULT_EMBEDDING_RATE_LIMIT_TPM',
|
|
41
|
+
'CODEVAULT_RATE_LIMIT_TPM',
|
|
42
|
+
'CODEVAULT_ENCRYPTION_KEY',
|
|
43
|
+
'CODEVAULT_RERANK_API_URL',
|
|
44
|
+
'CODEVAULT_RERANK_API_KEY',
|
|
45
|
+
'CODEVAULT_RERANK_MODEL',
|
|
46
|
+
'CODEVAULT_CHAT_API_KEY',
|
|
47
|
+
'CODEVAULT_CHAT_BASE_URL',
|
|
48
|
+
'CODEVAULT_CHAT_MODEL',
|
|
49
|
+
'CODEVAULT_OPENAI_CHAT_MODEL',
|
|
50
|
+
'CODEVAULT_CHAT_MAX_TOKENS',
|
|
51
|
+
'CODEVAULT_CHAT_TEMPERATURE',
|
|
52
|
+
];
|
|
53
|
+
const saved = {};
|
|
54
|
+
for (const key of envVars) {
|
|
55
|
+
saved[key] = process.env[key];
|
|
56
|
+
}
|
|
57
|
+
return saved;
|
|
58
|
+
}
|
|
59
|
+
function restoreEnv(saved) {
|
|
60
|
+
for (const [key, value] of Object.entries(saved)) {
|
|
61
|
+
if (value === undefined) {
|
|
62
|
+
delete process.env[key];
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
process.env[key] = value;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function clearEnv(keys) {
|
|
70
|
+
for (const key of keys) {
|
|
71
|
+
delete process.env[key];
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// =============================================================================
|
|
75
|
+
// readGlobalConfig tests
|
|
76
|
+
// =============================================================================
|
|
77
|
+
test('readGlobalConfig returns null when file does not exist', () => {
|
|
78
|
+
// This test relies on the actual global config path
|
|
79
|
+
// We cannot easily mock fs.existsSync in Node.js test runner
|
|
80
|
+
// So we verify the function returns a config or null without error
|
|
81
|
+
const result = readGlobalConfig();
|
|
82
|
+
assert.ok(result === null || typeof result === 'object', 'should return null or config object');
|
|
83
|
+
});
|
|
84
|
+
// =============================================================================
|
|
85
|
+
// readProjectConfig tests
|
|
86
|
+
// =============================================================================
|
|
87
|
+
test('readProjectConfig returns null when config file does not exist', () => {
|
|
88
|
+
const tempDir = createTempDir();
|
|
89
|
+
try {
|
|
90
|
+
const result = readProjectConfig(tempDir);
|
|
91
|
+
assert.equal(result, null, 'should return null for non-existent config');
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
cleanupTempDir(tempDir);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
test('readProjectConfig reads valid JSON config', () => {
|
|
98
|
+
const tempDir = createTempDir();
|
|
99
|
+
const configDir = path.join(tempDir, '.codevault');
|
|
100
|
+
const configPath = path.join(configDir, 'config.json');
|
|
101
|
+
try {
|
|
102
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
103
|
+
const testConfig = {
|
|
104
|
+
defaultProvider: 'openai',
|
|
105
|
+
maxTokens: 4096,
|
|
106
|
+
providers: {
|
|
107
|
+
openai: {
|
|
108
|
+
apiKey: 'test-key-123',
|
|
109
|
+
model: 'text-embedding-3-small'
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
fs.writeFileSync(configPath, JSON.stringify(testConfig, null, 2));
|
|
114
|
+
const result = readProjectConfig(tempDir);
|
|
115
|
+
assert.ok(result !== null, 'should return config object');
|
|
116
|
+
assert.equal(result?.defaultProvider, 'openai');
|
|
117
|
+
assert.equal(result?.maxTokens, 4096);
|
|
118
|
+
assert.equal(result?.providers?.openai?.apiKey, 'test-key-123');
|
|
119
|
+
assert.equal(result?.providers?.openai?.model, 'text-embedding-3-small');
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
cleanupTempDir(tempDir);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
test('readProjectConfig returns null for invalid JSON', () => {
|
|
126
|
+
const tempDir = createTempDir();
|
|
127
|
+
const configDir = path.join(tempDir, '.codevault');
|
|
128
|
+
const configPath = path.join(configDir, 'config.json');
|
|
129
|
+
try {
|
|
130
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
131
|
+
fs.writeFileSync(configPath, 'invalid json {{{');
|
|
132
|
+
const result = readProjectConfig(tempDir);
|
|
133
|
+
assert.equal(result, null, 'should return null for invalid JSON');
|
|
134
|
+
}
|
|
135
|
+
finally {
|
|
136
|
+
cleanupTempDir(tempDir);
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
test('readProjectConfig resolves relative basePath', () => {
|
|
140
|
+
const tempDir = createTempDir();
|
|
141
|
+
const configDir = path.join(tempDir, '.codevault');
|
|
142
|
+
const configPath = path.join(configDir, 'config.json');
|
|
143
|
+
try {
|
|
144
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
145
|
+
fs.writeFileSync(configPath, JSON.stringify({ maxTokens: 1000 }));
|
|
146
|
+
// Change to temp dir to test relative path resolution
|
|
147
|
+
const originalCwd = process.cwd();
|
|
148
|
+
process.chdir(tempDir);
|
|
149
|
+
try {
|
|
150
|
+
const result = readProjectConfig('.');
|
|
151
|
+
assert.ok(result !== null, 'should read config with relative path');
|
|
152
|
+
assert.equal(result?.maxTokens, 1000);
|
|
153
|
+
}
|
|
154
|
+
finally {
|
|
155
|
+
process.chdir(originalCwd);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
cleanupTempDir(tempDir);
|
|
160
|
+
}
|
|
161
|
+
});
|
|
162
|
+
// =============================================================================
|
|
163
|
+
// readEnvConfig tests
|
|
164
|
+
// =============================================================================
|
|
165
|
+
test('readEnvConfig returns empty object when no env vars set', () => {
|
|
166
|
+
const saved = saveEnv();
|
|
167
|
+
clearEnv(Object.keys(saved));
|
|
168
|
+
try {
|
|
169
|
+
const result = readEnvConfig();
|
|
170
|
+
assert.ok(typeof result === 'object', 'should return an object');
|
|
171
|
+
// Should not have providers if no env vars are set
|
|
172
|
+
assert.equal(result.providers, undefined, 'should not have providers');
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
restoreEnv(saved);
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
test('readEnvConfig reads CODEVAULT_EMBEDDING_API_KEY', () => {
|
|
179
|
+
const saved = saveEnv();
|
|
180
|
+
clearEnv(Object.keys(saved));
|
|
181
|
+
try {
|
|
182
|
+
process.env.CODEVAULT_EMBEDDING_API_KEY = 'test-api-key';
|
|
183
|
+
const result = readEnvConfig();
|
|
184
|
+
assert.equal(result.providers?.openai?.apiKey, 'test-api-key');
|
|
185
|
+
}
|
|
186
|
+
finally {
|
|
187
|
+
restoreEnv(saved);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
test('readEnvConfig falls back to OPENAI_API_KEY', () => {
|
|
191
|
+
const saved = saveEnv();
|
|
192
|
+
clearEnv(Object.keys(saved));
|
|
193
|
+
try {
|
|
194
|
+
process.env.OPENAI_API_KEY = 'openai-fallback-key';
|
|
195
|
+
const result = readEnvConfig();
|
|
196
|
+
assert.equal(result.providers?.openai?.apiKey, 'openai-fallback-key');
|
|
197
|
+
}
|
|
198
|
+
finally {
|
|
199
|
+
restoreEnv(saved);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
test('readEnvConfig prefers CODEVAULT_EMBEDDING_API_KEY over OPENAI_API_KEY', () => {
|
|
203
|
+
const saved = saveEnv();
|
|
204
|
+
clearEnv(Object.keys(saved));
|
|
205
|
+
try {
|
|
206
|
+
process.env.CODEVAULT_EMBEDDING_API_KEY = 'codevault-key';
|
|
207
|
+
process.env.OPENAI_API_KEY = 'openai-key';
|
|
208
|
+
const result = readEnvConfig();
|
|
209
|
+
assert.equal(result.providers?.openai?.apiKey, 'codevault-key');
|
|
210
|
+
}
|
|
211
|
+
finally {
|
|
212
|
+
restoreEnv(saved);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
test('readEnvConfig reads base URL with fallback', () => {
|
|
216
|
+
const saved = saveEnv();
|
|
217
|
+
clearEnv(Object.keys(saved));
|
|
218
|
+
try {
|
|
219
|
+
process.env.CODEVAULT_EMBEDDING_BASE_URL = 'https://api.example.com';
|
|
220
|
+
const result = readEnvConfig();
|
|
221
|
+
assert.equal(result.providers?.openai?.baseUrl, 'https://api.example.com');
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
restoreEnv(saved);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
test('readEnvConfig reads embedding model with multiple fallbacks', () => {
|
|
228
|
+
const saved = saveEnv();
|
|
229
|
+
clearEnv(Object.keys(saved));
|
|
230
|
+
try {
|
|
231
|
+
// Test first priority
|
|
232
|
+
process.env.CODEVAULT_EMBEDDING_MODEL = 'model-v1';
|
|
233
|
+
let result = readEnvConfig();
|
|
234
|
+
assert.equal(result.providers?.openai?.model, 'model-v1');
|
|
235
|
+
// Test second priority
|
|
236
|
+
delete process.env.CODEVAULT_EMBEDDING_MODEL;
|
|
237
|
+
process.env.CODEVAULT_OPENAI_EMBEDDING_MODEL = 'model-v2';
|
|
238
|
+
result = readEnvConfig();
|
|
239
|
+
assert.equal(result.providers?.openai?.model, 'model-v2');
|
|
240
|
+
// Test third priority (OPENAI_MODEL)
|
|
241
|
+
delete process.env.CODEVAULT_OPENAI_EMBEDDING_MODEL;
|
|
242
|
+
process.env.OPENAI_MODEL = 'model-v3';
|
|
243
|
+
result = readEnvConfig();
|
|
244
|
+
assert.equal(result.providers?.openai?.model, 'model-v3');
|
|
245
|
+
}
|
|
246
|
+
finally {
|
|
247
|
+
restoreEnv(saved);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
test('readEnvConfig parses dimensions correctly', () => {
|
|
251
|
+
const saved = saveEnv();
|
|
252
|
+
clearEnv(Object.keys(saved));
|
|
253
|
+
try {
|
|
254
|
+
// Need to set API key first for the dimensions to be applied
|
|
255
|
+
process.env.CODEVAULT_EMBEDDING_API_KEY = 'test-key';
|
|
256
|
+
process.env.CODEVAULT_EMBEDDING_DIMENSIONS = '1536';
|
|
257
|
+
const result = readEnvConfig();
|
|
258
|
+
assert.equal(result.providers?.openai?.dimensions, 1536);
|
|
259
|
+
}
|
|
260
|
+
finally {
|
|
261
|
+
restoreEnv(saved);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
test('readEnvConfig ignores invalid dimensions', () => {
|
|
265
|
+
const saved = saveEnv();
|
|
266
|
+
clearEnv(Object.keys(saved));
|
|
267
|
+
try {
|
|
268
|
+
process.env.CODEVAULT_EMBEDDING_API_KEY = 'test-key';
|
|
269
|
+
process.env.CODEVAULT_EMBEDDING_DIMENSIONS = 'not-a-number';
|
|
270
|
+
const result = readEnvConfig();
|
|
271
|
+
assert.equal(result.providers?.openai?.dimensions, undefined);
|
|
272
|
+
}
|
|
273
|
+
finally {
|
|
274
|
+
restoreEnv(saved);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
test('readEnvConfig ignores zero or negative dimensions', () => {
|
|
278
|
+
const saved = saveEnv();
|
|
279
|
+
clearEnv(Object.keys(saved));
|
|
280
|
+
try {
|
|
281
|
+
process.env.CODEVAULT_EMBEDDING_API_KEY = 'test-key';
|
|
282
|
+
process.env.CODEVAULT_EMBEDDING_DIMENSIONS = '0';
|
|
283
|
+
let result = readEnvConfig();
|
|
284
|
+
assert.equal(result.providers?.openai?.dimensions, undefined);
|
|
285
|
+
process.env.CODEVAULT_EMBEDDING_DIMENSIONS = '-100';
|
|
286
|
+
result = readEnvConfig();
|
|
287
|
+
assert.equal(result.providers?.openai?.dimensions, undefined);
|
|
288
|
+
}
|
|
289
|
+
finally {
|
|
290
|
+
restoreEnv(saved);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
test('readEnvConfig parses maxTokens correctly', () => {
|
|
294
|
+
const saved = saveEnv();
|
|
295
|
+
clearEnv(Object.keys(saved));
|
|
296
|
+
try {
|
|
297
|
+
process.env.CODEVAULT_EMBEDDING_MAX_TOKENS = '8192';
|
|
298
|
+
const result = readEnvConfig();
|
|
299
|
+
assert.equal(result.maxTokens, 8192);
|
|
300
|
+
}
|
|
301
|
+
finally {
|
|
302
|
+
restoreEnv(saved);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
test('readEnvConfig parses rate limits correctly', () => {
|
|
306
|
+
const saved = saveEnv();
|
|
307
|
+
clearEnv(Object.keys(saved));
|
|
308
|
+
try {
|
|
309
|
+
process.env.CODEVAULT_EMBEDDING_RATE_LIMIT_RPM = '100';
|
|
310
|
+
process.env.CODEVAULT_EMBEDDING_RATE_LIMIT_TPM = '50000';
|
|
311
|
+
const result = readEnvConfig();
|
|
312
|
+
assert.equal(result.rateLimit?.rpm, 100);
|
|
313
|
+
assert.equal(result.rateLimit?.tpm, 50000);
|
|
314
|
+
}
|
|
315
|
+
finally {
|
|
316
|
+
restoreEnv(saved);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
test('readEnvConfig reads encryption key and enables encryption', () => {
|
|
320
|
+
const saved = saveEnv();
|
|
321
|
+
clearEnv(Object.keys(saved));
|
|
322
|
+
try {
|
|
323
|
+
process.env.CODEVAULT_ENCRYPTION_KEY = 'my-secret-encryption-key';
|
|
324
|
+
const result = readEnvConfig();
|
|
325
|
+
assert.equal(result.encryption?.key, 'my-secret-encryption-key');
|
|
326
|
+
assert.equal(result.encryption?.enabled, true);
|
|
327
|
+
}
|
|
328
|
+
finally {
|
|
329
|
+
restoreEnv(saved);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
test('readEnvConfig reads reranker settings', () => {
|
|
333
|
+
const saved = saveEnv();
|
|
334
|
+
clearEnv(Object.keys(saved));
|
|
335
|
+
try {
|
|
336
|
+
process.env.CODEVAULT_RERANK_API_URL = 'https://reranker.example.com';
|
|
337
|
+
process.env.CODEVAULT_RERANK_API_KEY = 'reranker-key';
|
|
338
|
+
process.env.CODEVAULT_RERANK_MODEL = 'rerank-v1';
|
|
339
|
+
const result = readEnvConfig();
|
|
340
|
+
assert.equal(result.reranker?.apiUrl, 'https://reranker.example.com');
|
|
341
|
+
assert.equal(result.reranker?.apiKey, 'reranker-key');
|
|
342
|
+
assert.equal(result.reranker?.model, 'rerank-v1');
|
|
343
|
+
}
|
|
344
|
+
finally {
|
|
345
|
+
restoreEnv(saved);
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
test('readEnvConfig reads chat LLM settings', () => {
|
|
349
|
+
const saved = saveEnv();
|
|
350
|
+
clearEnv(Object.keys(saved));
|
|
351
|
+
try {
|
|
352
|
+
process.env.CODEVAULT_CHAT_API_KEY = 'chat-api-key';
|
|
353
|
+
process.env.CODEVAULT_CHAT_BASE_URL = 'https://chat.example.com';
|
|
354
|
+
process.env.CODEVAULT_CHAT_MODEL = 'gpt-4';
|
|
355
|
+
process.env.CODEVAULT_CHAT_MAX_TOKENS = '4000';
|
|
356
|
+
process.env.CODEVAULT_CHAT_TEMPERATURE = '0.7';
|
|
357
|
+
const result = readEnvConfig();
|
|
358
|
+
assert.equal(result.chatLLM?.openai?.apiKey, 'chat-api-key');
|
|
359
|
+
assert.equal(result.chatLLM?.openai?.baseUrl, 'https://chat.example.com');
|
|
360
|
+
assert.equal(result.chatLLM?.openai?.model, 'gpt-4');
|
|
361
|
+
assert.equal(result.chatLLM?.openai?.maxTokens, 4000);
|
|
362
|
+
assert.equal(result.chatLLM?.openai?.temperature, 0.7);
|
|
363
|
+
}
|
|
364
|
+
finally {
|
|
365
|
+
restoreEnv(saved);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
test('readEnvConfig caps chat max tokens at 256000', () => {
|
|
369
|
+
const saved = saveEnv();
|
|
370
|
+
clearEnv(Object.keys(saved));
|
|
371
|
+
try {
|
|
372
|
+
process.env.CODEVAULT_CHAT_MAX_TOKENS = '500000';
|
|
373
|
+
const result = readEnvConfig();
|
|
374
|
+
assert.equal(result.chatLLM?.openai?.maxTokens, 256000);
|
|
375
|
+
}
|
|
376
|
+
finally {
|
|
377
|
+
restoreEnv(saved);
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
test('readEnvConfig chat falls back to OPENAI_API_KEY', () => {
|
|
381
|
+
const saved = saveEnv();
|
|
382
|
+
clearEnv(Object.keys(saved));
|
|
383
|
+
try {
|
|
384
|
+
process.env.OPENAI_API_KEY = 'shared-openai-key';
|
|
385
|
+
const result = readEnvConfig();
|
|
386
|
+
assert.equal(result.providers?.openai?.apiKey, 'shared-openai-key');
|
|
387
|
+
assert.equal(result.chatLLM?.openai?.apiKey, 'shared-openai-key');
|
|
388
|
+
}
|
|
389
|
+
finally {
|
|
390
|
+
restoreEnv(saved);
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
// =============================================================================
|
|
394
|
+
// loadConfig tests (integration of all sources)
|
|
395
|
+
// =============================================================================
|
|
396
|
+
test('loadConfig merges configs with correct priority (env > project > global)', () => {
|
|
397
|
+
const tempDir = createTempDir();
|
|
398
|
+
const configDir = path.join(tempDir, '.codevault');
|
|
399
|
+
const saved = saveEnv();
|
|
400
|
+
clearEnv(Object.keys(saved));
|
|
401
|
+
try {
|
|
402
|
+
// Create project config
|
|
403
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
404
|
+
const projectConfig = {
|
|
405
|
+
maxTokens: 2000,
|
|
406
|
+
providers: {
|
|
407
|
+
openai: {
|
|
408
|
+
model: 'project-model'
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
fs.writeFileSync(path.join(configDir, 'config.json'), JSON.stringify(projectConfig));
|
|
413
|
+
// Set env var that should override project config
|
|
414
|
+
process.env.CODEVAULT_EMBEDDING_MAX_TOKENS = '4000';
|
|
415
|
+
const result = loadConfig(tempDir);
|
|
416
|
+
// Env should override project
|
|
417
|
+
assert.equal(result.maxTokens, 4000, 'env should override project maxTokens');
|
|
418
|
+
// Project config should still be present for non-overridden values
|
|
419
|
+
assert.equal(result.providers?.openai?.model, 'project-model', 'project model should be preserved');
|
|
420
|
+
}
|
|
421
|
+
finally {
|
|
422
|
+
restoreEnv(saved);
|
|
423
|
+
cleanupTempDir(tempDir);
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
test('loadConfig returns empty object when no config sources exist', () => {
|
|
427
|
+
const tempDir = createTempDir();
|
|
428
|
+
const saved = saveEnv();
|
|
429
|
+
clearEnv(Object.keys(saved));
|
|
430
|
+
try {
|
|
431
|
+
const result = loadConfig(tempDir);
|
|
432
|
+
assert.ok(typeof result === 'object', 'should return an object');
|
|
433
|
+
}
|
|
434
|
+
finally {
|
|
435
|
+
restoreEnv(saved);
|
|
436
|
+
cleanupTempDir(tempDir);
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
test('loadConfig deep merges nested provider objects', () => {
|
|
440
|
+
const tempDir = createTempDir();
|
|
441
|
+
const configDir = path.join(tempDir, '.codevault');
|
|
442
|
+
const saved = saveEnv();
|
|
443
|
+
clearEnv(Object.keys(saved));
|
|
444
|
+
try {
|
|
445
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
446
|
+
const projectConfig = {
|
|
447
|
+
providers: {
|
|
448
|
+
openai: {
|
|
449
|
+
model: 'project-model',
|
|
450
|
+
dimensions: 1536
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
fs.writeFileSync(path.join(configDir, 'config.json'), JSON.stringify(projectConfig));
|
|
455
|
+
// Set only API key in env
|
|
456
|
+
process.env.CODEVAULT_EMBEDDING_API_KEY = 'env-api-key';
|
|
457
|
+
const result = loadConfig(tempDir);
|
|
458
|
+
// Should have both env and project values
|
|
459
|
+
assert.equal(result.providers?.openai?.apiKey, 'env-api-key', 'should have env API key');
|
|
460
|
+
assert.equal(result.providers?.openai?.model, 'project-model', 'should preserve project model');
|
|
461
|
+
assert.equal(result.providers?.openai?.dimensions, 1536, 'should preserve project dimensions');
|
|
462
|
+
}
|
|
463
|
+
finally {
|
|
464
|
+
restoreEnv(saved);
|
|
465
|
+
cleanupTempDir(tempDir);
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
test('loadConfig deep merges rate limit config', () => {
|
|
469
|
+
const tempDir = createTempDir();
|
|
470
|
+
const configDir = path.join(tempDir, '.codevault');
|
|
471
|
+
const saved = saveEnv();
|
|
472
|
+
clearEnv(Object.keys(saved));
|
|
473
|
+
try {
|
|
474
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
475
|
+
const projectConfig = {
|
|
476
|
+
rateLimit: {
|
|
477
|
+
rpm: 50,
|
|
478
|
+
tpm: 10000
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
fs.writeFileSync(path.join(configDir, 'config.json'), JSON.stringify(projectConfig));
|
|
482
|
+
// Override only RPM via env
|
|
483
|
+
process.env.CODEVAULT_EMBEDDING_RATE_LIMIT_RPM = '100';
|
|
484
|
+
const result = loadConfig(tempDir);
|
|
485
|
+
assert.equal(result.rateLimit?.rpm, 100, 'should override RPM from env');
|
|
486
|
+
assert.equal(result.rateLimit?.tpm, 10000, 'should preserve TPM from project');
|
|
487
|
+
}
|
|
488
|
+
finally {
|
|
489
|
+
restoreEnv(saved);
|
|
490
|
+
cleanupTempDir(tempDir);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
test('loadConfig deep merges chatLLM config', () => {
|
|
494
|
+
const tempDir = createTempDir();
|
|
495
|
+
const configDir = path.join(tempDir, '.codevault');
|
|
496
|
+
const saved = saveEnv();
|
|
497
|
+
clearEnv(Object.keys(saved));
|
|
498
|
+
try {
|
|
499
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
500
|
+
const projectConfig = {
|
|
501
|
+
chatLLM: {
|
|
502
|
+
openai: {
|
|
503
|
+
model: 'gpt-4',
|
|
504
|
+
temperature: 0.5
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
};
|
|
508
|
+
fs.writeFileSync(path.join(configDir, 'config.json'), JSON.stringify(projectConfig));
|
|
509
|
+
// Override only API key via env
|
|
510
|
+
process.env.CODEVAULT_CHAT_API_KEY = 'chat-env-key';
|
|
511
|
+
const result = loadConfig(tempDir);
|
|
512
|
+
assert.equal(result.chatLLM?.openai?.apiKey, 'chat-env-key', 'should have env API key');
|
|
513
|
+
assert.equal(result.chatLLM?.openai?.model, 'gpt-4', 'should preserve project model');
|
|
514
|
+
assert.equal(result.chatLLM?.openai?.temperature, 0.5, 'should preserve project temperature');
|
|
515
|
+
}
|
|
516
|
+
finally {
|
|
517
|
+
restoreEnv(saved);
|
|
518
|
+
cleanupTempDir(tempDir);
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
// =============================================================================
|
|
522
|
+
// getConfigSources tests
|
|
523
|
+
// =============================================================================
|
|
524
|
+
test('getConfigSources returns all three sources', () => {
|
|
525
|
+
const tempDir = createTempDir();
|
|
526
|
+
const saved = saveEnv();
|
|
527
|
+
clearEnv(Object.keys(saved));
|
|
528
|
+
try {
|
|
529
|
+
const sources = getConfigSources(tempDir);
|
|
530
|
+
assert.ok('global' in sources, 'should have global key');
|
|
531
|
+
assert.ok('project' in sources, 'should have project key');
|
|
532
|
+
assert.ok('env' in sources, 'should have env key');
|
|
533
|
+
}
|
|
534
|
+
finally {
|
|
535
|
+
restoreEnv(saved);
|
|
536
|
+
cleanupTempDir(tempDir);
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
test('getConfigSources returns null for missing global and project', () => {
|
|
540
|
+
const tempDir = createTempDir();
|
|
541
|
+
const saved = saveEnv();
|
|
542
|
+
clearEnv(Object.keys(saved));
|
|
543
|
+
try {
|
|
544
|
+
const sources = getConfigSources(tempDir);
|
|
545
|
+
// Project should be null (no config in temp dir)
|
|
546
|
+
assert.equal(sources.project, null, 'project should be null');
|
|
547
|
+
// Env should always be an object
|
|
548
|
+
assert.ok(typeof sources.env === 'object', 'env should be object');
|
|
549
|
+
}
|
|
550
|
+
finally {
|
|
551
|
+
restoreEnv(saved);
|
|
552
|
+
cleanupTempDir(tempDir);
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
// =============================================================================
|
|
556
|
+
// saveGlobalConfig tests
|
|
557
|
+
// =============================================================================
|
|
558
|
+
test('saveGlobalConfig creates directory and file', () => {
|
|
559
|
+
// We cannot easily test the actual global config path without mocking
|
|
560
|
+
// This test verifies the function signature and return type
|
|
561
|
+
// In a real environment, we would use dependency injection or mock fs
|
|
562
|
+
// Just verify the function exists and has correct signature
|
|
563
|
+
assert.ok(typeof saveGlobalConfig === 'function');
|
|
564
|
+
});
|
|
565
|
+
// =============================================================================
|
|
566
|
+
// saveProjectConfig tests
|
|
567
|
+
// =============================================================================
|
|
568
|
+
test('saveProjectConfig creates directory and writes config', () => {
|
|
569
|
+
const tempDir = createTempDir();
|
|
570
|
+
const configDir = path.join(tempDir, '.codevault');
|
|
571
|
+
const configPath = path.join(configDir, 'config.json');
|
|
572
|
+
try {
|
|
573
|
+
const testConfig = {
|
|
574
|
+
defaultProvider: 'openai',
|
|
575
|
+
maxTokens: 8192,
|
|
576
|
+
providers: {
|
|
577
|
+
openai: {
|
|
578
|
+
apiKey: 'saved-key',
|
|
579
|
+
model: 'text-embedding-3-large'
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
saveProjectConfig(testConfig, tempDir);
|
|
584
|
+
assert.ok(fs.existsSync(configPath), 'config file should exist');
|
|
585
|
+
const savedContent = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
586
|
+
assert.equal(savedContent.defaultProvider, 'openai');
|
|
587
|
+
assert.equal(savedContent.maxTokens, 8192);
|
|
588
|
+
assert.equal(savedContent.providers?.openai?.apiKey, 'saved-key');
|
|
589
|
+
}
|
|
590
|
+
finally {
|
|
591
|
+
cleanupTempDir(tempDir);
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
test('saveProjectConfig overwrites existing config', () => {
|
|
595
|
+
const tempDir = createTempDir();
|
|
596
|
+
const configDir = path.join(tempDir, '.codevault');
|
|
597
|
+
const configPath = path.join(configDir, 'config.json');
|
|
598
|
+
try {
|
|
599
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
600
|
+
fs.writeFileSync(configPath, JSON.stringify({ maxTokens: 1000 }));
|
|
601
|
+
saveProjectConfig({ maxTokens: 2000 }, tempDir);
|
|
602
|
+
const savedContent = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
603
|
+
assert.equal(savedContent.maxTokens, 2000, 'should overwrite with new value');
|
|
604
|
+
}
|
|
605
|
+
finally {
|
|
606
|
+
cleanupTempDir(tempDir);
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
// =============================================================================
|
|
610
|
+
// hasGlobalConfig tests
|
|
611
|
+
// =============================================================================
|
|
612
|
+
test('hasGlobalConfig returns boolean', () => {
|
|
613
|
+
const result = hasGlobalConfig();
|
|
614
|
+
assert.ok(typeof result === 'boolean', 'should return a boolean');
|
|
615
|
+
});
|
|
616
|
+
// =============================================================================
|
|
617
|
+
// hasProjectConfig tests
|
|
618
|
+
// =============================================================================
|
|
619
|
+
test('hasProjectConfig returns false for non-existent config', () => {
|
|
620
|
+
const tempDir = createTempDir();
|
|
621
|
+
try {
|
|
622
|
+
const result = hasProjectConfig(tempDir);
|
|
623
|
+
assert.equal(result, false, 'should return false when config does not exist');
|
|
624
|
+
}
|
|
625
|
+
finally {
|
|
626
|
+
cleanupTempDir(tempDir);
|
|
627
|
+
}
|
|
628
|
+
});
|
|
629
|
+
test('hasProjectConfig returns true for existing config', () => {
|
|
630
|
+
const tempDir = createTempDir();
|
|
631
|
+
const configDir = path.join(tempDir, '.codevault');
|
|
632
|
+
try {
|
|
633
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
634
|
+
fs.writeFileSync(path.join(configDir, 'config.json'), '{}');
|
|
635
|
+
const result = hasProjectConfig(tempDir);
|
|
636
|
+
assert.equal(result, true, 'should return true when config exists');
|
|
637
|
+
}
|
|
638
|
+
finally {
|
|
639
|
+
cleanupTempDir(tempDir);
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
// =============================================================================
|
|
643
|
+
// getGlobalConfigPath tests
|
|
644
|
+
// =============================================================================
|
|
645
|
+
test('getGlobalConfigPath returns expected path', () => {
|
|
646
|
+
const result = getGlobalConfigPath();
|
|
647
|
+
assert.ok(result.includes('.codevault'), 'should contain .codevault');
|
|
648
|
+
assert.ok(result.includes('config.json'), 'should contain config.json');
|
|
649
|
+
assert.ok(result.startsWith(os.homedir()), 'should start with home directory');
|
|
650
|
+
});
|
|
651
|
+
// =============================================================================
|
|
652
|
+
// getProjectConfigPath tests
|
|
653
|
+
// =============================================================================
|
|
654
|
+
test('getProjectConfigPath returns expected path with basePath', () => {
|
|
655
|
+
const result = getProjectConfigPath('/some/project');
|
|
656
|
+
assert.ok(result.includes('.codevault'), 'should contain .codevault');
|
|
657
|
+
assert.ok(result.includes('config.json'), 'should contain config.json');
|
|
658
|
+
assert.ok(result.startsWith('/some/project'), 'should start with basePath');
|
|
659
|
+
});
|
|
660
|
+
test('getProjectConfigPath resolves relative basePath', () => {
|
|
661
|
+
const result = getProjectConfigPath('.');
|
|
662
|
+
assert.ok(result.includes('.codevault'), 'should contain .codevault');
|
|
663
|
+
assert.ok(result.includes('config.json'), 'should contain config.json');
|
|
664
|
+
assert.ok(path.isAbsolute(result), 'should be an absolute path');
|
|
665
|
+
});
|
|
666
|
+
// =============================================================================
|
|
667
|
+
// Edge cases and error handling
|
|
668
|
+
// =============================================================================
|
|
669
|
+
test('readEnvConfig handles NaN values gracefully', () => {
|
|
670
|
+
const saved = saveEnv();
|
|
671
|
+
clearEnv(Object.keys(saved));
|
|
672
|
+
try {
|
|
673
|
+
process.env.CODEVAULT_CHAT_TEMPERATURE = 'not-a-number';
|
|
674
|
+
const result = readEnvConfig();
|
|
675
|
+
// Should not set temperature if parse fails
|
|
676
|
+
assert.equal(result.chatLLM?.openai?.temperature, undefined);
|
|
677
|
+
}
|
|
678
|
+
finally {
|
|
679
|
+
restoreEnv(saved);
|
|
680
|
+
}
|
|
681
|
+
});
|
|
682
|
+
test('readEnvConfig handles empty string values', () => {
|
|
683
|
+
const saved = saveEnv();
|
|
684
|
+
clearEnv(Object.keys(saved));
|
|
685
|
+
try {
|
|
686
|
+
process.env.CODEVAULT_EMBEDDING_API_KEY = '';
|
|
687
|
+
const result = readEnvConfig();
|
|
688
|
+
// Empty string is falsy, so should not be set
|
|
689
|
+
assert.equal(result.providers, undefined);
|
|
690
|
+
}
|
|
691
|
+
finally {
|
|
692
|
+
restoreEnv(saved);
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
test('loadConfig returns object when only project config is missing', () => {
|
|
696
|
+
const tempDir = createTempDir();
|
|
697
|
+
const saved = saveEnv();
|
|
698
|
+
clearEnv(Object.keys(saved));
|
|
699
|
+
try {
|
|
700
|
+
const result = loadConfig(tempDir);
|
|
701
|
+
// Should return object without errors
|
|
702
|
+
// Note: global config may still populate some fields
|
|
703
|
+
assert.ok(typeof result === 'object');
|
|
704
|
+
}
|
|
705
|
+
finally {
|
|
706
|
+
restoreEnv(saved);
|
|
707
|
+
cleanupTempDir(tempDir);
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
test('readProjectConfig handles permission errors gracefully', () => {
|
|
711
|
+
// This test is platform-dependent and may not work on all systems
|
|
712
|
+
// The function should return null and log a warning on errors
|
|
713
|
+
const result = readProjectConfig('/nonexistent/path/that/does/not/exist');
|
|
714
|
+
assert.equal(result, null, 'should return null for inaccessible path');
|
|
715
|
+
});
|
|
716
|
+
//# sourceMappingURL=loader.test.js.map
|