directory-indexer 0.3.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/config.js +4 -4
- package/dist/config.js.map +1 -1
- package/dist/storage.js +2 -2
- package/dist/storage.js.map +1 -1
- package/package.json +16 -22
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Self-hosted semantic search for local files. Give your AI assistant access to se
|
|
|
14
14
|
**Prerequisites:**
|
|
15
15
|
|
|
16
16
|
- **[Docker](https://docs.docker.com/get-docker/)** - For running Qdrant and Ollama _(skip if you already have them running natively)_
|
|
17
|
-
- **[Node.js 18+](https://nodejs.org/en/download/)** - Required for running directory-indexer
|
|
17
|
+
- **[Node.js 18+](https://nodejs.org/en/download/)** - Required for running directory-indexer (the v1.x line targets Node 18 LTS compatibility)
|
|
18
18
|
|
|
19
19
|
_Note: For native Qdrant and Ollama installation without Docker, see [Setup section](#setup)._
|
|
20
20
|
|
package/dist/config.js
CHANGED
|
@@ -12,14 +12,14 @@ const WorkspaceSchema = z.object({
|
|
|
12
12
|
const ConfigSchema = z.object({
|
|
13
13
|
storage: z.object({
|
|
14
14
|
sqlitePath: z.string(),
|
|
15
|
-
qdrantEndpoint: z.
|
|
15
|
+
qdrantEndpoint: z.url(),
|
|
16
16
|
qdrantCollection: z.string(),
|
|
17
17
|
qdrantApiKey: z.string().optional()
|
|
18
18
|
}),
|
|
19
19
|
embedding: z.object({
|
|
20
20
|
provider: z.enum(["ollama", "openai", "mock"]),
|
|
21
21
|
model: z.string(),
|
|
22
|
-
endpoint: z.
|
|
22
|
+
endpoint: z.url()
|
|
23
23
|
}),
|
|
24
24
|
indexing: z.object({
|
|
25
25
|
chunkSize: z.number().positive(),
|
|
@@ -30,7 +30,7 @@ const ConfigSchema = z.object({
|
|
|
30
30
|
}),
|
|
31
31
|
dataDir: z.string(),
|
|
32
32
|
verbose: z.boolean(),
|
|
33
|
-
workspaces: z.record(WorkspaceSchema)
|
|
33
|
+
workspaces: z.record(z.string(), WorkspaceSchema)
|
|
34
34
|
});
|
|
35
35
|
class ConfigError extends Error {
|
|
36
36
|
constructor(message, cause) {
|
|
@@ -109,7 +109,7 @@ function loadConfig(options = {}) {
|
|
|
109
109
|
return ConfigSchema.parse(config);
|
|
110
110
|
} catch (error) {
|
|
111
111
|
if (error instanceof z.ZodError) {
|
|
112
|
-
const messages = error.
|
|
112
|
+
const messages = error.issues.map((e) => `${e.path.join(".")}: ${e.message}`);
|
|
113
113
|
throw new ConfigError(`Configuration validation failed: ${messages.join(", ")}`, error);
|
|
114
114
|
}
|
|
115
115
|
throw new ConfigError("Failed to load configuration", error);
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sources":["../src/config.ts"],"sourcesContent":["import { homedir } from 'os';\nimport { join } from 'path';\nimport { existsSync, statSync } from 'fs';\nimport { z } from 'zod';\nimport { normalizePath } from './utils';\n\nconst WorkspaceSchema = z.object({\n paths: z.array(z.string()),\n isValid: z.boolean(),\n filesCount: z.number().optional(),\n chunksCount: z.number().optional(),\n});\n\nconst ConfigSchema = z.object({\n storage: z.object({\n sqlitePath: z.string(),\n qdrantEndpoint: z.
|
|
1
|
+
{"version":3,"file":"config.js","sources":["../src/config.ts"],"sourcesContent":["import { homedir } from 'os';\nimport { join } from 'path';\nimport { existsSync, statSync } from 'fs';\nimport { z } from 'zod';\nimport { normalizePath } from './utils';\n\nconst WorkspaceSchema = z.object({\n paths: z.array(z.string()),\n isValid: z.boolean(),\n filesCount: z.number().optional(),\n chunksCount: z.number().optional(),\n});\n\nconst ConfigSchema = z.object({\n storage: z.object({\n sqlitePath: z.string(),\n qdrantEndpoint: z.url(),\n qdrantCollection: z.string(),\n qdrantApiKey: z.string().optional(),\n }),\n embedding: z.object({\n provider: z.enum(['ollama', 'openai', 'mock']),\n model: z.string(),\n endpoint: z.url(),\n }),\n indexing: z.object({\n chunkSize: z.number().positive(),\n chunkOverlap: z.number().nonnegative(),\n maxFileSize: z.number().positive(),\n ignorePatterns: z.array(z.string()),\n respectGitignore: z.boolean(),\n }),\n dataDir: z.string(),\n verbose: z.boolean(),\n workspaces: z.record(z.string(), WorkspaceSchema),\n});\n\nexport type Config = z.infer<typeof ConfigSchema>;\nexport type WorkspaceConfig = z.infer<typeof WorkspaceSchema>;\n\nexport class ConfigError extends Error {\n constructor(message: string, public override cause?: Error) {\n super(message);\n this.name = 'ConfigError';\n }\n}\n\nfunction parseWorkspaces(env: Record<string, string | undefined>): Record<string, WorkspaceConfig> {\n const workspaces: Record<string, WorkspaceConfig> = {};\n \n for (const [key, value] of Object.entries(env)) {\n if (key.startsWith('WORKSPACE_') && value) {\n const name = key.replace('WORKSPACE_', '').toLowerCase();\n \n // Parse paths from comma-separated string or JSON array\n let paths: string[];\n try {\n // Try parsing as JSON array first\n paths = JSON.parse(value);\n if (!Array.isArray(paths)) {\n throw new Error('Not an array');\n }\n } catch {\n // Fall back to comma-separated string\n paths = value.split(',').map(p => p.trim()).filter(p => p.length > 0);\n }\n \n // Normalize paths for consistent comparison\n const normalizedPaths = paths.map(normalizePath);\n \n // Validate that paths exist and are directories\n const isValid = normalizedPaths.every(path => {\n try {\n return existsSync(path) && statSync(path).isDirectory();\n } catch {\n return false;\n }\n });\n \n workspaces[name] = {\n paths: normalizedPaths,\n isValid,\n };\n }\n }\n \n return workspaces;\n}\n\nexport function getWorkspacePaths(config: Config, workspace: string): string[] {\n const workspaceConfig = config.workspaces[workspace];\n return workspaceConfig?.paths || [];\n}\n\nexport function getAvailableWorkspaces(config: Config): string[] {\n return Object.keys(config.workspaces);\n}\n\nexport function loadConfig(options: { verbose?: boolean } = {}): Config {\n const dataDir = process.env.DIRECTORY_INDEXER_DATA_DIR || join(homedir(), '.directory-indexer');\n \n // Use separate database file and collection for tests to avoid contaminating main data\n const isTest = process.env.NODE_ENV === 'test' || process.env.VITEST === 'true';\n const dbFileName = isTest ? 'test-data.db' : 'data.db';\n const defaultCollection = isTest ? 'directory-indexer-test' : 'directory-indexer';\n \n // Parse workspace configurations from environment variables\n const workspaces = parseWorkspaces(process.env);\n \n const config = {\n storage: {\n sqlitePath: join(dataDir, dbFileName),\n qdrantEndpoint: process.env.QDRANT_ENDPOINT || 'http://127.0.0.1:6333',\n qdrantCollection: process.env.DIRECTORY_INDEXER_QDRANT_COLLECTION || defaultCollection,\n qdrantApiKey: process.env.QDRANT_API_KEY,\n },\n embedding: {\n provider: (process.env.EMBEDDING_PROVIDER as Config['embedding']['provider']) || 'ollama',\n model: process.env.EMBEDDING_MODEL || 'nomic-embed-text',\n endpoint: process.env.OLLAMA_ENDPOINT || 'http://127.0.0.1:11434',\n },\n indexing: {\n chunkSize: parseInt(process.env.CHUNK_SIZE || '512'),\n chunkOverlap: parseInt(process.env.CHUNK_OVERLAP || '50'),\n maxFileSize: parseInt(process.env.MAX_FILE_SIZE || '10485760'),\n ignorePatterns: ['.git', 'node_modules', 'target', '.DS_Store'],\n respectGitignore: process.env.RESPECT_GITIGNORE !== 'false',\n },\n dataDir,\n verbose: options.verbose ?? (process.env.VERBOSE === 'true'),\n workspaces,\n };\n\n try {\n return ConfigSchema.parse(config);\n } catch (error) {\n if (error instanceof z.ZodError) {\n const messages = error.issues.map(e => `${e.path.join('.')}: ${e.message}`);\n throw new ConfigError(`Configuration validation failed: ${messages.join(', ')}`, error);\n }\n throw new ConfigError('Failed to load configuration', error as Error);\n }\n}"],"names":[],"mappings":"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"}
|
package/dist/storage.js
CHANGED
|
@@ -312,7 +312,7 @@ class SQLiteStorage {
|
|
|
312
312
|
id: row.id,
|
|
313
313
|
path: row.path,
|
|
314
314
|
size: row.size,
|
|
315
|
-
modifiedTime: new Date(row.modified_time
|
|
315
|
+
modifiedTime: new Date(row.modified_time),
|
|
316
316
|
hash: row.hash,
|
|
317
317
|
parentDirs: JSON.parse(row.parent_dirs),
|
|
318
318
|
chunks: row.chunks_json ? JSON.parse(row.chunks_json) : [],
|
|
@@ -362,7 +362,7 @@ class SQLiteStorage {
|
|
|
362
362
|
id: row.id,
|
|
363
363
|
path: row.path,
|
|
364
364
|
size: row.size,
|
|
365
|
-
modifiedTime: new Date(row.modified_time
|
|
365
|
+
modifiedTime: new Date(row.modified_time),
|
|
366
366
|
hash: row.hash,
|
|
367
367
|
parentDirs: JSON.parse(row.parent_dirs),
|
|
368
368
|
chunks: row.chunks_json ? JSON.parse(row.chunks_json) : [],
|
package/dist/storage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"storage.js","sources":["../src/storage.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { Config } from './config.js';\nimport { FileInfo, ChunkInfo, ensureDirectory } from './utils.js';\nimport { dirname } from 'path';\n\n/**\n * Escape special characters in a string for use in a SQL LIKE pattern.\n * Escapes %, _, and \\ using \\ as the escape character.\n */\nfunction escapeLike(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/%/g, '\\\\%').replace(/_/g, '\\\\_');\n}\n\n/**\n * Build a SQL WHERE clause that matches files within a directory,\n * handling both `/` and `\\` as path separators so queries work\n * regardless of how paths were stored.\n *\n * Returns [clause, ...params] where clause uses `?` placeholders.\n */\nfunction directoryLikeClause(column: string, dirPath: string): { clause: string; params: string[] } {\n const escaped = escapeLike(dirPath);\n return {\n clause: `(${column} = ? OR ${column} LIKE ? ESCAPE '\\\\' OR ${column} LIKE ? ESCAPE '\\\\')`,\n params: [dirPath, `${escaped}/%`, `${escaped}\\\\%`]\n };\n}\n\n// Registry of all open SQLiteStorage instances for graceful shutdown\nconst openStorageInstances = new Set<SQLiteStorage>();\n\n/**\n * Close all open SQLiteStorage instances. Called during graceful shutdown.\n */\nexport function closeAllStorage(): void {\n for (const instance of openStorageInstances) {\n try {\n instance.close();\n } catch {\n // Ignore errors during shutdown cleanup\n }\n }\n}\n\nexport interface DirectoryRecord {\n id: number;\n path: string;\n status: 'pending' | 'indexing' | 'completed' | 'failed';\n indexedAt: Date;\n}\n\nexport interface FileRecord {\n id: number;\n path: string;\n size: number;\n modifiedTime: Date;\n hash: string;\n parentDirs: string[];\n chunks: ChunkInfo[];\n errors?: string[];\n}\n\nexport interface QdrantPoint {\n id: string | number;\n vector: number[];\n payload: {\n filePath: string;\n chunkId: string;\n fileHash: string;\n content: string;\n parentDirectories: string[];\n };\n score?: number;\n}\n\nexport class StorageError extends Error {\n constructor(message: string, public override cause?: Error) {\n super(message);\n this.name = 'StorageError';\n }\n}\n\nexport class QdrantClient {\n constructor(private config: Config) {}\n\n async healthCheck(): Promise<boolean> {\n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/healthz`);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n async createCollection(): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const checkResponse = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`);\n if (checkResponse.ok) {\n return;\n }\n\n const createResponse = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n vectors: {\n size: 768,\n distance: 'Cosine'\n }\n })\n });\n\n if (!createResponse.ok) {\n throw new Error(`Failed to create collection: ${createResponse.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to create Qdrant collection`, error as Error);\n }\n }\n\n async upsertPoints(points: QdrantPoint[]): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ points })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to upsert points: ${response.status} ${response.statusText} - ${errorText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to upsert points to Qdrant`, error as Error);\n }\n }\n\n async searchPoints(vector: number[], limit: number = 10, filter?: Record<string, unknown>): Promise<QdrantPoint[]> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const searchBody: Record<string, unknown> = {\n vector,\n limit,\n with_payload: true\n };\n \n if (filter) {\n searchBody.filter = filter;\n }\n \n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/search`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(searchBody)\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Failed to search points: ${response.statusText} - ${errorBody}`);\n }\n\n const data = await response.json();\n return data.result.map((item: { id: string | number; vector: number[]; payload: Record<string, unknown>; score: number }) => ({\n id: item.id,\n vector: item.vector,\n payload: item.payload,\n score: item.score\n }));\n } catch (error) {\n throw new StorageError(`Failed to search points in Qdrant`, error as Error);\n }\n }\n\n async deletePoints(ids: (string | number)[]): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ points: ids })\n });\n\n if (!response.ok) {\n throw new Error(`Failed to delete points: ${response.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete points from Qdrant`, error as Error);\n }\n }\n\n async deletePointsByFilePath(filePath: string): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n filter: {\n must: [\n {\n key: 'filePath',\n match: { value: filePath }\n }\n ]\n }\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete points by file path: ${response.status} ${response.statusText} - ${errorText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete points by file path from Qdrant`, error as Error);\n }\n }\n\n async countPoints(filter?: Record<string, unknown>): Promise<number> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const countBody: Record<string, unknown> = { exact: true };\n if (filter) {\n countBody.filter = filter;\n }\n\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/count`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(countBody)\n });\n\n if (!response.ok) {\n throw new Error(`Failed to count points: ${response.statusText}`);\n }\n\n const data = await response.json();\n return data.result.count;\n } catch (error) {\n throw new StorageError(`Failed to count points in Qdrant`, error as Error);\n }\n }\n\n async scrollPoints(filter?: Record<string, unknown>, limit: number = 1000): Promise<QdrantPoint[]> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const scrollBody: Record<string, unknown> = {\n limit,\n with_payload: true,\n with_vector: false\n };\n if (filter) {\n scrollBody.filter = filter;\n }\n\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/scroll`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(scrollBody)\n });\n\n if (!response.ok) {\n throw new Error(`Failed to scroll points: ${response.statusText}`);\n }\n\n const data = await response.json();\n return data.result.points.map((item: { id: string | number; payload: Record<string, unknown> }) => ({\n id: item.id,\n vector: [],\n payload: item.payload,\n score: 0\n }));\n } catch (error) {\n throw new StorageError(`Failed to scroll points in Qdrant`, error as Error);\n }\n }\n\n async getCollectionInfo(): Promise<{ vectors_count?: number } | null> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`);\n \n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`Failed to get collection info: ${response.statusText}`);\n }\n\n const data = await response.json();\n return {\n vectors_count: data.result?.points_count || data.result?.vectors_count || 0\n };\n } catch (error) {\n throw new StorageError(`Failed to get collection info from Qdrant`, error as Error);\n }\n }\n\n async deleteCollection(): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`, {\n method: 'DELETE',\n headers: this.config.storage.qdrantApiKey ? {\n 'api-key': this.config.storage.qdrantApiKey\n } : {}\n });\n\n if (!response.ok && response.status !== 404) {\n throw new Error(`Failed to delete collection: ${response.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete collection from Qdrant`, error as Error);\n }\n }\n}\n\nexport class SQLiteStorage {\n public db: Database.Database;\n\n constructor(private config: Config) {\n this.db = this.initializeDatabase();\n openStorageInstances.add(this);\n }\n\n private initializeDatabase(): Database.Database {\n try {\n ensureDirectory(dirname(this.config.storage.sqlitePath));\n \n const db = new Database(this.config.storage.sqlitePath);\n \n db.exec(`\n CREATE TABLE IF NOT EXISTS directories (\n id INTEGER PRIMARY KEY,\n path TEXT UNIQUE NOT NULL,\n status TEXT DEFAULT 'pending',\n indexed_at INTEGER DEFAULT 0\n );\n\n CREATE TABLE IF NOT EXISTS files (\n id INTEGER PRIMARY KEY,\n path TEXT UNIQUE NOT NULL,\n size INTEGER NOT NULL,\n modified_time INTEGER NOT NULL,\n hash TEXT NOT NULL,\n parent_dirs TEXT NOT NULL,\n chunks_json TEXT,\n errors_json TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_files_path ON files(path);\n CREATE INDEX IF NOT EXISTS idx_files_hash ON files(hash);\n CREATE INDEX IF NOT EXISTS idx_directories_path ON directories(path);\n `);\n\n db.pragma('journal_mode = WAL');\n db.pragma('busy_timeout = 5000');\n\n return db;\n } catch (error) {\n throw new StorageError(`Failed to initialize SQLite database`, error as Error);\n }\n }\n\n async getDirectory(path: string): Promise<DirectoryRecord | null> {\n try {\n const stmt = this.db.prepare('SELECT * FROM directories WHERE path = ?');\n const row = stmt.get(path) as { id: number; path: string; status: 'pending' | 'indexing' | 'completed' | 'failed'; indexed_at: number } | undefined;\n \n if (!row) return null;\n \n return {\n id: row.id,\n path: row.path,\n status: row.status,\n indexedAt: new Date(row.indexed_at)\n };\n } catch (error) {\n throw new StorageError(`Failed to get directory record`, error as Error);\n }\n }\n\n async upsertDirectory(path: string, status: DirectoryRecord['status']): Promise<void> {\n try {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO directories (path, status, indexed_at)\n VALUES (?, ?, ?)\n `);\n \n stmt.run(path, status, Date.now());\n } catch (error) {\n throw new StorageError(`Failed to upsert directory record`, error as Error);\n }\n }\n\n async getFile(path: string): Promise<FileRecord | null> {\n try {\n const stmt = this.db.prepare('SELECT * FROM files WHERE path = ?');\n const row = stmt.get(path) as { id: number; path: string; size: number; modified_time: number; hash: string; parent_dirs: string; chunks_json: string | null; errors_json: string | null } | undefined;\n \n if (!row) return null;\n \n return {\n id: row.id,\n path: row.path,\n size: row.size,\n modifiedTime: new Date(row.modified_time * 1000),\n hash: row.hash,\n parentDirs: JSON.parse(row.parent_dirs),\n chunks: row.chunks_json ? JSON.parse(row.chunks_json) : [],\n errors: row.errors_json ? JSON.parse(row.errors_json) : undefined\n };\n } catch (error) {\n throw new StorageError(`Failed to get file record`, error as Error);\n }\n }\n\n async upsertFile(fileInfo: FileInfo, chunks: ChunkInfo[] = [], errors: string[] = []): Promise<void> {\n try {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO files (path, size, modified_time, hash, parent_dirs, chunks_json, errors_json)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n \n stmt.run(\n fileInfo.path,\n fileInfo.size,\n fileInfo.modifiedTime.getTime(),\n fileInfo.hash,\n JSON.stringify(fileInfo.parentDirs),\n chunks.length > 0 ? JSON.stringify(chunks) : null,\n errors.length > 0 ? JSON.stringify(errors) : null\n );\n } catch (error) {\n throw new StorageError(`Failed to upsert file record`, error as Error);\n }\n }\n\n async deleteFile(path: string): Promise<void> {\n try {\n const stmt = this.db.prepare('DELETE FROM files WHERE path = ?');\n stmt.run(path);\n } catch (error) {\n throw new StorageError(`Failed to delete file record`, error as Error);\n }\n }\n\n getDirectories(): string[] {\n const rows = this.db.prepare('SELECT path FROM directories').all() as { path: string }[];\n return rows.map(row => row.path);\n }\n\n async getFilesByDirectory(directoryPath: string): Promise<FileRecord[]> {\n try {\n const { clause, params } = directoryLikeClause('path', directoryPath);\n const stmt = this.db.prepare(`SELECT * FROM files WHERE ${clause}`);\n const rows = stmt.all(...params) as { id: number; path: string; size: number; modified_time: number; hash: string; parent_dirs: string; chunks_json: string | null; errors_json: string | null }[];\n\n return rows.map(row => ({\n id: row.id,\n path: row.path,\n size: row.size,\n modifiedTime: new Date(row.modified_time * 1000),\n hash: row.hash,\n parentDirs: JSON.parse(row.parent_dirs),\n chunks: row.chunks_json ? JSON.parse(row.chunks_json) : [],\n errors: row.errors_json ? JSON.parse(row.errors_json) : undefined\n }));\n } catch (error) {\n throw new StorageError(`Failed to get files by directory`, error as Error);\n }\n }\n\n deleteDirectory(path: string): void {\n this.db.prepare('DELETE FROM directories WHERE path = ?').run(path);\n }\n\n deleteFilesByDirectory(directoryPath: string): number {\n const { clause, params } = directoryLikeClause('path', directoryPath);\n const result = this.db.prepare(`DELETE FROM files WHERE ${clause}`).run(...params);\n return result.changes;\n }\n\n close(): void {\n openStorageInstances.delete(this);\n this.db.close();\n }\n}\n\nexport async function initializeStorage(config: Config): Promise<{ sqlite: SQLiteStorage; qdrant: QdrantClient }> {\n const sqlite = new SQLiteStorage(config);\n const qdrant = new QdrantClient(config);\n \n await qdrant.createCollection();\n \n return { sqlite, qdrant };\n}\n\nexport async function initDatabase(dbPath: string): Promise<Database.Database> {\n return new Database(dbPath);\n}\n\nexport interface ResetStats {\n sqliteExists: boolean;\n sqliteSize?: string;\n qdrantCollectionExists: boolean;\n qdrantVectorCount?: number;\n}\n\nexport interface ResetResult {\n sqliteDeleted: boolean;\n qdrantDeleted: boolean;\n warnings: string[];\n}\n\nexport async function getResetPreview(config: Config): Promise<ResetStats> {\n const stats: ResetStats = {\n sqliteExists: false,\n qdrantCollectionExists: false\n };\n\n if (await import('fs').then(fs => fs.existsSync(config.storage.sqlitePath))) {\n stats.sqliteExists = true;\n try {\n const fileStats = await import('fs').then(fs => fs.statSync(config.storage.sqlitePath));\n const sizeInMB = (fileStats.size / (1024 * 1024)).toFixed(1);\n stats.sqliteSize = `${sizeInMB} MB`;\n } catch {\n stats.sqliteSize = 'unknown size';\n }\n }\n\n try {\n const qdrant = new QdrantClient(config);\n const isHealthy = await qdrant.healthCheck();\n \n if (isHealthy) {\n const collectionInfo = await qdrant.getCollectionInfo();\n if (collectionInfo) {\n stats.qdrantCollectionExists = true;\n stats.qdrantVectorCount = collectionInfo.vectors_count || 0;\n }\n }\n } catch {\n // Qdrant unavailable\n }\n\n return stats;\n}\n\nexport async function clearDatabase(config: Config): Promise<boolean> {\n try {\n if (!await import('fs').then(fs => fs.existsSync(config.storage.sqlitePath))) {\n return true; // Already clean\n }\n \n await import('fs/promises').then(fs => fs.unlink(config.storage.sqlitePath));\n return true;\n } catch (error) {\n throw new StorageError(`Failed to delete SQLite database: ${error instanceof Error ? error.message : 'Unknown error'}`, error as Error);\n }\n}\n\nexport async function clearVectorCollection(config: Config): Promise<boolean> {\n try {\n const qdrant = new QdrantClient(config);\n const isHealthy = await qdrant.healthCheck();\n \n if (!isHealthy) {\n throw new StorageError(`Qdrant unavailable at ${config.storage.qdrantEndpoint}`);\n }\n \n const collectionInfo = await qdrant.getCollectionInfo();\n if (collectionInfo) {\n await qdrant.deleteCollection();\n }\n \n return true;\n } catch (error) {\n throw new StorageError(`Failed to reset Qdrant collection: ${error instanceof Error ? error.message : 'Unknown error'}`, error as Error);\n }\n}\n\nexport interface DirectoryStatus {\n path: string;\n status: string;\n filesCount: number;\n chunksCount: number;\n lastIndexed: string | null;\n errors: string[];\n}\n\nexport interface WorkspaceStatus {\n name: string;\n paths: string[];\n isValid: boolean;\n filesCount: number;\n chunksCount: number;\n health: {\n status: 'healthy' | 'warning' | 'error';\n issues: string[];\n recommendations: string[];\n };\n}\n\nexport interface IndexStatus {\n directoriesIndexed: number;\n filesIndexed: number;\n chunksIndexed: number;\n databaseSize: string;\n lastIndexed: string | null;\n errors: string[];\n directories: DirectoryStatus[];\n workspaces: WorkspaceStatus[];\n workspaceHealth: {\n healthy: number;\n warnings: number;\n errors: number;\n criticalIssues: string[];\n recommendations: string[];\n };\n qdrantConsistency: {\n isConsistent: boolean;\n issues: string[];\n };\n}\n\nasync function calculateWorkspaceStatistics(sqlite: SQLiteStorage, config: Config): Promise<WorkspaceStatus[]> {\n const { getAvailableWorkspaces, getWorkspacePaths } = await import('./config.js');\n const workspaces: WorkspaceStatus[] = [];\n \n // Get all indexed directories for comparison\n const directoriesStmt = sqlite.db.prepare('SELECT path, status FROM directories');\n const indexedDirectories = directoriesStmt.all() as { path: string; status: string }[];\n const indexedDirectoryPaths = indexedDirectories.map(d => d.path);\n \n // Get Qdrant client for efficient workspace filtering\n const qdrant = new QdrantClient(config);\n \n for (const workspaceName of getAvailableWorkspaces(config)) {\n const workspacePaths = getWorkspacePaths(config, workspaceName);\n const workspaceConfig = config.workspaces[workspaceName];\n \n let filesCount = 0;\n let chunksCount = 0;\n \n if (workspacePaths.length > 0) {\n try {\n // Build workspace filter using same logic as search\n const workspaceFilter = {\n must: [\n {\n key: \"parentDirectories\",\n match: { any: workspacePaths }\n }\n ]\n };\n \n // Get chunk count efficiently using Qdrant\n chunksCount = await qdrant.countPoints(workspaceFilter);\n \n // Get unique file paths using Qdrant scroll\n const points = await qdrant.scrollPoints(workspaceFilter, 10000);\n const uniqueFilePaths = new Set(points.map(p => p.payload.filePath));\n filesCount = uniqueFilePaths.size;\n \n } catch {\n // Fallback to 0 if Qdrant is unavailable\n filesCount = 0;\n chunksCount = 0;\n }\n }\n \n // Check workspace health\n const health = analyzeWorkspaceHealth(workspacePaths, workspaceConfig.isValid, filesCount, indexedDirectoryPaths);\n \n workspaces.push({\n name: workspaceName,\n paths: workspacePaths,\n isValid: workspaceConfig.isValid,\n filesCount,\n chunksCount,\n health\n });\n }\n \n return workspaces;\n}\n\nfunction analyzeWorkspaceHealth(\n workspacePaths: string[], \n isValid: boolean, \n filesCount: number, \n indexedDirectoryPaths: string[]\n): { status: 'healthy' | 'warning' | 'error'; issues: string[]; recommendations: string[] } {\n const issues: string[] = [];\n const recommendations: string[] = [];\n \n // Check if workspace paths exist and are valid\n if (!isValid) {\n issues.push('One or more workspace directories do not exist on the filesystem');\n recommendations.push('Verify workspace directory paths exist and are accessible');\n }\n \n // Check if workspace paths are indexed\n const unindexedPaths: string[] = [];\n const partiallyIndexedPaths: string[] = [];\n \n for (const workspacePath of workspacePaths) {\n // Check if this exact path is indexed\n const exactMatch = indexedDirectoryPaths.includes(workspacePath);\n \n if (exactMatch) {\n // Perfect match - workspace directory is directly indexed\n continue;\n }\n \n // Check if workspace path is covered by a parent directory that is indexed\n const isChildOfIndexed = indexedDirectoryPaths.some(indexedPath => {\n // Workspace path must start with indexed path and be a subdirectory\n return workspacePath.startsWith(indexedPath + '/') || workspacePath.startsWith(indexedPath + '\\\\');\n });\n \n if (isChildOfIndexed) {\n partiallyIndexedPaths.push(workspacePath);\n } else {\n unindexedPaths.push(workspacePath);\n }\n }\n \n // Report unindexed paths\n if (unindexedPaths.length > 0) {\n issues.push(`Workspace directories not indexed: ${unindexedPaths.join(', ')}`);\n recommendations.push(`Run indexing on these directories: ${unindexedPaths.join(', ')}`);\n }\n \n // Report partially indexed paths (where parent directory is indexed)\n if (partiallyIndexedPaths.length > 0) {\n issues.push(`Workspace directories indexed as part of parent directory: ${partiallyIndexedPaths.join(', ')}`);\n recommendations.push('Consider indexing workspace directories directly for better organization');\n }\n \n // Check if workspace is empty (no files found)\n if (isValid && filesCount === 0 && unindexedPaths.length === 0) {\n issues.push('Workspace contains no indexed files');\n recommendations.push('Verify the workspace directory contains files and re-index if necessary');\n }\n \n // Determine overall health status\n let status: 'healthy' | 'warning' | 'error';\n if (!isValid || unindexedPaths.length > 0) {\n status = 'error';\n } else if (issues.length > 0) {\n status = 'warning';\n } else {\n status = 'healthy';\n }\n \n return { status, issues, recommendations };\n}\n\nfunction calculateWorkspaceHealthSummary(workspaces: WorkspaceStatus[]): {\n healthy: number;\n warnings: number; \n errors: number;\n criticalIssues: string[];\n recommendations: string[];\n} {\n let healthy = 0;\n let warnings = 0;\n let errors = 0;\n const criticalIssues: string[] = [];\n const recommendations: string[] = [];\n \n for (const workspace of workspaces) {\n switch (workspace.health.status) {\n case 'healthy':\n healthy++;\n break;\n case 'warning':\n warnings++;\n break;\n case 'error':\n errors++;\n criticalIssues.push(`${workspace.name}: ${workspace.health.issues.join(', ')}`);\n break;\n }\n \n // Collect unique recommendations\n for (const rec of workspace.health.recommendations) {\n if (!recommendations.includes(rec)) {\n recommendations.push(rec);\n }\n }\n }\n \n return {\n healthy,\n warnings,\n errors,\n criticalIssues,\n recommendations\n };\n}\n\nasync function checkQdrantConsistency(sqlite: SQLiteStorage, config: Config): Promise<{ isConsistent: boolean; issues: string[] }> {\n const issues: string[] = [];\n \n try {\n const qdrant = new QdrantClient(config);\n const isHealthy = await qdrant.healthCheck();\n \n if (!isHealthy) {\n issues.push('Qdrant vector database is not running or accessible');\n return { isConsistent: false, issues };\n }\n \n const filesWithChunksStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM files WHERE chunks_json IS NOT NULL');\n const filesWithChunks = filesWithChunksStmt.get() as { count: number };\n \n const totalChunksStmt = sqlite.db.prepare('SELECT SUM(json_array_length(chunks_json)) as count FROM files WHERE chunks_json IS NOT NULL');\n const totalChunks = totalChunksStmt.get() as { count: number | null };\n \n if (filesWithChunks.count > 0 && (totalChunks.count || 0) === 0) {\n issues.push('Files exist but no chunks found - possible data corruption');\n }\n \n const collectionName = config.storage.qdrantCollection;\n try {\n const response = await fetch(`${config.storage.qdrantEndpoint}/collections/${collectionName}`);\n if (!response.ok) {\n issues.push(`Vector collection '${collectionName}' not found (normal during first-time setup)`);\n return { isConsistent: false, issues };\n }\n \n const collectionInfo = await response.json();\n const qdrantPointCount = collectionInfo.result?.points_count || 0;\n const sqliteChunkCount = totalChunks.count || 0;\n \n if (Math.abs(qdrantPointCount - sqliteChunkCount) > 0) {\n if (qdrantPointCount > sqliteChunkCount) {\n issues.push(`Extra vectors in database: ${qdrantPointCount} vectors vs ${sqliteChunkCount} indexed chunks (normal during cleanup)`);\n } else {\n issues.push(`Missing vectors: ${sqliteChunkCount} indexed chunks vs ${qdrantPointCount} vectors (normal during indexing)`);\n }\n }\n } catch (error) {\n issues.push(`Cannot verify vector database status: ${error}`);\n }\n \n } catch (error) {\n issues.push(`Database status check failed: ${error}`);\n }\n \n return {\n isConsistent: issues.length === 0,\n issues\n };\n}\n\nexport async function getIndexStatus(): Promise<IndexStatus> {\n const config = await import('./config.js').then(m => m.loadConfig());\n const sqlite = new SQLiteStorage(config);\n \n try {\n const directoriesStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM directories WHERE status = ?');\n const directoriesCount = directoriesStmt.get('completed') as { count: number };\n \n const filesStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM files');\n const filesCount = filesStmt.get() as { count: number };\n \n const chunksStmt = sqlite.db.prepare('SELECT SUM(json_array_length(chunks_json)) as count FROM files WHERE chunks_json IS NOT NULL');\n const chunksCount = chunksStmt.get() as { count: number | null };\n \n const lastIndexedStmt = sqlite.db.prepare('SELECT MAX(indexed_at) as last_indexed FROM directories WHERE indexed_at > 0');\n const lastIndexedResult = lastIndexedStmt.get() as { last_indexed: number | null };\n \n const errorsStmt = sqlite.db.prepare('SELECT errors_json FROM files WHERE errors_json IS NOT NULL');\n const errorRows = errorsStmt.all() as { errors_json: string }[];\n \n const allErrors: string[] = [];\n errorRows.forEach(row => {\n try {\n const errors = JSON.parse(row.errors_json);\n allErrors.push(...errors);\n } catch {\n allErrors.push('Failed to parse error JSON');\n }\n });\n \n const directoriesDetailStmt = sqlite.db.prepare(`\n SELECT\n d.path,\n d.status,\n d.indexed_at,\n (SELECT COUNT(*) FROM files f WHERE f.path = d.path OR f.path LIKE REPLACE(d.path, '\\\\', '\\\\\\\\') || '/%' ESCAPE '\\\\' OR f.path LIKE REPLACE(d.path, '\\\\', '\\\\\\\\') || '\\\\%' ESCAPE '\\\\') as files_count,\n (SELECT COALESCE(SUM(json_array_length(f.chunks_json)), 0) FROM files f WHERE (f.path = d.path OR f.path LIKE REPLACE(d.path, '\\\\', '\\\\\\\\') || '/%' ESCAPE '\\\\' OR f.path LIKE REPLACE(d.path, '\\\\', '\\\\\\\\') || '\\\\%' ESCAPE '\\\\') AND f.chunks_json IS NOT NULL) as chunks_count\n FROM directories d\n ORDER BY d.indexed_at DESC\n `);\n const directoryDetails = directoriesDetailStmt.all() as { id: number; path: string; status: 'pending' | 'indexing' | 'completed' | 'failed'; indexed_at: number; files_count: number; chunks_count: number }[];\n\n const directories: DirectoryStatus[] = directoryDetails.map(row => {\n const { clause, params } = directoryLikeClause('path', row.path);\n const errorsByDirStmt = sqlite.db.prepare(`\n SELECT errors_json FROM files\n WHERE ${clause} AND errors_json IS NOT NULL\n `);\n const dirErrors = errorsByDirStmt.all(...params) as { errors_json: string }[];\n \n const dirErrorsList: string[] = [];\n dirErrors.forEach(errorRow => {\n try {\n const errors = JSON.parse(errorRow.errors_json);\n dirErrorsList.push(...errors);\n } catch {\n dirErrorsList.push('Failed to parse error JSON');\n }\n });\n \n return {\n path: row.path,\n status: row.status,\n filesCount: row.files_count,\n chunksCount: row.chunks_count,\n lastIndexed: row.indexed_at && row.indexed_at > 0 ? new Date(row.indexed_at).toISOString() : null,\n errors: dirErrorsList\n };\n });\n \n const qdrantConsistency = await checkQdrantConsistency(sqlite, config);\n const workspaces = await calculateWorkspaceStatistics(sqlite, config);\n \n // Calculate workspace health summary\n const workspaceHealth = calculateWorkspaceHealthSummary(workspaces);\n \n const fs = await import('fs');\n let databaseSize = '0 KB';\n try {\n const stats = fs.statSync(config.storage.sqlitePath);\n const sizeInBytes = stats.size;\n if (sizeInBytes > 1024 * 1024) {\n databaseSize = `${(sizeInBytes / (1024 * 1024)).toFixed(2)} MB`;\n } else if (sizeInBytes > 1024) {\n databaseSize = `${(sizeInBytes / 1024).toFixed(2)} KB`;\n } else {\n databaseSize = `${sizeInBytes} bytes`;\n }\n } catch {\n databaseSize = 'Unknown';\n }\n \n return {\n directoriesIndexed: directoriesCount.count,\n filesIndexed: filesCount.count,\n chunksIndexed: chunksCount.count || 0,\n databaseSize,\n lastIndexed: lastIndexedResult.last_indexed ? new Date(lastIndexedResult.last_indexed).toISOString() : null,\n errors: allErrors,\n directories,\n workspaces,\n workspaceHealth,\n qdrantConsistency\n };\n } finally {\n sqlite.close();\n }\n}"],"names":[],"mappings":";;;AASA,SAAS,WAAW,OAAuB;AACzC,SAAO,MAAM,QAAQ,OAAO,MAAM,EAAE,QAAQ,MAAM,KAAK,EAAE,QAAQ,MAAM,KAAK;AAC9E;AASA,SAAS,oBAAoB,QAAgB,SAAuD;AAClG,QAAM,UAAU,WAAW,OAAO;AAClC,SAAO;AAAA,IACL,QAAQ,IAAI,MAAM,WAAW,MAAM,0BAA0B,MAAM;AAAA,IACnE,QAAQ,CAAC,SAAS,GAAG,OAAO,MAAM,GAAG,OAAO,KAAK;AAAA,EAAA;AAErD;AAGA,MAAM,2CAA2B,IAAA;AAK1B,SAAS,kBAAwB;AACtC,aAAW,YAAY,sBAAsB;AAC3C,QAAI;AACF,eAAS,MAAA;AAAA,IACX,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAiCO,MAAM,qBAAqB,MAAM;AAAA,EACtC,YAAY,SAAiC,OAAe;AAC1D,UAAM,OAAO;AAD8B,SAAA,QAAA;AAE3C,SAAK,OAAO;AAAA,EACd;AACF;AAEO,MAAM,aAAa;AAAA,EACxB,YAAoB,QAAgB;AAAhB,SAAA,SAAA;AAAA,EAAiB;AAAA,EAErC,MAAM,cAAgC;AACpC,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,UAAU;AAC5E,aAAO,SAAS;AAAA,IAClB,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAM,mBAAkC;AACtC,UAAM,iBAAiB,KAAK,OAAO,QAAQ;AAE3C,QAAI;AACF,YAAM,gBAAgB,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,gBAAgB,cAAc,EAAE;AACvG,UAAI,cAAc,IAAI;AACpB;AAAA,MACF;AAEA,YAAM,iBAAiB,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,gBAAgB,cAAc,IAAI;AAAA,QACxG,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,QAC3B,MAAM,KAAK,UAAU;AAAA,UACnB,SAAS;AAAA,YACP,MAAM;AAAA,YACN,UAAU;AAAA,UAAA;AAAA,QACZ,CACD;AAAA,MAAA,CACF;AAED,UAAI,CAAC,eAAe,IAAI;AACtB,cAAM,IAAI,MAAM,gCAAgC,eAAe,UAAU,EAAE;AAAA,MAC7E;AAAA,IACF,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,sCAAsC,KAAc;AAAA,IAC7E;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,QAAsC;AACvD,UAAM,iBAAiB,KAAK,OAAO,QAAQ;AAE3C,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,gBAAgB,cAAc,WAAW;AAAA,QACzG,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,QAC3B,MAAM,KAAK,UAAU,EAAE,QAAQ;AAAA,MAAA,CAChC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAA;AACjC,cAAM,IAAI,MAAM,4BAA4B,SAAS,MAAM,IAAI,SAAS,UAAU,MAAM,SAAS,EAAE;AAAA,MACrG;AAAA,IACF,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,qCAAqC,KAAc;AAAA,IAC5E;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,QAAkB,QAAgB,IAAI,QAA0D;AACjH,UAAM,iBAAiB,KAAK,OAAO,QAAQ;AAE3C,QAAI;AACF,YAAM,aAAsC;AAAA,QAC1C;AAAA,QACA;AAAA,QACA,cAAc;AAAA,MAAA;AAGhB,UAAI,QAAQ;AACV,mBAAW,SAAS;AAAA,MACtB;AAEA,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,gBAAgB,cAAc,kBAAkB;AAAA,QAChH,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,QAC3B,MAAM,KAAK,UAAU,UAAU;AAAA,MAAA,CAChC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAA;AACjC,cAAM,IAAI,MAAM,4BAA4B,SAAS,UAAU,MAAM,SAAS,EAAE;AAAA,MAClF;AAEA,YAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,aAAO,KAAK,OAAO,IAAI,CAAC,UAAsG;AAAA,QAC5H,IAAI,KAAK;AAAA,QACT,QAAQ,KAAK;AAAA,QACb,SAAS,KAAK;AAAA,QACd,OAAO,KAAK;AAAA,MAAA,EACZ;AAAA,IACJ,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,qCAAqC,KAAc;AAAA,IAC5E;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,KAAyC;AAC1D,UAAM,iBAAiB,KAAK,OAAO,QAAQ;AAE3C,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,gBAAgB,cAAc,kBAAkB;AAAA,QAChH,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,QAC3B,MAAM,KAAK,UAAU,EAAE,QAAQ,KAAK;AAAA,MAAA,CACrC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,4BAA4B,SAAS,UAAU,EAAE;AAAA,MACnE;AAAA,IACF,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,uCAAuC,KAAc;AAAA,IAC9E;AAAA,EACF;AAAA,EAEA,MAAM,uBAAuB,UAAiC;AAC5D,UAAM,iBAAiB,KAAK,OAAO,QAAQ;AAE3C,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,gBAAgB,cAAc,kBAAkB;AAAA,QAChH,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,QAC3B,MAAM,KAAK,UAAU;AAAA,UACnB,QAAQ;AAAA,YACN,MAAM;AAAA,cACJ;AAAA,gBACE,KAAK;AAAA,gBACL,OAAO,EAAE,OAAO,SAAA;AAAA,cAAS;AAAA,YAC3B;AAAA,UACF;AAAA,QACF,CACD;AAAA,MAAA,CACF;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,YAAY,MAAM,SAAS,KAAA;AACjC,cAAM,IAAI,MAAM,yCAAyC,SAAS,MAAM,IAAI,SAAS,UAAU,MAAM,SAAS,EAAE;AAAA,MAClH;AAAA,IACF,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,oDAAoD,KAAc;AAAA,IAC3F;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,QAAmD;AACnE,UAAM,iBAAiB,KAAK,OAAO,QAAQ;AAE3C,QAAI;AACF,YAAM,YAAqC,EAAE,OAAO,KAAA;AACpD,UAAI,QAAQ;AACV,kBAAU,SAAS;AAAA,MACrB;AAEA,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,gBAAgB,cAAc,iBAAiB;AAAA,QAC/G,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,QAC3B,MAAM,KAAK,UAAU,SAAS;AAAA,MAAA,CAC/B;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,2BAA2B,SAAS,UAAU,EAAE;AAAA,MAClE;AAEA,YAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,aAAO,KAAK,OAAO;AAAA,IACrB,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,oCAAoC,KAAc;AAAA,IAC3E;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,QAAkC,QAAgB,KAA8B;AACjG,UAAM,iBAAiB,KAAK,OAAO,QAAQ;AAE3C,QAAI;AACF,YAAM,aAAsC;AAAA,QAC1C;AAAA,QACA,cAAc;AAAA,QACd,aAAa;AAAA,MAAA;AAEf,UAAI,QAAQ;AACV,mBAAW,SAAS;AAAA,MACtB;AAEA,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,gBAAgB,cAAc,kBAAkB;AAAA,QAChH,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAA;AAAA,QAC3B,MAAM,KAAK,UAAU,UAAU;AAAA,MAAA,CAChC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,4BAA4B,SAAS,UAAU,EAAE;AAAA,MACnE;AAEA,YAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,aAAO,KAAK,OAAO,OAAO,IAAI,CAAC,UAAqE;AAAA,QAClG,IAAI,KAAK;AAAA,QACT,QAAQ,CAAA;AAAA,QACR,SAAS,KAAK;AAAA,QACd,OAAO;AAAA,MAAA,EACP;AAAA,IACJ,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,qCAAqC,KAAc;AAAA,IAC5E;AAAA,EACF;AAAA,EAEA,MAAM,oBAAgE;AACpE,UAAM,iBAAiB,KAAK,OAAO,QAAQ;AAE3C,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,gBAAgB,cAAc,EAAE;AAElG,UAAI,CAAC,SAAS,IAAI;AAChB,YAAI,SAAS,WAAW,KAAK;AAC3B,iBAAO;AAAA,QACT;AACA,cAAM,IAAI,MAAM,kCAAkC,SAAS,UAAU,EAAE;AAAA,MACzE;AAEA,YAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,aAAO;AAAA,QACL,eAAe,KAAK,QAAQ,gBAAgB,KAAK,QAAQ,iBAAiB;AAAA,MAAA;AAAA,IAE9E,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,6CAA6C,KAAc;AAAA,IACpF;AAAA,EACF;AAAA,EAEA,MAAM,mBAAkC;AACtC,UAAM,iBAAiB,KAAK,OAAO,QAAQ;AAE3C,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,QAAQ,cAAc,gBAAgB,cAAc,IAAI;AAAA,QAClG,QAAQ;AAAA,QACR,SAAS,KAAK,OAAO,QAAQ,eAAe;AAAA,UAC1C,WAAW,KAAK,OAAO,QAAQ;AAAA,QAAA,IAC7B,CAAA;AAAA,MAAC,CACN;AAED,UAAI,CAAC,SAAS,MAAM,SAAS,WAAW,KAAK;AAC3C,cAAM,IAAI,MAAM,gCAAgC,SAAS,UAAU,EAAE;AAAA,MACvE;AAAA,IACF,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,2CAA2C,KAAc;AAAA,IAClF;AAAA,EACF;AACF;AAEO,MAAM,cAAc;AAAA,EAGzB,YAAoB,QAAgB;AAAhB,SAAA,SAAA;AAClB,SAAK,KAAK,KAAK,mBAAA;AACf,yBAAqB,IAAI,IAAI;AAAA,EAC/B;AAAA,EALO;AAAA,EAOC,qBAAwC;AAC9C,QAAI;AACF,sBAAgB,QAAQ,KAAK,OAAO,QAAQ,UAAU,CAAC;AAEvD,YAAM,KAAK,IAAI,SAAS,KAAK,OAAO,QAAQ,UAAU;AAEtD,SAAG,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,OAsBP;AAED,SAAG,OAAO,oBAAoB;AAC9B,SAAG,OAAO,qBAAqB;AAE/B,aAAO;AAAA,IACT,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,wCAAwC,KAAc;AAAA,IAC/E;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,MAA+C;AAChE,QAAI;AACF,YAAM,OAAO,KAAK,GAAG,QAAQ,0CAA0C;AACvE,YAAM,MAAM,KAAK,IAAI,IAAI;AAEzB,UAAI,CAAC,IAAK,QAAO;AAEjB,aAAO;AAAA,QACL,IAAI,IAAI;AAAA,QACR,MAAM,IAAI;AAAA,QACV,QAAQ,IAAI;AAAA,QACZ,WAAW,IAAI,KAAK,IAAI,UAAU;AAAA,MAAA;AAAA,IAEtC,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,kCAAkC,KAAc;AAAA,IACzE;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,MAAc,QAAkD;AACpF,QAAI;AACF,YAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,OAG5B;AAED,WAAK,IAAI,MAAM,QAAQ,KAAK,KAAK;AAAA,IACnC,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,qCAAqC,KAAc;AAAA,IAC5E;AAAA,EACF;AAAA,EAEA,MAAM,QAAQ,MAA0C;AACtD,QAAI;AACF,YAAM,OAAO,KAAK,GAAG,QAAQ,oCAAoC;AACjE,YAAM,MAAM,KAAK,IAAI,IAAI;AAEzB,UAAI,CAAC,IAAK,QAAO;AAEjB,aAAO;AAAA,QACL,IAAI,IAAI;AAAA,QACR,MAAM,IAAI;AAAA,QACV,MAAM,IAAI;AAAA,QACV,cAAc,IAAI,KAAK,IAAI,gBAAgB,GAAI;AAAA,QAC/C,MAAM,IAAI;AAAA,QACV,YAAY,KAAK,MAAM,IAAI,WAAW;AAAA,QACtC,QAAQ,IAAI,cAAc,KAAK,MAAM,IAAI,WAAW,IAAI,CAAA;AAAA,QACxD,QAAQ,IAAI,cAAc,KAAK,MAAM,IAAI,WAAW,IAAI;AAAA,MAAA;AAAA,IAE5D,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,6BAA6B,KAAc;AAAA,IACpE;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,UAAoB,SAAsB,CAAA,GAAI,SAAmB,CAAA,GAAmB;AACnG,QAAI;AACF,YAAM,OAAO,KAAK,GAAG,QAAQ;AAAA;AAAA;AAAA,OAG5B;AAED,WAAK;AAAA,QACH,SAAS;AAAA,QACT,SAAS;AAAA,QACT,SAAS,aAAa,QAAA;AAAA,QACtB,SAAS;AAAA,QACT,KAAK,UAAU,SAAS,UAAU;AAAA,QAClC,OAAO,SAAS,IAAI,KAAK,UAAU,MAAM,IAAI;AAAA,QAC7C,OAAO,SAAS,IAAI,KAAK,UAAU,MAAM,IAAI;AAAA,MAAA;AAAA,IAEjD,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,gCAAgC,KAAc;AAAA,IACvE;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,MAA6B;AAC5C,QAAI;AACF,YAAM,OAAO,KAAK,GAAG,QAAQ,kCAAkC;AAC/D,WAAK,IAAI,IAAI;AAAA,IACf,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,gCAAgC,KAAc;AAAA,IACvE;AAAA,EACF;AAAA,EAEA,iBAA2B;AACzB,UAAM,OAAO,KAAK,GAAG,QAAQ,8BAA8B,EAAE,IAAA;AAC7D,WAAO,KAAK,IAAI,CAAA,QAAO,IAAI,IAAI;AAAA,EACjC;AAAA,EAEA,MAAM,oBAAoB,eAA8C;AACtE,QAAI;AACF,YAAM,EAAE,QAAQ,OAAA,IAAW,oBAAoB,QAAQ,aAAa;AACpE,YAAM,OAAO,KAAK,GAAG,QAAQ,6BAA6B,MAAM,EAAE;AAClE,YAAM,OAAO,KAAK,IAAI,GAAG,MAAM;AAE/B,aAAO,KAAK,IAAI,CAAA,SAAQ;AAAA,QACtB,IAAI,IAAI;AAAA,QACR,MAAM,IAAI;AAAA,QACV,MAAM,IAAI;AAAA,QACV,cAAc,IAAI,KAAK,IAAI,gBAAgB,GAAI;AAAA,QAC/C,MAAM,IAAI;AAAA,QACV,YAAY,KAAK,MAAM,IAAI,WAAW;AAAA,QACtC,QAAQ,IAAI,cAAc,KAAK,MAAM,IAAI,WAAW,IAAI,CAAA;AAAA,QACxD,QAAQ,IAAI,cAAc,KAAK,MAAM,IAAI,WAAW,IAAI;AAAA,MAAA,EACxD;AAAA,IACJ,SAAS,OAAO;AACd,YAAM,IAAI,aAAa,oCAAoC,KAAc;AAAA,IAC3E;AAAA,EACF;AAAA,EAEA,gBAAgB,MAAoB;AAClC,SAAK,GAAG,QAAQ,wCAAwC,EAAE,IAAI,IAAI;AAAA,EACpE;AAAA,EAEA,uBAAuB,eAA+B;AACpD,UAAM,EAAE,QAAQ,OAAA,IAAW,oBAAoB,QAAQ,aAAa;AACpE,UAAM,SAAS,KAAK,GAAG,QAAQ,2BAA2B,MAAM,EAAE,EAAE,IAAI,GAAG,MAAM;AACjF,WAAO,OAAO;AAAA,EAChB;AAAA,EAEA,QAAc;AACZ,yBAAqB,OAAO,IAAI;AAChC,SAAK,GAAG,MAAA;AAAA,EACV;AACF;AAEA,eAAsB,kBAAkB,QAA0E;AAChH,QAAM,SAAS,IAAI,cAAc,MAAM;AACvC,QAAM,SAAS,IAAI,aAAa,MAAM;AAEtC,QAAM,OAAO,iBAAA;AAEb,SAAO,EAAE,QAAQ,OAAA;AACnB;AAEA,eAAsB,aAAa,QAA4C;AAC7E,SAAO,IAAI,SAAS,MAAM;AAC5B;AAeA,eAAsB,gBAAgB,QAAqC;AACzE,QAAM,QAAoB;AAAA,IACxB,cAAc;AAAA,IACd,wBAAwB;AAAA,EAAA;AAG1B,MAAI,MAAM,OAAO,IAAI,EAAE,KAAK,CAAA,OAAM,GAAG,WAAW,OAAO,QAAQ,UAAU,CAAC,GAAG;AAC3E,UAAM,eAAe;AACrB,QAAI;AACF,YAAM,YAAY,MAAM,OAAO,IAAI,EAAE,KAAK,CAAA,OAAM,GAAG,SAAS,OAAO,QAAQ,UAAU,CAAC;AACtF,YAAM,YAAY,UAAU,QAAQ,OAAO,OAAO,QAAQ,CAAC;AAC3D,YAAM,aAAa,GAAG,QAAQ;AAAA,IAChC,QAAQ;AACN,YAAM,aAAa;AAAA,IACrB;AAAA,EACF;AAEA,MAAI;AACF,UAAM,SAAS,IAAI,aAAa,MAAM;AACtC,UAAM,YAAY,MAAM,OAAO,YAAA;AAE/B,QAAI,WAAW;AACb,YAAM,iBAAiB,MAAM,OAAO,kBAAA;AACpC,UAAI,gBAAgB;AAClB,cAAM,yBAAyB;AAC/B,cAAM,oBAAoB,eAAe,iBAAiB;AAAA,MAC5D;AAAA,IACF;AAAA,EACF,QAAQ;AAAA,EAER;AAEA,SAAO;AACT;AAEA,eAAsB,cAAc,QAAkC;AACpE,MAAI;AACF,QAAI,CAAC,MAAM,OAAO,IAAI,EAAE,KAAK,CAAA,OAAM,GAAG,WAAW,OAAO,QAAQ,UAAU,CAAC,GAAG;AAC5E,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,aAAa,EAAE,KAAK,CAAA,OAAM,GAAG,OAAO,OAAO,QAAQ,UAAU,CAAC;AAC3E,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI,aAAa,qCAAqC,iBAAiB,QAAQ,MAAM,UAAU,eAAe,IAAI,KAAc;AAAA,EACxI;AACF;AAEA,eAAsB,sBAAsB,QAAkC;AAC5E,MAAI;AACF,UAAM,SAAS,IAAI,aAAa,MAAM;AACtC,UAAM,YAAY,MAAM,OAAO,YAAA;AAE/B,QAAI,CAAC,WAAW;AACd,YAAM,IAAI,aAAa,yBAAyB,OAAO,QAAQ,cAAc,EAAE;AAAA,IACjF;AAEA,UAAM,iBAAiB,MAAM,OAAO,kBAAA;AACpC,QAAI,gBAAgB;AAClB,YAAM,OAAO,iBAAA;AAAA,IACf;AAEA,WAAO;AAAA,EACT,SAAS,OAAO;AACd,UAAM,IAAI,aAAa,sCAAsC,iBAAiB,QAAQ,MAAM,UAAU,eAAe,IAAI,KAAc;AAAA,EACzI;AACF;AA8CA,eAAe,6BAA6B,QAAuB,QAA4C;AAC7G,QAAM,EAAE,wBAAwB,sBAAsB,MAAM,OAAO,aAAa;AAChF,QAAM,aAAgC,CAAA;AAGtC,QAAM,kBAAkB,OAAO,GAAG,QAAQ,sCAAsC;AAChF,QAAM,qBAAqB,gBAAgB,IAAA;AAC3C,QAAM,wBAAwB,mBAAmB,IAAI,CAAA,MAAK,EAAE,IAAI;AAGhE,QAAM,SAAS,IAAI,aAAa,MAAM;AAEtC,aAAW,iBAAiB,uBAAuB,MAAM,GAAG;AAC1D,UAAM,iBAAiB,kBAAkB,QAAQ,aAAa;AAC9D,UAAM,kBAAkB,OAAO,WAAW,aAAa;AAEvD,QAAI,aAAa;AACjB,QAAI,cAAc;AAElB,QAAI,eAAe,SAAS,GAAG;AAC7B,UAAI;AAEF,cAAM,kBAAkB;AAAA,UACtB,MAAM;AAAA,YACJ;AAAA,cACE,KAAK;AAAA,cACL,OAAO,EAAE,KAAK,eAAA;AAAA,YAAe;AAAA,UAC/B;AAAA,QACF;AAIF,sBAAc,MAAM,OAAO,YAAY,eAAe;AAGtD,cAAM,SAAS,MAAM,OAAO,aAAa,iBAAiB,GAAK;AAC/D,cAAM,kBAAkB,IAAI,IAAI,OAAO,IAAI,CAAA,MAAK,EAAE,QAAQ,QAAQ,CAAC;AACnE,qBAAa,gBAAgB;AAAA,MAE/B,QAAQ;AAEN,qBAAa;AACb,sBAAc;AAAA,MAChB;AAAA,IACF;AAGA,UAAM,SAAS,uBAAuB,gBAAgB,gBAAgB,SAAS,YAAY,qBAAqB;AAEhH,eAAW,KAAK;AAAA,MACd,MAAM;AAAA,MACN,OAAO;AAAA,MACP,SAAS,gBAAgB;AAAA,MACzB;AAAA,MACA;AAAA,MACA;AAAA,IAAA,CACD;AAAA,EACH;AAEA,SAAO;AACT;AAEA,SAAS,uBACP,gBACA,SACA,YACA,uBAC0F;AAC1F,QAAM,SAAmB,CAAA;AACzB,QAAM,kBAA4B,CAAA;AAGlC,MAAI,CAAC,SAAS;AACZ,WAAO,KAAK,kEAAkE;AAC9E,oBAAgB,KAAK,2DAA2D;AAAA,EAClF;AAGA,QAAM,iBAA2B,CAAA;AACjC,QAAM,wBAAkC,CAAA;AAExC,aAAW,iBAAiB,gBAAgB;AAE1C,UAAM,aAAa,sBAAsB,SAAS,aAAa;AAE/D,QAAI,YAAY;AAEd;AAAA,IACF;AAGA,UAAM,mBAAmB,sBAAsB,KAAK,CAAA,gBAAe;AAEjE,aAAO,cAAc,WAAW,cAAc,GAAG,KAAK,cAAc,WAAW,cAAc,IAAI;AAAA,IACnG,CAAC;AAED,QAAI,kBAAkB;AACpB,4BAAsB,KAAK,aAAa;AAAA,IAC1C,OAAO;AACL,qBAAe,KAAK,aAAa;AAAA,IACnC;AAAA,EACF;AAGA,MAAI,eAAe,SAAS,GAAG;AAC7B,WAAO,KAAK,sCAAsC,eAAe,KAAK,IAAI,CAAC,EAAE;AAC7E,oBAAgB,KAAK,sCAAsC,eAAe,KAAK,IAAI,CAAC,EAAE;AAAA,EACxF;AAGA,MAAI,sBAAsB,SAAS,GAAG;AACpC,WAAO,KAAK,8DAA8D,sBAAsB,KAAK,IAAI,CAAC,EAAE;AAC5G,oBAAgB,KAAK,0EAA0E;AAAA,EACjG;AAGA,MAAI,WAAW,eAAe,KAAK,eAAe,WAAW,GAAG;AAC9D,WAAO,KAAK,qCAAqC;AACjD,oBAAgB,KAAK,yEAAyE;AAAA,EAChG;AAGA,MAAI;AACJ,MAAI,CAAC,WAAW,eAAe,SAAS,GAAG;AACzC,aAAS;AAAA,EACX,WAAW,OAAO,SAAS,GAAG;AAC5B,aAAS;AAAA,EACX,OAAO;AACL,aAAS;AAAA,EACX;AAEA,SAAO,EAAE,QAAQ,QAAQ,gBAAA;AAC3B;AAEA,SAAS,gCAAgC,YAMvC;AACA,MAAI,UAAU;AACd,MAAI,WAAW;AACf,MAAI,SAAS;AACb,QAAM,iBAA2B,CAAA;AACjC,QAAM,kBAA4B,CAAA;AAElC,aAAW,aAAa,YAAY;AAClC,YAAQ,UAAU,OAAO,QAAA;AAAA,MACvB,KAAK;AACH;AACA;AAAA,MACF,KAAK;AACH;AACA;AAAA,MACF,KAAK;AACH;AACA,uBAAe,KAAK,GAAG,UAAU,IAAI,KAAK,UAAU,OAAO,OAAO,KAAK,IAAI,CAAC,EAAE;AAC9E;AAAA,IAAA;AAIJ,eAAW,OAAO,UAAU,OAAO,iBAAiB;AAClD,UAAI,CAAC,gBAAgB,SAAS,GAAG,GAAG;AAClC,wBAAgB,KAAK,GAAG;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EAAA;AAEJ;AAEA,eAAe,uBAAuB,QAAuB,QAAsE;AACjI,QAAM,SAAmB,CAAA;AAEzB,MAAI;AACF,UAAM,SAAS,IAAI,aAAa,MAAM;AACtC,UAAM,YAAY,MAAM,OAAO,YAAA;AAE/B,QAAI,CAAC,WAAW;AACd,aAAO,KAAK,qDAAqD;AACjE,aAAO,EAAE,cAAc,OAAO,OAAA;AAAA,IAChC;AAEA,UAAM,sBAAsB,OAAO,GAAG,QAAQ,mEAAmE;AACjH,UAAM,kBAAkB,oBAAoB,IAAA;AAE5C,UAAM,kBAAkB,OAAO,GAAG,QAAQ,8FAA8F;AACxI,UAAM,cAAc,gBAAgB,IAAA;AAEpC,QAAI,gBAAgB,QAAQ,MAAM,YAAY,SAAS,OAAO,GAAG;AAC/D,aAAO,KAAK,4DAA4D;AAAA,IAC1E;AAEA,UAAM,iBAAiB,OAAO,QAAQ;AACtC,QAAI;AACF,YAAM,WAAW,MAAM,MAAM,GAAG,OAAO,QAAQ,cAAc,gBAAgB,cAAc,EAAE;AAC7F,UAAI,CAAC,SAAS,IAAI;AAChB,eAAO,KAAK,sBAAsB,cAAc,8CAA8C;AAC9F,eAAO,EAAE,cAAc,OAAO,OAAA;AAAA,MAChC;AAEA,YAAM,iBAAiB,MAAM,SAAS,KAAA;AACtC,YAAM,mBAAmB,eAAe,QAAQ,gBAAgB;AAChE,YAAM,mBAAmB,YAAY,SAAS;AAE9C,UAAI,KAAK,IAAI,mBAAmB,gBAAgB,IAAI,GAAG;AACrD,YAAI,mBAAmB,kBAAkB;AACvC,iBAAO,KAAK,8BAA8B,gBAAgB,eAAe,gBAAgB,yCAAyC;AAAA,QACpI,OAAO;AACL,iBAAO,KAAK,oBAAoB,gBAAgB,sBAAsB,gBAAgB,mCAAmC;AAAA,QAC3H;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,aAAO,KAAK,yCAAyC,KAAK,EAAE;AAAA,IAC9D;AAAA,EAEF,SAAS,OAAO;AACd,WAAO,KAAK,iCAAiC,KAAK,EAAE;AAAA,EACtD;AAEA,SAAO;AAAA,IACL,cAAc,OAAO,WAAW;AAAA,IAChC;AAAA,EAAA;AAEJ;AAEA,eAAsB,iBAAuC;AAC3D,QAAM,SAAS,MAAM,OAAO,aAAa,EAAE,KAAK,CAAA,MAAK,EAAE,YAAY;AACnE,QAAM,SAAS,IAAI,cAAc,MAAM;AAEvC,MAAI;AACF,UAAM,kBAAkB,OAAO,GAAG,QAAQ,4DAA4D;AACtG,UAAM,mBAAmB,gBAAgB,IAAI,WAAW;AAExD,UAAM,YAAY,OAAO,GAAG,QAAQ,qCAAqC;AACzE,UAAM,aAAa,UAAU,IAAA;AAE7B,UAAM,aAAa,OAAO,GAAG,QAAQ,8FAA8F;AACnI,UAAM,cAAc,WAAW,IAAA;AAE/B,UAAM,kBAAkB,OAAO,GAAG,QAAQ,8EAA8E;AACxH,UAAM,oBAAoB,gBAAgB,IAAA;AAE1C,UAAM,aAAa,OAAO,GAAG,QAAQ,6DAA6D;AAClG,UAAM,YAAY,WAAW,IAAA;AAE7B,UAAM,YAAsB,CAAA;AAC5B,cAAU,QAAQ,CAAA,QAAO;AACvB,UAAI;AACF,cAAM,SAAS,KAAK,MAAM,IAAI,WAAW;AACzC,kBAAU,KAAK,GAAG,MAAM;AAAA,MAC1B,QAAQ;AACN,kBAAU,KAAK,4BAA4B;AAAA,MAC7C;AAAA,IACF,CAAC;AAED,UAAM,wBAAwB,OAAO,GAAG,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAS/C;AACD,UAAM,mBAAmB,sBAAsB,IAAA;AAE/C,UAAM,cAAiC,iBAAiB,IAAI,CAAA,QAAO;AACjE,YAAM,EAAE,QAAQ,OAAA,IAAW,oBAAoB,QAAQ,IAAI,IAAI;AAC/D,YAAM,kBAAkB,OAAO,GAAG,QAAQ;AAAA;AAAA,gBAEhC,MAAM;AAAA,OACf;AACD,YAAM,YAAY,gBAAgB,IAAI,GAAG,MAAM;AAE/C,YAAM,gBAA0B,CAAA;AAChC,gBAAU,QAAQ,CAAA,aAAY;AAC5B,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,SAAS,WAAW;AAC9C,wBAAc,KAAK,GAAG,MAAM;AAAA,QAC9B,QAAQ;AACN,wBAAc,KAAK,4BAA4B;AAAA,QACjD;AAAA,MACF,CAAC;AAED,aAAO;AAAA,QACL,MAAM,IAAI;AAAA,QACV,QAAQ,IAAI;AAAA,QACZ,YAAY,IAAI;AAAA,QAChB,aAAa,IAAI;AAAA,QACjB,aAAa,IAAI,cAAc,IAAI,aAAa,IAAI,IAAI,KAAK,IAAI,UAAU,EAAE,YAAA,IAAgB;AAAA,QAC7F,QAAQ;AAAA,MAAA;AAAA,IAEZ,CAAC;AAED,UAAM,oBAAoB,MAAM,uBAAuB,QAAQ,MAAM;AACrE,UAAM,aAAa,MAAM,6BAA6B,QAAQ,MAAM;AAGpE,UAAM,kBAAkB,gCAAgC,UAAU;AAElE,UAAM,KAAK,MAAM,OAAO,IAAI;AAC5B,QAAI,eAAe;AACnB,QAAI;AACF,YAAM,QAAQ,GAAG,SAAS,OAAO,QAAQ,UAAU;AACnD,YAAM,cAAc,MAAM;AAC1B,UAAI,cAAc,OAAO,MAAM;AAC7B,uBAAe,IAAI,eAAe,OAAO,OAAO,QAAQ,CAAC,CAAC;AAAA,MAC5D,WAAW,cAAc,MAAM;AAC7B,uBAAe,IAAI,cAAc,MAAM,QAAQ,CAAC,CAAC;AAAA,MACnD,OAAO;AACL,uBAAe,GAAG,WAAW;AAAA,MAC/B;AAAA,IACF,QAAQ;AACN,qBAAe;AAAA,IACjB;AAEA,WAAO;AAAA,MACL,oBAAoB,iBAAiB;AAAA,MACrC,cAAc,WAAW;AAAA,MACzB,eAAe,YAAY,SAAS;AAAA,MACpC;AAAA,MACA,aAAa,kBAAkB,eAAe,IAAI,KAAK,kBAAkB,YAAY,EAAE,YAAA,IAAgB;AAAA,MACvG,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ,UAAA;AACE,WAAO,MAAA;AAAA,EACT;AACF;"}
|
|
1
|
+
{"version":3,"file":"storage.js","sources":["../src/storage.ts"],"sourcesContent":["import Database from 'better-sqlite3';\nimport { Config } from './config.js';\nimport { FileInfo, ChunkInfo, ensureDirectory } from './utils.js';\nimport { dirname } from 'path';\n\n/**\n * Escape special characters in a string for use in a SQL LIKE pattern.\n * Escapes %, _, and \\ using \\ as the escape character.\n */\nfunction escapeLike(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/%/g, '\\\\%').replace(/_/g, '\\\\_');\n}\n\n/**\n * Build a SQL WHERE clause that matches files within a directory,\n * handling both `/` and `\\` as path separators so queries work\n * regardless of how paths were stored.\n *\n * Returns [clause, ...params] where clause uses `?` placeholders.\n */\nfunction directoryLikeClause(column: string, dirPath: string): { clause: string; params: string[] } {\n const escaped = escapeLike(dirPath);\n return {\n clause: `(${column} = ? OR ${column} LIKE ? ESCAPE '\\\\' OR ${column} LIKE ? ESCAPE '\\\\')`,\n params: [dirPath, `${escaped}/%`, `${escaped}\\\\%`]\n };\n}\n\n// Registry of all open SQLiteStorage instances for graceful shutdown\nconst openStorageInstances = new Set<SQLiteStorage>();\n\n/**\n * Close all open SQLiteStorage instances. Called during graceful shutdown.\n */\nexport function closeAllStorage(): void {\n for (const instance of openStorageInstances) {\n try {\n instance.close();\n } catch {\n // Ignore errors during shutdown cleanup\n }\n }\n}\n\nexport interface DirectoryRecord {\n id: number;\n path: string;\n status: 'pending' | 'indexing' | 'completed' | 'failed';\n indexedAt: Date;\n}\n\nexport interface FileRecord {\n id: number;\n path: string;\n size: number;\n modifiedTime: Date;\n hash: string;\n parentDirs: string[];\n chunks: ChunkInfo[];\n errors?: string[];\n}\n\nexport interface QdrantPoint {\n id: string | number;\n vector: number[];\n payload: {\n filePath: string;\n chunkId: string;\n fileHash: string;\n content: string;\n parentDirectories: string[];\n };\n score?: number;\n}\n\nexport class StorageError extends Error {\n constructor(message: string, public override cause?: Error) {\n super(message);\n this.name = 'StorageError';\n }\n}\n\nexport class QdrantClient {\n constructor(private config: Config) {}\n\n async healthCheck(): Promise<boolean> {\n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/healthz`);\n return response.ok;\n } catch {\n return false;\n }\n }\n\n async createCollection(): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const checkResponse = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`);\n if (checkResponse.ok) {\n return;\n }\n\n const createResponse = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n vectors: {\n size: 768,\n distance: 'Cosine'\n }\n })\n });\n\n if (!createResponse.ok) {\n throw new Error(`Failed to create collection: ${createResponse.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to create Qdrant collection`, error as Error);\n }\n }\n\n async upsertPoints(points: QdrantPoint[]): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ points })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to upsert points: ${response.status} ${response.statusText} - ${errorText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to upsert points to Qdrant`, error as Error);\n }\n }\n\n async searchPoints(vector: number[], limit: number = 10, filter?: Record<string, unknown>): Promise<QdrantPoint[]> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const searchBody: Record<string, unknown> = {\n vector,\n limit,\n with_payload: true\n };\n \n if (filter) {\n searchBody.filter = filter;\n }\n \n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/search`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(searchBody)\n });\n\n if (!response.ok) {\n const errorBody = await response.text();\n throw new Error(`Failed to search points: ${response.statusText} - ${errorBody}`);\n }\n\n const data = await response.json();\n return data.result.map((item: { id: string | number; vector: number[]; payload: Record<string, unknown>; score: number }) => ({\n id: item.id,\n vector: item.vector,\n payload: item.payload,\n score: item.score\n }));\n } catch (error) {\n throw new StorageError(`Failed to search points in Qdrant`, error as Error);\n }\n }\n\n async deletePoints(ids: (string | number)[]): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ points: ids })\n });\n\n if (!response.ok) {\n throw new Error(`Failed to delete points: ${response.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete points from Qdrant`, error as Error);\n }\n }\n\n async deletePointsByFilePath(filePath: string): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/delete`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n filter: {\n must: [\n {\n key: 'filePath',\n match: { value: filePath }\n }\n ]\n }\n })\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Failed to delete points by file path: ${response.status} ${response.statusText} - ${errorText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete points by file path from Qdrant`, error as Error);\n }\n }\n\n async countPoints(filter?: Record<string, unknown>): Promise<number> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const countBody: Record<string, unknown> = { exact: true };\n if (filter) {\n countBody.filter = filter;\n }\n\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/count`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(countBody)\n });\n\n if (!response.ok) {\n throw new Error(`Failed to count points: ${response.statusText}`);\n }\n\n const data = await response.json();\n return data.result.count;\n } catch (error) {\n throw new StorageError(`Failed to count points in Qdrant`, error as Error);\n }\n }\n\n async scrollPoints(filter?: Record<string, unknown>, limit: number = 1000): Promise<QdrantPoint[]> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const scrollBody: Record<string, unknown> = {\n limit,\n with_payload: true,\n with_vector: false\n };\n if (filter) {\n scrollBody.filter = filter;\n }\n\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}/points/scroll`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(scrollBody)\n });\n\n if (!response.ok) {\n throw new Error(`Failed to scroll points: ${response.statusText}`);\n }\n\n const data = await response.json();\n return data.result.points.map((item: { id: string | number; payload: Record<string, unknown> }) => ({\n id: item.id,\n vector: [],\n payload: item.payload,\n score: 0\n }));\n } catch (error) {\n throw new StorageError(`Failed to scroll points in Qdrant`, error as Error);\n }\n }\n\n async getCollectionInfo(): Promise<{ vectors_count?: number } | null> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`);\n \n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`Failed to get collection info: ${response.statusText}`);\n }\n\n const data = await response.json();\n return {\n vectors_count: data.result?.points_count || data.result?.vectors_count || 0\n };\n } catch (error) {\n throw new StorageError(`Failed to get collection info from Qdrant`, error as Error);\n }\n }\n\n async deleteCollection(): Promise<void> {\n const collectionName = this.config.storage.qdrantCollection;\n \n try {\n const response = await fetch(`${this.config.storage.qdrantEndpoint}/collections/${collectionName}`, {\n method: 'DELETE',\n headers: this.config.storage.qdrantApiKey ? {\n 'api-key': this.config.storage.qdrantApiKey\n } : {}\n });\n\n if (!response.ok && response.status !== 404) {\n throw new Error(`Failed to delete collection: ${response.statusText}`);\n }\n } catch (error) {\n throw new StorageError(`Failed to delete collection from Qdrant`, error as Error);\n }\n }\n}\n\nexport class SQLiteStorage {\n public db: Database.Database;\n\n constructor(private config: Config) {\n this.db = this.initializeDatabase();\n openStorageInstances.add(this);\n }\n\n private initializeDatabase(): Database.Database {\n try {\n ensureDirectory(dirname(this.config.storage.sqlitePath));\n \n const db = new Database(this.config.storage.sqlitePath);\n \n db.exec(`\n CREATE TABLE IF NOT EXISTS directories (\n id INTEGER PRIMARY KEY,\n path TEXT UNIQUE NOT NULL,\n status TEXT DEFAULT 'pending',\n indexed_at INTEGER DEFAULT 0\n );\n\n CREATE TABLE IF NOT EXISTS files (\n id INTEGER PRIMARY KEY,\n path TEXT UNIQUE NOT NULL,\n size INTEGER NOT NULL,\n modified_time INTEGER NOT NULL,\n hash TEXT NOT NULL,\n parent_dirs TEXT NOT NULL,\n chunks_json TEXT,\n errors_json TEXT\n );\n\n CREATE INDEX IF NOT EXISTS idx_files_path ON files(path);\n CREATE INDEX IF NOT EXISTS idx_files_hash ON files(hash);\n CREATE INDEX IF NOT EXISTS idx_directories_path ON directories(path);\n `);\n\n db.pragma('journal_mode = WAL');\n db.pragma('busy_timeout = 5000');\n\n return db;\n } catch (error) {\n throw new StorageError(`Failed to initialize SQLite database`, error as Error);\n }\n }\n\n async getDirectory(path: string): Promise<DirectoryRecord | null> {\n try {\n const stmt = this.db.prepare('SELECT * FROM directories WHERE path = ?');\n const row = stmt.get(path) as { id: number; path: string; status: 'pending' | 'indexing' | 'completed' | 'failed'; indexed_at: number } | undefined;\n \n if (!row) return null;\n \n return {\n id: row.id,\n path: row.path,\n status: row.status,\n indexedAt: new Date(row.indexed_at)\n };\n } catch (error) {\n throw new StorageError(`Failed to get directory record`, error as Error);\n }\n }\n\n async upsertDirectory(path: string, status: DirectoryRecord['status']): Promise<void> {\n try {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO directories (path, status, indexed_at)\n VALUES (?, ?, ?)\n `);\n \n stmt.run(path, status, Date.now());\n } catch (error) {\n throw new StorageError(`Failed to upsert directory record`, error as Error);\n }\n }\n\n async getFile(path: string): Promise<FileRecord | null> {\n try {\n const stmt = this.db.prepare('SELECT * FROM files WHERE path = ?');\n const row = stmt.get(path) as { id: number; path: string; size: number; modified_time: number; hash: string; parent_dirs: string; chunks_json: string | null; errors_json: string | null } | undefined;\n \n if (!row) return null;\n \n return {\n id: row.id,\n path: row.path,\n size: row.size,\n modifiedTime: new Date(row.modified_time),\n hash: row.hash,\n parentDirs: JSON.parse(row.parent_dirs),\n chunks: row.chunks_json ? JSON.parse(row.chunks_json) : [],\n errors: row.errors_json ? JSON.parse(row.errors_json) : undefined\n };\n } catch (error) {\n throw new StorageError(`Failed to get file record`, error as Error);\n }\n }\n\n async upsertFile(fileInfo: FileInfo, chunks: ChunkInfo[] = [], errors: string[] = []): Promise<void> {\n try {\n const stmt = this.db.prepare(`\n INSERT OR REPLACE INTO files (path, size, modified_time, hash, parent_dirs, chunks_json, errors_json)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n `);\n \n stmt.run(\n fileInfo.path,\n fileInfo.size,\n fileInfo.modifiedTime.getTime(),\n fileInfo.hash,\n JSON.stringify(fileInfo.parentDirs),\n chunks.length > 0 ? JSON.stringify(chunks) : null,\n errors.length > 0 ? JSON.stringify(errors) : null\n );\n } catch (error) {\n throw new StorageError(`Failed to upsert file record`, error as Error);\n }\n }\n\n async deleteFile(path: string): Promise<void> {\n try {\n const stmt = this.db.prepare('DELETE FROM files WHERE path = ?');\n stmt.run(path);\n } catch (error) {\n throw new StorageError(`Failed to delete file record`, error as Error);\n }\n }\n\n getDirectories(): string[] {\n const rows = this.db.prepare('SELECT path FROM directories').all() as { path: string }[];\n return rows.map(row => row.path);\n }\n\n async getFilesByDirectory(directoryPath: string): Promise<FileRecord[]> {\n try {\n const { clause, params } = directoryLikeClause('path', directoryPath);\n const stmt = this.db.prepare(`SELECT * FROM files WHERE ${clause}`);\n const rows = stmt.all(...params) as { id: number; path: string; size: number; modified_time: number; hash: string; parent_dirs: string; chunks_json: string | null; errors_json: string | null }[];\n\n return rows.map(row => ({\n id: row.id,\n path: row.path,\n size: row.size,\n modifiedTime: new Date(row.modified_time),\n hash: row.hash,\n parentDirs: JSON.parse(row.parent_dirs),\n chunks: row.chunks_json ? JSON.parse(row.chunks_json) : [],\n errors: row.errors_json ? JSON.parse(row.errors_json) : undefined\n }));\n } catch (error) {\n throw new StorageError(`Failed to get files by directory`, error as Error);\n }\n }\n\n deleteDirectory(path: string): void {\n this.db.prepare('DELETE FROM directories WHERE path = ?').run(path);\n }\n\n deleteFilesByDirectory(directoryPath: string): number {\n const { clause, params } = directoryLikeClause('path', directoryPath);\n const result = this.db.prepare(`DELETE FROM files WHERE ${clause}`).run(...params);\n return result.changes;\n }\n\n close(): void {\n openStorageInstances.delete(this);\n this.db.close();\n }\n}\n\nexport async function initializeStorage(config: Config): Promise<{ sqlite: SQLiteStorage; qdrant: QdrantClient }> {\n const sqlite = new SQLiteStorage(config);\n const qdrant = new QdrantClient(config);\n \n await qdrant.createCollection();\n \n return { sqlite, qdrant };\n}\n\nexport async function initDatabase(dbPath: string): Promise<Database.Database> {\n return new Database(dbPath);\n}\n\nexport interface ResetStats {\n sqliteExists: boolean;\n sqliteSize?: string;\n qdrantCollectionExists: boolean;\n qdrantVectorCount?: number;\n}\n\nexport interface ResetResult {\n sqliteDeleted: boolean;\n qdrantDeleted: boolean;\n warnings: string[];\n}\n\nexport async function getResetPreview(config: Config): Promise<ResetStats> {\n const stats: ResetStats = {\n sqliteExists: false,\n qdrantCollectionExists: false\n };\n\n if (await import('fs').then(fs => fs.existsSync(config.storage.sqlitePath))) {\n stats.sqliteExists = true;\n try {\n const fileStats = await import('fs').then(fs => fs.statSync(config.storage.sqlitePath));\n const sizeInMB = (fileStats.size / (1024 * 1024)).toFixed(1);\n stats.sqliteSize = `${sizeInMB} MB`;\n } catch {\n stats.sqliteSize = 'unknown size';\n }\n }\n\n try {\n const qdrant = new QdrantClient(config);\n const isHealthy = await qdrant.healthCheck();\n \n if (isHealthy) {\n const collectionInfo = await qdrant.getCollectionInfo();\n if (collectionInfo) {\n stats.qdrantCollectionExists = true;\n stats.qdrantVectorCount = collectionInfo.vectors_count || 0;\n }\n }\n } catch {\n // Qdrant unavailable\n }\n\n return stats;\n}\n\nexport async function clearDatabase(config: Config): Promise<boolean> {\n try {\n if (!await import('fs').then(fs => fs.existsSync(config.storage.sqlitePath))) {\n return true; // Already clean\n }\n \n await import('fs/promises').then(fs => fs.unlink(config.storage.sqlitePath));\n return true;\n } catch (error) {\n throw new StorageError(`Failed to delete SQLite database: ${error instanceof Error ? error.message : 'Unknown error'}`, error as Error);\n }\n}\n\nexport async function clearVectorCollection(config: Config): Promise<boolean> {\n try {\n const qdrant = new QdrantClient(config);\n const isHealthy = await qdrant.healthCheck();\n \n if (!isHealthy) {\n throw new StorageError(`Qdrant unavailable at ${config.storage.qdrantEndpoint}`);\n }\n \n const collectionInfo = await qdrant.getCollectionInfo();\n if (collectionInfo) {\n await qdrant.deleteCollection();\n }\n \n return true;\n } catch (error) {\n throw new StorageError(`Failed to reset Qdrant collection: ${error instanceof Error ? error.message : 'Unknown error'}`, error as Error);\n }\n}\n\nexport interface DirectoryStatus {\n path: string;\n status: string;\n filesCount: number;\n chunksCount: number;\n lastIndexed: string | null;\n errors: string[];\n}\n\nexport interface WorkspaceStatus {\n name: string;\n paths: string[];\n isValid: boolean;\n filesCount: number;\n chunksCount: number;\n health: {\n status: 'healthy' | 'warning' | 'error';\n issues: string[];\n recommendations: string[];\n };\n}\n\nexport interface IndexStatus {\n directoriesIndexed: number;\n filesIndexed: number;\n chunksIndexed: number;\n databaseSize: string;\n lastIndexed: string | null;\n errors: string[];\n directories: DirectoryStatus[];\n workspaces: WorkspaceStatus[];\n workspaceHealth: {\n healthy: number;\n warnings: number;\n errors: number;\n criticalIssues: string[];\n recommendations: string[];\n };\n qdrantConsistency: {\n isConsistent: boolean;\n issues: string[];\n };\n}\n\nasync function calculateWorkspaceStatistics(sqlite: SQLiteStorage, config: Config): Promise<WorkspaceStatus[]> {\n const { getAvailableWorkspaces, getWorkspacePaths } = await import('./config.js');\n const workspaces: WorkspaceStatus[] = [];\n \n // Get all indexed directories for comparison\n const directoriesStmt = sqlite.db.prepare('SELECT path, status FROM directories');\n const indexedDirectories = directoriesStmt.all() as { path: string; status: string }[];\n const indexedDirectoryPaths = indexedDirectories.map(d => d.path);\n \n // Get Qdrant client for efficient workspace filtering\n const qdrant = new QdrantClient(config);\n \n for (const workspaceName of getAvailableWorkspaces(config)) {\n const workspacePaths = getWorkspacePaths(config, workspaceName);\n const workspaceConfig = config.workspaces[workspaceName];\n \n let filesCount = 0;\n let chunksCount = 0;\n \n if (workspacePaths.length > 0) {\n try {\n // Build workspace filter using same logic as search\n const workspaceFilter = {\n must: [\n {\n key: \"parentDirectories\",\n match: { any: workspacePaths }\n }\n ]\n };\n \n // Get chunk count efficiently using Qdrant\n chunksCount = await qdrant.countPoints(workspaceFilter);\n \n // Get unique file paths using Qdrant scroll\n const points = await qdrant.scrollPoints(workspaceFilter, 10000);\n const uniqueFilePaths = new Set(points.map(p => p.payload.filePath));\n filesCount = uniqueFilePaths.size;\n \n } catch {\n // Fallback to 0 if Qdrant is unavailable\n filesCount = 0;\n chunksCount = 0;\n }\n }\n \n // Check workspace health\n const health = analyzeWorkspaceHealth(workspacePaths, workspaceConfig.isValid, filesCount, indexedDirectoryPaths);\n \n workspaces.push({\n name: workspaceName,\n paths: workspacePaths,\n isValid: workspaceConfig.isValid,\n filesCount,\n chunksCount,\n health\n });\n }\n \n return workspaces;\n}\n\nfunction analyzeWorkspaceHealth(\n workspacePaths: string[], \n isValid: boolean, \n filesCount: number, \n indexedDirectoryPaths: string[]\n): { status: 'healthy' | 'warning' | 'error'; issues: string[]; recommendations: string[] } {\n const issues: string[] = [];\n const recommendations: string[] = [];\n \n // Check if workspace paths exist and are valid\n if (!isValid) {\n issues.push('One or more workspace directories do not exist on the filesystem');\n recommendations.push('Verify workspace directory paths exist and are accessible');\n }\n \n // Check if workspace paths are indexed\n const unindexedPaths: string[] = [];\n const partiallyIndexedPaths: string[] = [];\n \n for (const workspacePath of workspacePaths) {\n // Check if this exact path is indexed\n const exactMatch = indexedDirectoryPaths.includes(workspacePath);\n \n if (exactMatch) {\n // Perfect match - workspace directory is directly indexed\n continue;\n }\n \n // Check if workspace path is covered by a parent directory that is indexed\n const isChildOfIndexed = indexedDirectoryPaths.some(indexedPath => {\n // Workspace path must start with indexed path and be a subdirectory\n return workspacePath.startsWith(indexedPath + '/') || workspacePath.startsWith(indexedPath + '\\\\');\n });\n \n if (isChildOfIndexed) {\n partiallyIndexedPaths.push(workspacePath);\n } else {\n unindexedPaths.push(workspacePath);\n }\n }\n \n // Report unindexed paths\n if (unindexedPaths.length > 0) {\n issues.push(`Workspace directories not indexed: ${unindexedPaths.join(', ')}`);\n recommendations.push(`Run indexing on these directories: ${unindexedPaths.join(', ')}`);\n }\n \n // Report partially indexed paths (where parent directory is indexed)\n if (partiallyIndexedPaths.length > 0) {\n issues.push(`Workspace directories indexed as part of parent directory: ${partiallyIndexedPaths.join(', ')}`);\n recommendations.push('Consider indexing workspace directories directly for better organization');\n }\n \n // Check if workspace is empty (no files found)\n if (isValid && filesCount === 0 && unindexedPaths.length === 0) {\n issues.push('Workspace contains no indexed files');\n recommendations.push('Verify the workspace directory contains files and re-index if necessary');\n }\n \n // Determine overall health status\n let status: 'healthy' | 'warning' | 'error';\n if (!isValid || unindexedPaths.length > 0) {\n status = 'error';\n } else if (issues.length > 0) {\n status = 'warning';\n } else {\n status = 'healthy';\n }\n \n return { status, issues, recommendations };\n}\n\nfunction calculateWorkspaceHealthSummary(workspaces: WorkspaceStatus[]): {\n healthy: number;\n warnings: number; \n errors: number;\n criticalIssues: string[];\n recommendations: string[];\n} {\n let healthy = 0;\n let warnings = 0;\n let errors = 0;\n const criticalIssues: string[] = [];\n const recommendations: string[] = [];\n \n for (const workspace of workspaces) {\n switch (workspace.health.status) {\n case 'healthy':\n healthy++;\n break;\n case 'warning':\n warnings++;\n break;\n case 'error':\n errors++;\n criticalIssues.push(`${workspace.name}: ${workspace.health.issues.join(', ')}`);\n break;\n }\n \n // Collect unique recommendations\n for (const rec of workspace.health.recommendations) {\n if (!recommendations.includes(rec)) {\n recommendations.push(rec);\n }\n }\n }\n \n return {\n healthy,\n warnings,\n errors,\n criticalIssues,\n recommendations\n };\n}\n\nasync function checkQdrantConsistency(sqlite: SQLiteStorage, config: Config): Promise<{ isConsistent: boolean; issues: string[] }> {\n const issues: string[] = [];\n \n try {\n const qdrant = new QdrantClient(config);\n const isHealthy = await qdrant.healthCheck();\n \n if (!isHealthy) {\n issues.push('Qdrant vector database is not running or accessible');\n return { isConsistent: false, issues };\n }\n \n const filesWithChunksStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM files WHERE chunks_json IS NOT NULL');\n const filesWithChunks = filesWithChunksStmt.get() as { count: number };\n \n const totalChunksStmt = sqlite.db.prepare('SELECT SUM(json_array_length(chunks_json)) as count FROM files WHERE chunks_json IS NOT NULL');\n const totalChunks = totalChunksStmt.get() as { count: number | null };\n \n if (filesWithChunks.count > 0 && (totalChunks.count || 0) === 0) {\n issues.push('Files exist but no chunks found - possible data corruption');\n }\n \n const collectionName = config.storage.qdrantCollection;\n try {\n const response = await fetch(`${config.storage.qdrantEndpoint}/collections/${collectionName}`);\n if (!response.ok) {\n issues.push(`Vector collection '${collectionName}' not found (normal during first-time setup)`);\n return { isConsistent: false, issues };\n }\n \n const collectionInfo = await response.json();\n const qdrantPointCount = collectionInfo.result?.points_count || 0;\n const sqliteChunkCount = totalChunks.count || 0;\n \n if (Math.abs(qdrantPointCount - sqliteChunkCount) > 0) {\n if (qdrantPointCount > sqliteChunkCount) {\n issues.push(`Extra vectors in database: ${qdrantPointCount} vectors vs ${sqliteChunkCount} indexed chunks (normal during cleanup)`);\n } else {\n issues.push(`Missing vectors: ${sqliteChunkCount} indexed chunks vs ${qdrantPointCount} vectors (normal during indexing)`);\n }\n }\n } catch (error) {\n issues.push(`Cannot verify vector database status: ${error}`);\n }\n \n } catch (error) {\n issues.push(`Database status check failed: ${error}`);\n }\n \n return {\n isConsistent: issues.length === 0,\n issues\n };\n}\n\nexport async function getIndexStatus(): Promise<IndexStatus> {\n const config = await import('./config.js').then(m => m.loadConfig());\n const sqlite = new SQLiteStorage(config);\n \n try {\n const directoriesStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM directories WHERE status = ?');\n const directoriesCount = directoriesStmt.get('completed') as { count: number };\n \n const filesStmt = sqlite.db.prepare('SELECT COUNT(*) as count FROM files');\n const filesCount = filesStmt.get() as { count: number };\n \n const chunksStmt = sqlite.db.prepare('SELECT SUM(json_array_length(chunks_json)) as count FROM files WHERE chunks_json IS NOT NULL');\n const chunksCount = chunksStmt.get() as { count: number | null };\n \n const lastIndexedStmt = sqlite.db.prepare('SELECT MAX(indexed_at) as last_indexed FROM directories WHERE indexed_at > 0');\n const lastIndexedResult = lastIndexedStmt.get() as { last_indexed: number | null };\n \n const errorsStmt = sqlite.db.prepare('SELECT errors_json FROM files WHERE errors_json IS NOT NULL');\n const errorRows = errorsStmt.all() as { errors_json: string }[];\n \n const allErrors: string[] = [];\n errorRows.forEach(row => {\n try {\n const errors = JSON.parse(row.errors_json);\n allErrors.push(...errors);\n } catch {\n allErrors.push('Failed to parse error JSON');\n }\n });\n \n const directoriesDetailStmt = sqlite.db.prepare(`\n SELECT\n d.path,\n d.status,\n d.indexed_at,\n (SELECT COUNT(*) FROM files f WHERE f.path = d.path OR f.path LIKE REPLACE(d.path, '\\\\', '\\\\\\\\') || '/%' ESCAPE '\\\\' OR f.path LIKE REPLACE(d.path, '\\\\', '\\\\\\\\') || '\\\\%' ESCAPE '\\\\') as files_count,\n (SELECT COALESCE(SUM(json_array_length(f.chunks_json)), 0) FROM files f WHERE (f.path = d.path OR f.path LIKE REPLACE(d.path, '\\\\', '\\\\\\\\') || '/%' ESCAPE '\\\\' OR f.path LIKE REPLACE(d.path, '\\\\', '\\\\\\\\') || '\\\\%' ESCAPE '\\\\') AND f.chunks_json IS NOT NULL) as chunks_count\n FROM directories d\n ORDER BY d.indexed_at DESC\n `);\n const directoryDetails = directoriesDetailStmt.all() as { id: number; path: string; status: 'pending' | 'indexing' | 'completed' | 'failed'; indexed_at: number; files_count: number; chunks_count: number }[];\n\n const directories: DirectoryStatus[] = directoryDetails.map(row => {\n const { clause, params } = directoryLikeClause('path', row.path);\n const errorsByDirStmt = sqlite.db.prepare(`\n SELECT errors_json FROM files\n WHERE ${clause} AND errors_json IS NOT NULL\n `);\n const dirErrors = errorsByDirStmt.all(...params) as { errors_json: string }[];\n \n const dirErrorsList: string[] = [];\n dirErrors.forEach(errorRow => {\n try {\n const errors = JSON.parse(errorRow.errors_json);\n dirErrorsList.push(...errors);\n } catch {\n dirErrorsList.push('Failed to parse error JSON');\n }\n });\n \n return {\n path: row.path,\n status: row.status,\n filesCount: row.files_count,\n chunksCount: row.chunks_count,\n lastIndexed: row.indexed_at && row.indexed_at > 0 ? new Date(row.indexed_at).toISOString() : null,\n errors: dirErrorsList\n };\n });\n \n const qdrantConsistency = await checkQdrantConsistency(sqlite, config);\n const workspaces = await calculateWorkspaceStatistics(sqlite, config);\n \n // Calculate workspace health summary\n const workspaceHealth = calculateWorkspaceHealthSummary(workspaces);\n \n const fs = await import('fs');\n let databaseSize = '0 KB';\n try {\n const stats = fs.statSync(config.storage.sqlitePath);\n const sizeInBytes = stats.size;\n if (sizeInBytes > 1024 * 1024) {\n databaseSize = `${(sizeInBytes / (1024 * 1024)).toFixed(2)} MB`;\n } else if (sizeInBytes > 1024) {\n databaseSize = `${(sizeInBytes / 1024).toFixed(2)} KB`;\n } else {\n databaseSize = `${sizeInBytes} bytes`;\n }\n } catch {\n databaseSize = 'Unknown';\n }\n \n return {\n directoriesIndexed: directoriesCount.count,\n filesIndexed: filesCount.count,\n chunksIndexed: chunksCount.count || 0,\n databaseSize,\n lastIndexed: lastIndexedResult.last_indexed ? new Date(lastIndexedResult.last_indexed).toISOString() : null,\n errors: allErrors,\n directories,\n workspaces,\n workspaceHealth,\n qdrantConsistency\n };\n } finally {\n sqlite.close();\n }\n}"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "directory-indexer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "AI-powered directory indexing with semantic search for MCP servers",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"build": "vite build",
|
|
12
12
|
"dev": "vite build --watch",
|
|
13
13
|
"test": "npm run test:unit",
|
|
14
|
-
"test:unit": "vitest run tests
|
|
14
|
+
"test:unit": "vitest run tests --exclude \"tests/integration/**\" --exclude \"tests/integration.test.ts\"",
|
|
15
15
|
"test:integration": "vitest run tests/integration/",
|
|
16
16
|
"test:all": "vitest run",
|
|
17
17
|
"test:watch": "vitest",
|
|
@@ -42,33 +42,27 @@
|
|
|
42
42
|
"engines": {
|
|
43
43
|
"node": ">=18.0.0"
|
|
44
44
|
},
|
|
45
|
-
"publishConfig": {
|
|
46
|
-
"provenance": true,
|
|
47
|
-
"access": "public"
|
|
48
|
-
},
|
|
49
45
|
"files": [
|
|
50
46
|
"dist/",
|
|
51
47
|
"bin/"
|
|
52
48
|
],
|
|
53
49
|
"dependencies": {
|
|
54
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
55
|
-
"better-sqlite3": "^11.
|
|
56
|
-
"commander": "^
|
|
57
|
-
"ignore": "^7.0.
|
|
58
|
-
"
|
|
59
|
-
"zod": "^3.25.0"
|
|
50
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
51
|
+
"better-sqlite3": "^11.10.0",
|
|
52
|
+
"commander": "^13.1.0",
|
|
53
|
+
"ignore": "^7.0.9",
|
|
54
|
+
"zod": "^4.6.5"
|
|
60
55
|
},
|
|
61
56
|
"devDependencies": {
|
|
62
|
-
"@
|
|
63
|
-
"@types/
|
|
64
|
-
"@types/node": "^
|
|
65
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
66
|
-
"@typescript-eslint/parser": "^8.
|
|
67
|
-
"@vitest/coverage-v8": "^3.2.
|
|
68
|
-
"eslint": "^9.39.
|
|
69
|
-
"tmp": "^0.2.4",
|
|
57
|
+
"@eslint/js": "^9.39.5",
|
|
58
|
+
"@types/better-sqlite3": "^9.6.0",
|
|
59
|
+
"@types/node": "^18.19.130",
|
|
60
|
+
"@typescript-eslint/eslint-plugin": "^8.70.0",
|
|
61
|
+
"@typescript-eslint/parser": "^8.70.0",
|
|
62
|
+
"@vitest/coverage-v8": "^3.2.7",
|
|
63
|
+
"eslint": "^9.39.5",
|
|
70
64
|
"typescript": "^5.9.3",
|
|
71
|
-
"vite": "^6.4.
|
|
72
|
-
"vitest": "^3.2.
|
|
65
|
+
"vite": "^6.4.3",
|
|
66
|
+
"vitest": "^3.2.7"
|
|
73
67
|
}
|
|
74
68
|
}
|