@weavelogic/knowledge-graph-agent 0.7.4 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_virtual/__vite-browser-external.js +2 -2
- package/dist/_virtual/__vite-browser-external.js.map +1 -1
- package/dist/_virtual/browser.js +2 -3
- package/dist/_virtual/browser.js.map +1 -1
- package/dist/_virtual/index10.js +2 -4
- package/dist/_virtual/index10.js.map +1 -1
- package/dist/_virtual/index11.js +2 -2
- package/dist/cli/commands/docs.d.ts.map +1 -1
- package/dist/cli/commands/docs.js +10 -5
- package/dist/cli/commands/docs.js.map +1 -1
- package/dist/cli/commands/hive-mind/add-frontmatter.js +2 -2
- package/dist/cli/commands/hive-mind/add-frontmatter.js.map +1 -1
- package/dist/cli/commands/hive-mind/analyze-links.js +2 -2
- package/dist/cli/commands/hive-mind/analyze-links.js.map +1 -1
- package/dist/cli/commands/hive-mind/find-connections.js +2 -2
- package/dist/cli/commands/hive-mind/find-connections.js.map +1 -1
- package/dist/cli/commands/hive-mind/validate-names.js +2 -2
- package/dist/cli/commands/hive-mind/validate-names.js.map +1 -1
- package/dist/graphql/server.js +2 -2
- package/dist/graphql/server.js.map +1 -1
- package/dist/mcp-server/tools/audit/index.d.ts +4 -0
- package/dist/mcp-server/tools/audit/index.d.ts.map +1 -1
- package/dist/node_modules/@typescript-eslint/project-service/dist/index.js +1 -1
- package/dist/node_modules/debug/src/browser.js +1 -1
- package/dist/node_modules/fdir/dist/index.js +14 -14
- package/dist/node_modules/fdir/dist/index.js.map +1 -1
- package/dist/node_modules/tinyglobby/dist/index.js +14 -14
- package/dist/node_modules/tinyglobby/dist/index.js.map +1 -1
- package/dist/node_modules/ts-api-utils/lib/index.js +1 -1
- package/dist/node_modules/typescript/lib/typescript.js +24 -24
- package/dist/node_modules/typescript/lib/typescript.js.map +1 -1
- package/dist/vector/services/embedding-service.js +1 -7
- package/dist/vector/services/embedding-service.js.map +1 -1
- package/package.json +2 -1
- package/dist/_virtual/browser2.js +0 -5
- package/dist/_virtual/browser2.js.map +0 -1
- package/dist/_virtual/index12.js +0 -5
- package/dist/_virtual/index12.js.map +0 -1
- package/dist/_virtual/ort-web.min.js +0 -8
- package/dist/_virtual/ort-web.min.js.map +0 -1
- package/dist/_virtual/ort-web.min2.js +0 -5
- package/dist/_virtual/ort-web.min2.js.map +0 -1
- package/dist/node_modules/@huggingface/jinja/dist/index.js +0 -118
- package/dist/node_modules/@huggingface/jinja/dist/index.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/backends/onnx.js +0 -24
- package/dist/node_modules/@xenova/transformers/src/backends/onnx.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/configs.js +0 -52
- package/dist/node_modules/@xenova/transformers/src/configs.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/env.js +0 -35
- package/dist/node_modules/@xenova/transformers/src/env.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/models.js +0 -3852
- package/dist/node_modules/@xenova/transformers/src/models.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/tokenizers.js +0 -144
- package/dist/node_modules/@xenova/transformers/src/tokenizers.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/utils/core.js +0 -52
- package/dist/node_modules/@xenova/transformers/src/utils/core.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/utils/generation.js +0 -623
- package/dist/node_modules/@xenova/transformers/src/utils/generation.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/utils/hub.js +0 -395
- package/dist/node_modules/@xenova/transformers/src/utils/hub.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/utils/image.js +0 -12
- package/dist/node_modules/@xenova/transformers/src/utils/image.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/utils/maths.js +0 -89
- package/dist/node_modules/@xenova/transformers/src/utils/maths.js.map +0 -1
- package/dist/node_modules/@xenova/transformers/src/utils/tensor.js +0 -750
- package/dist/node_modules/@xenova/transformers/src/utils/tensor.js.map +0 -1
- package/dist/node_modules/onnxruntime-common/dist/lib/backend-impl.js +0 -67
- package/dist/node_modules/onnxruntime-common/dist/lib/backend-impl.js.map +0 -1
- package/dist/node_modules/onnxruntime-common/dist/lib/env-impl.js +0 -24
- package/dist/node_modules/onnxruntime-common/dist/lib/env-impl.js.map +0 -1
- package/dist/node_modules/onnxruntime-common/dist/lib/env.js +0 -6
- package/dist/node_modules/onnxruntime-common/dist/lib/env.js.map +0 -1
- package/dist/node_modules/onnxruntime-common/dist/lib/index.js +0 -11
- package/dist/node_modules/onnxruntime-common/dist/lib/index.js.map +0 -1
- package/dist/node_modules/onnxruntime-common/dist/lib/inference-session-impl.js +0 -162
- package/dist/node_modules/onnxruntime-common/dist/lib/inference-session-impl.js.map +0 -1
- package/dist/node_modules/onnxruntime-common/dist/lib/inference-session.js +0 -6
- package/dist/node_modules/onnxruntime-common/dist/lib/inference-session.js.map +0 -1
- package/dist/node_modules/onnxruntime-common/dist/lib/tensor-impl.js +0 -393
- package/dist/node_modules/onnxruntime-common/dist/lib/tensor-impl.js.map +0 -1
- package/dist/node_modules/onnxruntime-common/dist/lib/tensor.js +0 -6
- package/dist/node_modules/onnxruntime-common/dist/lib/tensor.js.map +0 -1
- package/dist/node_modules/onnxruntime-web/dist/ort-web.min.js +0 -12919
- package/dist/node_modules/onnxruntime-web/dist/ort-web.min.js.map +0 -1
- package/dist/node_modules/ws/browser.js +0 -16
- package/dist/node_modules/ws/browser.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-connections.js","sources":["../../../../src/cli/commands/hive-mind/find-connections.ts"],"sourcesContent":["/**\n * Hive Mind - Connection Finder\n *\n * Uses TF-IDF similarity to find potential connections between documents.\n * Suggests links for orphan files to reconnect the knowledge graph.\n *\n * SPEC-003: Hive Mind Reconnection Tools\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport * as path from 'path';\nimport { readFile, writeFile } from 'fs/promises';\nimport { glob } from 'fast-glob';\nimport matter from 'gray-matter';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface FindConnectionsOptions {\n threshold?: string;\n suggest?: boolean;\n limit?: string;\n output?: string;\n json?: boolean;\n verbose?: boolean;\n}\n\nexport interface SimilarityMatch {\n source: string;\n target: string;\n similarity: number;\n sharedTerms: string[];\n}\n\nexport interface DocumentVector {\n file: string;\n terms: Map<string, number>;\n magnitude: number;\n}\n\nexport interface ConnectionFinderResult {\n totalDocuments: number;\n suggestedConnections: SimilarityMatch[];\n orphanConnections: SimilarityMatch[];\n termCount: number;\n averageSimilarity: number;\n}\n\n// ============================================================================\n// TF-IDF Implementation\n// ============================================================================\n\n/**\n * Simple tokenizer - splits text into words\n */\nfunction tokenize(text: string): string[] {\n // Remove code blocks\n const noCode = text.replace(/```[\\s\\S]*?```/g, '');\n // Remove inline code\n const noInline = noCode.replace(/`[^`]+`/g, '');\n // Remove URLs\n const noUrls = noInline.replace(/https?:\\/\\/[^\\s]+/g, '');\n // Remove markdown links but keep text\n const noLinks = noUrls.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1');\n // Remove wiki links but keep text\n const noWiki = noLinks.replace(/\\[\\[([^\\]|]+)(?:\\|[^\\]]+)?\\]\\]/g, '$1');\n // Remove special characters and split\n const words = noWiki\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, ' ')\n .split(/\\s+/)\n .filter(word => word.length > 2 && word.length < 30);\n\n return words;\n}\n\n/**\n * Stopwords to filter out\n */\nconst STOPWORDS = new Set([\n 'the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'had', 'her',\n 'was', 'one', 'our', 'out', 'has', 'have', 'been', 'were', 'some', 'this',\n 'that', 'what', 'when', 'where', 'which', 'while', 'who', 'will', 'with',\n 'would', 'there', 'their', 'from', 'they', 'been', 'said', 'each', 'she',\n 'how', 'its', 'may', 'more', 'than', 'then', 'these', 'into', 'only',\n 'other', 'also', 'any', 'such', 'because', 'about', 'just', 'could', 'very',\n]);\n\n/**\n * Filter stopwords\n */\nfunction filterStopwords(words: string[]): string[] {\n return words.filter(word => !STOPWORDS.has(word));\n}\n\n// ============================================================================\n// Connection Finder Class\n// ============================================================================\n\nexport class ConnectionFinder {\n private documents: Map<string, string> = new Map();\n private documentVectors: Map<string, DocumentVector> = new Map();\n private documentFrequency: Map<string, number> = new Map();\n private totalDocuments = 0;\n\n /**\n * Build TF-IDF index from vault\n */\n async buildIndex(vaultPath: string): Promise<void> {\n const resolvedPath = path.resolve(vaultPath);\n\n // Find all markdown files\n const files = await glob('**/*.md', {\n cwd: resolvedPath,\n ignore: ['node_modules/**', '.git/**', 'dist/**'],\n absolute: false,\n });\n\n if (files.length === 0) {\n throw new Error(`No markdown files found in: ${resolvedPath}`);\n }\n\n this.documents.clear();\n this.documentVectors.clear();\n this.documentFrequency.clear();\n this.totalDocuments = files.length;\n\n // First pass: calculate document frequency\n const termDocuments = new Map<string, Set<string>>();\n\n for (const file of files) {\n const filePath = path.join(resolvedPath, file);\n const content = await readFile(filePath, 'utf-8');\n const { content: bodyContent, data: frontmatter } = matter(content);\n\n // Combine content with frontmatter fields\n const fullText = [\n frontmatter.title || '',\n frontmatter.description || '',\n Array.isArray(frontmatter.tags) ? frontmatter.tags.join(' ') : '',\n Array.isArray(frontmatter.aliases) ? frontmatter.aliases.join(' ') : '',\n bodyContent,\n ].join(' ');\n\n this.documents.set(file, fullText);\n\n const tokens = filterStopwords(tokenize(fullText));\n const uniqueTerms = new Set(tokens);\n\n for (const term of uniqueTerms) {\n if (!termDocuments.has(term)) {\n termDocuments.set(term, new Set());\n }\n termDocuments.get(term)!.add(file);\n }\n }\n\n // Store document frequency\n for (const [term, docs] of termDocuments) {\n this.documentFrequency.set(term, docs.size);\n }\n\n // Second pass: calculate TF-IDF vectors\n for (const file of files) {\n const content = this.documents.get(file)!;\n const tokens = filterStopwords(tokenize(content));\n\n // Calculate term frequency\n const termFreq = new Map<string, number>();\n for (const term of tokens) {\n termFreq.set(term, (termFreq.get(term) || 0) + 1);\n }\n\n // Calculate TF-IDF\n const tfidf = new Map<string, number>();\n const maxFreq = Math.max(...termFreq.values(), 1);\n\n for (const [term, freq] of termFreq) {\n const tf = freq / maxFreq; // Normalized TF\n const df = this.documentFrequency.get(term) || 1;\n const idf = Math.log(this.totalDocuments / df);\n tfidf.set(term, tf * idf);\n }\n\n // Calculate magnitude\n let magnitude = 0;\n for (const value of tfidf.values()) {\n magnitude += value * value;\n }\n magnitude = Math.sqrt(magnitude);\n\n this.documentVectors.set(file, {\n file,\n terms: tfidf,\n magnitude,\n });\n }\n }\n\n /**\n * Find similar documents to a source file\n */\n findSimilar(sourceFile: string, threshold = 0.3, limit = 10): SimilarityMatch[] {\n const sourceVector = this.documentVectors.get(sourceFile);\n if (!sourceVector) {\n return [];\n }\n\n const matches: SimilarityMatch[] = [];\n\n for (const [targetFile, targetVector] of this.documentVectors) {\n if (targetFile === sourceFile) continue;\n\n const similarity = this.cosineSimilarity(sourceVector, targetVector);\n\n if (similarity >= threshold) {\n const sharedTerms = this.findSharedTerms(sourceVector, targetVector);\n matches.push({\n source: sourceFile,\n target: targetFile,\n similarity: Math.round(similarity * 1000) / 1000,\n sharedTerms: sharedTerms.slice(0, 5),\n });\n }\n }\n\n // Sort by similarity descending\n matches.sort((a, b) => b.similarity - a.similarity);\n\n return matches.slice(0, limit);\n }\n\n /**\n * Calculate cosine similarity between two document vectors\n */\n private cosineSimilarity(a: DocumentVector, b: DocumentVector): number {\n if (a.magnitude === 0 || b.magnitude === 0) return 0;\n\n let dotProduct = 0;\n for (const [term, aValue] of a.terms) {\n const bValue = b.terms.get(term) || 0;\n dotProduct += aValue * bValue;\n }\n\n return dotProduct / (a.magnitude * b.magnitude);\n }\n\n /**\n * Find shared terms between two documents\n */\n private findSharedTerms(a: DocumentVector, b: DocumentVector): string[] {\n const shared: Array<{ term: string; score: number }> = [];\n\n for (const [term, aValue] of a.terms) {\n const bValue = b.terms.get(term);\n if (bValue && bValue > 0) {\n shared.push({ term, score: aValue * bValue });\n }\n }\n\n // Sort by combined score\n shared.sort((a, b) => b.score - a.score);\n\n return shared.map(s => s.term);\n }\n\n /**\n * Suggest connections for orphan files\n */\n async suggestConnections(\n vaultPath: string,\n orphanFiles: string[],\n threshold = 0.3,\n limit = 5\n ): Promise<SimilarityMatch[]> {\n // Build index if not already built\n if (this.documentVectors.size === 0) {\n await this.buildIndex(vaultPath);\n }\n\n const suggestions: SimilarityMatch[] = [];\n\n for (const orphan of orphanFiles) {\n const similar = this.findSimilar(orphan, threshold, limit);\n suggestions.push(...similar);\n }\n\n // Sort by similarity descending\n suggestions.sort((a, b) => b.similarity - a.similarity);\n\n return suggestions;\n }\n\n /**\n * Find all potential connections above threshold\n */\n async findAllConnections(\n vaultPath: string,\n threshold = 0.3,\n limit = 100\n ): Promise<ConnectionFinderResult> {\n // Build index if not already built\n if (this.documentVectors.size === 0) {\n await this.buildIndex(vaultPath);\n }\n\n const allConnections: SimilarityMatch[] = [];\n const processed = new Set<string>();\n\n for (const sourceFile of this.documentVectors.keys()) {\n const similar = this.findSimilar(sourceFile, threshold, 10);\n for (const match of similar) {\n // Avoid duplicates (A-B and B-A)\n const key = [match.source, match.target].sort().join('|');\n if (!processed.has(key)) {\n processed.add(key);\n allConnections.push(match);\n }\n }\n }\n\n // Sort by similarity descending\n allConnections.sort((a, b) => b.similarity - a.similarity);\n\n const topConnections = allConnections.slice(0, limit);\n const averageSimilarity = topConnections.length > 0\n ? topConnections.reduce((sum, c) => sum + c.similarity, 0) / topConnections.length\n : 0;\n\n return {\n totalDocuments: this.totalDocuments,\n suggestedConnections: topConnections,\n orphanConnections: [],\n termCount: this.documentFrequency.size,\n averageSimilarity: Math.round(averageSimilarity * 1000) / 1000,\n };\n }\n\n /**\n * Get index statistics\n */\n getStats(): { documents: number; terms: number } {\n return {\n documents: this.documentVectors.size,\n terms: this.documentFrequency.size,\n };\n }\n}\n\n// ============================================================================\n// CLI Command\n// ============================================================================\n\nexport function createFindConnectionsCommand(): Command {\n const command = new Command('find-connections')\n .description('Find potential connections using TF-IDF similarity')\n .argument('<vault-path>', 'Path to Obsidian vault or docs directory')\n .option('-t, --threshold <number>', 'Similarity threshold (0-1)', '0.3')\n .option('-l, --limit <number>', 'Maximum connections to return', '50')\n .option('--suggest', 'Focus on orphan files')\n .option('-o, --output <file>', 'Output file for results')\n .option('--json', 'Output as JSON')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (vaultPath: string, options: FindConnectionsOptions) => {\n const finder = new ConnectionFinder();\n const threshold = parseFloat(options.threshold || '0.3');\n const limit = parseInt(options.limit || '50', 10);\n\n console.log(chalk.cyan('\\nBuilding TF-IDF index...\\n'));\n\n try {\n await finder.buildIndex(vaultPath);\n const stats = finder.getStats();\n\n console.log(chalk.white(` Documents indexed: ${stats.documents}`));\n console.log(chalk.white(` Unique terms: ${stats.terms}`));\n console.log('');\n\n const result = await finder.findAllConnections(vaultPath, threshold, limit);\n\n if (options.json) {\n if (options.output) {\n await writeFile(options.output, JSON.stringify(result, null, 2));\n console.log(chalk.green(`Results written to: ${options.output}`));\n } else {\n console.log(JSON.stringify(result, null, 2));\n }\n } else {\n console.log(chalk.bold('Potential Connections Found:\\n'));\n\n if (result.suggestedConnections.length === 0) {\n console.log(chalk.yellow(' No connections found above threshold.'));\n console.log(chalk.gray(' Try lowering the threshold with -t option.\\n'));\n } else {\n for (const conn of result.suggestedConnections.slice(0, options.verbose ? 50 : 20)) {\n const simColor = conn.similarity >= 0.5 ? chalk.green : conn.similarity >= 0.3 ? chalk.yellow : chalk.gray;\n console.log(\n simColor(` [${(conn.similarity * 100).toFixed(1)}%]`),\n chalk.white(conn.source),\n chalk.gray('->'),\n chalk.cyan(conn.target)\n );\n if (options.verbose && conn.sharedTerms.length > 0) {\n console.log(chalk.gray(` Terms: ${conn.sharedTerms.join(', ')}`));\n }\n }\n\n if (result.suggestedConnections.length > (options.verbose ? 50 : 20)) {\n console.log(\n chalk.gray(`\\n ... and ${result.suggestedConnections.length - (options.verbose ? 50 : 20)} more`)\n );\n }\n\n console.log('');\n console.log(chalk.bold('Summary:'));\n console.log(chalk.white(` Total Documents: ${result.totalDocuments}`));\n console.log(chalk.white(` Connections Found: ${result.suggestedConnections.length}`));\n console.log(chalk.white(` Average Similarity: ${(result.averageSimilarity * 100).toFixed(1)}%`));\n console.log('');\n }\n\n // Write to output file if specified\n if (options.output) {\n const reportLines = [\n '# Connection Suggestions\\n',\n `Generated: ${new Date().toISOString()}\\n`,\n `Threshold: ${threshold}\\n`,\n '',\n '## Suggested Links\\n',\n ];\n\n for (const conn of result.suggestedConnections) {\n reportLines.push(`- **${conn.source}** -> [[${conn.target}]]`);\n reportLines.push(` - Similarity: ${(conn.similarity * 100).toFixed(1)}%`);\n if (conn.sharedTerms.length > 0) {\n reportLines.push(` - Shared terms: ${conn.sharedTerms.join(', ')}`);\n }\n }\n\n await writeFile(options.output, reportLines.join('\\n'));\n console.log(chalk.green(`Report written to: ${options.output}`));\n }\n\n // Show next steps\n console.log(chalk.bold('Next Steps:'));\n console.log(chalk.gray(' 1. Review suggested connections'));\n console.log(chalk.gray(' 2. Add [[wiki-links]] to connect related documents'));\n console.log(chalk.gray(' 3. Run kg analyze-links to verify improvement'));\n console.log('');\n }\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\n return command;\n}\n\nexport default createFindConnectionsCommand;\n"],"names":["a","b"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"find-connections.js","sources":["../../../../src/cli/commands/hive-mind/find-connections.ts"],"sourcesContent":["/**\n * Hive Mind - Connection Finder\n *\n * Uses TF-IDF similarity to find potential connections between documents.\n * Suggests links for orphan files to reconnect the knowledge graph.\n *\n * SPEC-003: Hive Mind Reconnection Tools\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport * as path from 'path';\nimport { readFile, writeFile } from 'fs/promises';\nimport fg from 'fast-glob';\nimport matter from 'gray-matter';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface FindConnectionsOptions {\n threshold?: string;\n suggest?: boolean;\n limit?: string;\n output?: string;\n json?: boolean;\n verbose?: boolean;\n}\n\nexport interface SimilarityMatch {\n source: string;\n target: string;\n similarity: number;\n sharedTerms: string[];\n}\n\nexport interface DocumentVector {\n file: string;\n terms: Map<string, number>;\n magnitude: number;\n}\n\nexport interface ConnectionFinderResult {\n totalDocuments: number;\n suggestedConnections: SimilarityMatch[];\n orphanConnections: SimilarityMatch[];\n termCount: number;\n averageSimilarity: number;\n}\n\n// ============================================================================\n// TF-IDF Implementation\n// ============================================================================\n\n/**\n * Simple tokenizer - splits text into words\n */\nfunction tokenize(text: string): string[] {\n // Remove code blocks\n const noCode = text.replace(/```[\\s\\S]*?```/g, '');\n // Remove inline code\n const noInline = noCode.replace(/`[^`]+`/g, '');\n // Remove URLs\n const noUrls = noInline.replace(/https?:\\/\\/[^\\s]+/g, '');\n // Remove markdown links but keep text\n const noLinks = noUrls.replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1');\n // Remove wiki links but keep text\n const noWiki = noLinks.replace(/\\[\\[([^\\]|]+)(?:\\|[^\\]]+)?\\]\\]/g, '$1');\n // Remove special characters and split\n const words = noWiki\n .toLowerCase()\n .replace(/[^a-z0-9\\s-]/g, ' ')\n .split(/\\s+/)\n .filter(word => word.length > 2 && word.length < 30);\n\n return words;\n}\n\n/**\n * Stopwords to filter out\n */\nconst STOPWORDS = new Set([\n 'the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'had', 'her',\n 'was', 'one', 'our', 'out', 'has', 'have', 'been', 'were', 'some', 'this',\n 'that', 'what', 'when', 'where', 'which', 'while', 'who', 'will', 'with',\n 'would', 'there', 'their', 'from', 'they', 'been', 'said', 'each', 'she',\n 'how', 'its', 'may', 'more', 'than', 'then', 'these', 'into', 'only',\n 'other', 'also', 'any', 'such', 'because', 'about', 'just', 'could', 'very',\n]);\n\n/**\n * Filter stopwords\n */\nfunction filterStopwords(words: string[]): string[] {\n return words.filter(word => !STOPWORDS.has(word));\n}\n\n// ============================================================================\n// Connection Finder Class\n// ============================================================================\n\nexport class ConnectionFinder {\n private documents: Map<string, string> = new Map();\n private documentVectors: Map<string, DocumentVector> = new Map();\n private documentFrequency: Map<string, number> = new Map();\n private totalDocuments = 0;\n\n /**\n * Build TF-IDF index from vault\n */\n async buildIndex(vaultPath: string): Promise<void> {\n const resolvedPath = path.resolve(vaultPath);\n\n // Find all markdown files\n const files = await fg('**/*.md', {\n cwd: resolvedPath,\n ignore: ['node_modules/**', '.git/**', 'dist/**'],\n absolute: false,\n });\n\n if (files.length === 0) {\n throw new Error(`No markdown files found in: ${resolvedPath}`);\n }\n\n this.documents.clear();\n this.documentVectors.clear();\n this.documentFrequency.clear();\n this.totalDocuments = files.length;\n\n // First pass: calculate document frequency\n const termDocuments = new Map<string, Set<string>>();\n\n for (const file of files) {\n const filePath = path.join(resolvedPath, file);\n const content = await readFile(filePath, 'utf-8');\n const { content: bodyContent, data: frontmatter } = matter(content);\n\n // Combine content with frontmatter fields\n const fullText = [\n frontmatter.title || '',\n frontmatter.description || '',\n Array.isArray(frontmatter.tags) ? frontmatter.tags.join(' ') : '',\n Array.isArray(frontmatter.aliases) ? frontmatter.aliases.join(' ') : '',\n bodyContent,\n ].join(' ');\n\n this.documents.set(file, fullText);\n\n const tokens = filterStopwords(tokenize(fullText));\n const uniqueTerms = new Set(tokens);\n\n for (const term of uniqueTerms) {\n if (!termDocuments.has(term)) {\n termDocuments.set(term, new Set());\n }\n termDocuments.get(term)!.add(file);\n }\n }\n\n // Store document frequency\n for (const [term, docs] of termDocuments) {\n this.documentFrequency.set(term, docs.size);\n }\n\n // Second pass: calculate TF-IDF vectors\n for (const file of files) {\n const content = this.documents.get(file)!;\n const tokens = filterStopwords(tokenize(content));\n\n // Calculate term frequency\n const termFreq = new Map<string, number>();\n for (const term of tokens) {\n termFreq.set(term, (termFreq.get(term) || 0) + 1);\n }\n\n // Calculate TF-IDF\n const tfidf = new Map<string, number>();\n const maxFreq = Math.max(...termFreq.values(), 1);\n\n for (const [term, freq] of termFreq) {\n const tf = freq / maxFreq; // Normalized TF\n const df = this.documentFrequency.get(term) || 1;\n const idf = Math.log(this.totalDocuments / df);\n tfidf.set(term, tf * idf);\n }\n\n // Calculate magnitude\n let magnitude = 0;\n for (const value of tfidf.values()) {\n magnitude += value * value;\n }\n magnitude = Math.sqrt(magnitude);\n\n this.documentVectors.set(file, {\n file,\n terms: tfidf,\n magnitude,\n });\n }\n }\n\n /**\n * Find similar documents to a source file\n */\n findSimilar(sourceFile: string, threshold = 0.3, limit = 10): SimilarityMatch[] {\n const sourceVector = this.documentVectors.get(sourceFile);\n if (!sourceVector) {\n return [];\n }\n\n const matches: SimilarityMatch[] = [];\n\n for (const [targetFile, targetVector] of this.documentVectors) {\n if (targetFile === sourceFile) continue;\n\n const similarity = this.cosineSimilarity(sourceVector, targetVector);\n\n if (similarity >= threshold) {\n const sharedTerms = this.findSharedTerms(sourceVector, targetVector);\n matches.push({\n source: sourceFile,\n target: targetFile,\n similarity: Math.round(similarity * 1000) / 1000,\n sharedTerms: sharedTerms.slice(0, 5),\n });\n }\n }\n\n // Sort by similarity descending\n matches.sort((a, b) => b.similarity - a.similarity);\n\n return matches.slice(0, limit);\n }\n\n /**\n * Calculate cosine similarity between two document vectors\n */\n private cosineSimilarity(a: DocumentVector, b: DocumentVector): number {\n if (a.magnitude === 0 || b.magnitude === 0) return 0;\n\n let dotProduct = 0;\n for (const [term, aValue] of a.terms) {\n const bValue = b.terms.get(term) || 0;\n dotProduct += aValue * bValue;\n }\n\n return dotProduct / (a.magnitude * b.magnitude);\n }\n\n /**\n * Find shared terms between two documents\n */\n private findSharedTerms(a: DocumentVector, b: DocumentVector): string[] {\n const shared: Array<{ term: string; score: number }> = [];\n\n for (const [term, aValue] of a.terms) {\n const bValue = b.terms.get(term);\n if (bValue && bValue > 0) {\n shared.push({ term, score: aValue * bValue });\n }\n }\n\n // Sort by combined score\n shared.sort((a, b) => b.score - a.score);\n\n return shared.map(s => s.term);\n }\n\n /**\n * Suggest connections for orphan files\n */\n async suggestConnections(\n vaultPath: string,\n orphanFiles: string[],\n threshold = 0.3,\n limit = 5\n ): Promise<SimilarityMatch[]> {\n // Build index if not already built\n if (this.documentVectors.size === 0) {\n await this.buildIndex(vaultPath);\n }\n\n const suggestions: SimilarityMatch[] = [];\n\n for (const orphan of orphanFiles) {\n const similar = this.findSimilar(orphan, threshold, limit);\n suggestions.push(...similar);\n }\n\n // Sort by similarity descending\n suggestions.sort((a, b) => b.similarity - a.similarity);\n\n return suggestions;\n }\n\n /**\n * Find all potential connections above threshold\n */\n async findAllConnections(\n vaultPath: string,\n threshold = 0.3,\n limit = 100\n ): Promise<ConnectionFinderResult> {\n // Build index if not already built\n if (this.documentVectors.size === 0) {\n await this.buildIndex(vaultPath);\n }\n\n const allConnections: SimilarityMatch[] = [];\n const processed = new Set<string>();\n\n for (const sourceFile of this.documentVectors.keys()) {\n const similar = this.findSimilar(sourceFile, threshold, 10);\n for (const match of similar) {\n // Avoid duplicates (A-B and B-A)\n const key = [match.source, match.target].sort().join('|');\n if (!processed.has(key)) {\n processed.add(key);\n allConnections.push(match);\n }\n }\n }\n\n // Sort by similarity descending\n allConnections.sort((a, b) => b.similarity - a.similarity);\n\n const topConnections = allConnections.slice(0, limit);\n const averageSimilarity = topConnections.length > 0\n ? topConnections.reduce((sum, c) => sum + c.similarity, 0) / topConnections.length\n : 0;\n\n return {\n totalDocuments: this.totalDocuments,\n suggestedConnections: topConnections,\n orphanConnections: [],\n termCount: this.documentFrequency.size,\n averageSimilarity: Math.round(averageSimilarity * 1000) / 1000,\n };\n }\n\n /**\n * Get index statistics\n */\n getStats(): { documents: number; terms: number } {\n return {\n documents: this.documentVectors.size,\n terms: this.documentFrequency.size,\n };\n }\n}\n\n// ============================================================================\n// CLI Command\n// ============================================================================\n\nexport function createFindConnectionsCommand(): Command {\n const command = new Command('find-connections')\n .description('Find potential connections using TF-IDF similarity')\n .argument('<vault-path>', 'Path to Obsidian vault or docs directory')\n .option('-t, --threshold <number>', 'Similarity threshold (0-1)', '0.3')\n .option('-l, --limit <number>', 'Maximum connections to return', '50')\n .option('--suggest', 'Focus on orphan files')\n .option('-o, --output <file>', 'Output file for results')\n .option('--json', 'Output as JSON')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (vaultPath: string, options: FindConnectionsOptions) => {\n const finder = new ConnectionFinder();\n const threshold = parseFloat(options.threshold || '0.3');\n const limit = parseInt(options.limit || '50', 10);\n\n console.log(chalk.cyan('\\nBuilding TF-IDF index...\\n'));\n\n try {\n await finder.buildIndex(vaultPath);\n const stats = finder.getStats();\n\n console.log(chalk.white(` Documents indexed: ${stats.documents}`));\n console.log(chalk.white(` Unique terms: ${stats.terms}`));\n console.log('');\n\n const result = await finder.findAllConnections(vaultPath, threshold, limit);\n\n if (options.json) {\n if (options.output) {\n await writeFile(options.output, JSON.stringify(result, null, 2));\n console.log(chalk.green(`Results written to: ${options.output}`));\n } else {\n console.log(JSON.stringify(result, null, 2));\n }\n } else {\n console.log(chalk.bold('Potential Connections Found:\\n'));\n\n if (result.suggestedConnections.length === 0) {\n console.log(chalk.yellow(' No connections found above threshold.'));\n console.log(chalk.gray(' Try lowering the threshold with -t option.\\n'));\n } else {\n for (const conn of result.suggestedConnections.slice(0, options.verbose ? 50 : 20)) {\n const simColor = conn.similarity >= 0.5 ? chalk.green : conn.similarity >= 0.3 ? chalk.yellow : chalk.gray;\n console.log(\n simColor(` [${(conn.similarity * 100).toFixed(1)}%]`),\n chalk.white(conn.source),\n chalk.gray('->'),\n chalk.cyan(conn.target)\n );\n if (options.verbose && conn.sharedTerms.length > 0) {\n console.log(chalk.gray(` Terms: ${conn.sharedTerms.join(', ')}`));\n }\n }\n\n if (result.suggestedConnections.length > (options.verbose ? 50 : 20)) {\n console.log(\n chalk.gray(`\\n ... and ${result.suggestedConnections.length - (options.verbose ? 50 : 20)} more`)\n );\n }\n\n console.log('');\n console.log(chalk.bold('Summary:'));\n console.log(chalk.white(` Total Documents: ${result.totalDocuments}`));\n console.log(chalk.white(` Connections Found: ${result.suggestedConnections.length}`));\n console.log(chalk.white(` Average Similarity: ${(result.averageSimilarity * 100).toFixed(1)}%`));\n console.log('');\n }\n\n // Write to output file if specified\n if (options.output) {\n const reportLines = [\n '# Connection Suggestions\\n',\n `Generated: ${new Date().toISOString()}\\n`,\n `Threshold: ${threshold}\\n`,\n '',\n '## Suggested Links\\n',\n ];\n\n for (const conn of result.suggestedConnections) {\n reportLines.push(`- **${conn.source}** -> [[${conn.target}]]`);\n reportLines.push(` - Similarity: ${(conn.similarity * 100).toFixed(1)}%`);\n if (conn.sharedTerms.length > 0) {\n reportLines.push(` - Shared terms: ${conn.sharedTerms.join(', ')}`);\n }\n }\n\n await writeFile(options.output, reportLines.join('\\n'));\n console.log(chalk.green(`Report written to: ${options.output}`));\n }\n\n // Show next steps\n console.log(chalk.bold('Next Steps:'));\n console.log(chalk.gray(' 1. Review suggested connections'));\n console.log(chalk.gray(' 2. Add [[wiki-links]] to connect related documents'));\n console.log(chalk.gray(' 3. Run kg analyze-links to verify improvement'));\n console.log('');\n }\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\n return command;\n}\n\nexport default createFindConnectionsCommand;\n"],"names":["a","b"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { Command } from "commander";
|
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import { writeFile, access, mkdir, rename } from "fs/promises";
|
|
5
|
-
import
|
|
5
|
+
import fg from "fast-glob";
|
|
6
6
|
const SCHEMAS = {
|
|
7
7
|
kebab: {
|
|
8
8
|
name: "kebab-case",
|
|
@@ -46,7 +46,7 @@ class NameValidator {
|
|
|
46
46
|
*/
|
|
47
47
|
async validateVault(vaultPath, options = {}) {
|
|
48
48
|
const resolvedPath = path.resolve(vaultPath);
|
|
49
|
-
const files = await
|
|
49
|
+
const files = await fg("**/*.md", {
|
|
50
50
|
cwd: resolvedPath,
|
|
51
51
|
ignore: ["node_modules/**", ".git/**", "dist/**"],
|
|
52
52
|
absolute: false
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validate-names.js","sources":["../../../../src/cli/commands/hive-mind/validate-names.ts"],"sourcesContent":["/**\n * Hive Mind - Name Validator\n *\n * Validates file naming schema in a vault to ensure consistent, linkable names.\n * Supports kebab-case, lowercase, and other naming conventions.\n *\n * SPEC-003: Hive Mind Reconnection Tools\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport * as path from 'path';\nimport { readdir, rename, access, mkdir, writeFile } from 'fs/promises';\nimport { glob } from 'fast-glob';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ValidateNamesOptions {\n fix?: boolean;\n dryRun?: boolean;\n schema?: string;\n output?: string;\n json?: boolean;\n verbose?: boolean;\n}\n\nexport interface InvalidFile {\n file: string;\n issues: string[];\n suggested: string;\n}\n\nexport interface ValidationResult {\n valid: string[];\n invalid: InvalidFile[];\n statistics: {\n totalFiles: number;\n validCount: number;\n invalidCount: number;\n commonIssues: Map<string, number>;\n };\n}\n\nexport interface NamingSchema {\n name: string;\n patterns: {\n lowercase: boolean;\n separator: '-' | '_' | '.';\n maxLength: number;\n allowedChars: RegExp;\n reservedNames: string[];\n allowMixedSeparators?: boolean;\n };\n}\n\n// ============================================================================\n// Predefined Schemas\n// ============================================================================\n\nconst SCHEMAS: Record<string, NamingSchema> = {\n kebab: {\n name: 'kebab-case',\n patterns: {\n lowercase: true,\n separator: '-',\n maxLength: 100,\n allowedChars: /^[a-z0-9-]+$/,\n reservedNames: ['index', 'readme', 'changelog'],\n },\n },\n snake: {\n name: 'snake_case',\n patterns: {\n lowercase: true,\n separator: '_',\n maxLength: 100,\n allowedChars: /^[a-z0-9_]+$/,\n reservedNames: ['index', 'readme', 'changelog'],\n },\n },\n obsidian: {\n name: 'Obsidian-friendly',\n patterns: {\n lowercase: false,\n separator: '-',\n maxLength: 200,\n allowedChars: /^[a-zA-Z0-9-_ ]+$/,\n reservedNames: [],\n allowMixedSeparators: true,\n },\n },\n};\n\n// ============================================================================\n// Name Validator Class\n// ============================================================================\n\nexport class NameValidator {\n private schema: NamingSchema;\n\n constructor(schemaName = 'kebab') {\n this.schema = SCHEMAS[schemaName] || SCHEMAS.kebab;\n }\n\n /**\n * Validate all files in a vault\n */\n async validateVault(vaultPath: string, options: ValidateNamesOptions = {}): Promise<ValidationResult> {\n const resolvedPath = path.resolve(vaultPath);\n\n // Find all markdown files\n const files = await glob('**/*.md', {\n cwd: resolvedPath,\n ignore: ['node_modules/**', '.git/**', 'dist/**'],\n absolute: false,\n });\n\n if (files.length === 0) {\n throw new Error(`No markdown files found in: ${resolvedPath}`);\n }\n\n const valid: string[] = [];\n const invalid: InvalidFile[] = [];\n const commonIssues = new Map<string, number>();\n\n for (const file of files) {\n const basename = path.basename(file, '.md');\n const issues = this.validateFilename(basename);\n\n if (issues.length === 0) {\n valid.push(file);\n } else {\n const suggested = this.suggestRename(basename);\n\n invalid.push({\n file,\n issues,\n suggested: file.replace(basename + '.md', suggested + '.md'),\n });\n\n // Count issues\n for (const issue of issues) {\n commonIssues.set(issue, (commonIssues.get(issue) || 0) + 1);\n }\n }\n }\n\n return {\n valid,\n invalid,\n statistics: {\n totalFiles: files.length,\n validCount: valid.length,\n invalidCount: invalid.length,\n commonIssues,\n },\n };\n }\n\n /**\n * Validate a single filename\n */\n validateFilename(filename: string): string[] {\n const issues: string[] = [];\n const { patterns } = this.schema;\n\n // Check for uppercase (if lowercase required)\n if (patterns.lowercase && filename !== filename.toLowerCase()) {\n issues.push('Contains uppercase characters');\n }\n\n // Check for spaces (unless pattern allows spaces)\n const allowsSpaces = patterns.allowedChars.test('a b');\n if (!allowsSpaces && filename.includes(' ')) {\n issues.push('Contains spaces');\n }\n\n // Check for special characters\n const testName = patterns.lowercase\n ? filename.toLowerCase()\n : filename;\n if (!patterns.allowedChars.test(testName)) {\n issues.push(`Contains invalid characters (allowed: ${patterns.allowedChars})`);\n }\n\n // Check length\n if (filename.length > patterns.maxLength) {\n issues.push(`Exceeds maximum length (${patterns.maxLength})`);\n }\n\n // Check for consecutive separators\n const doubleSep = patterns.separator + patterns.separator;\n if (filename.includes(doubleSep)) {\n issues.push(`Contains consecutive separators (${doubleSep})`);\n }\n\n // Check for leading/trailing separators\n if (filename.startsWith(patterns.separator) || filename.endsWith(patterns.separator)) {\n issues.push('Starts or ends with separator');\n }\n\n // Check for underscores when using kebab-case (skip if mixed separators allowed)\n if (!patterns.allowMixedSeparators && patterns.separator === '-' && filename.includes('_')) {\n issues.push('Contains underscores (use hyphens)');\n }\n\n // Check for mixed separators (skip if allowed)\n if (!patterns.allowMixedSeparators) {\n if (patterns.separator === '-' && filename.includes('_')) {\n issues.push('Mixed separators (hyphens and underscores)');\n }\n if (patterns.separator === '_' && filename.includes('-')) {\n issues.push('Mixed separators (underscores and hyphens)');\n }\n }\n\n return issues;\n }\n\n /**\n * Suggest a valid filename\n */\n suggestRename(filename: string): string {\n const { patterns } = this.schema;\n\n let suggested = filename;\n\n // Convert to lowercase if required\n if (patterns.lowercase) {\n suggested = suggested.toLowerCase();\n }\n\n // Replace spaces with separator\n suggested = suggested.replace(/\\s+/g, patterns.separator);\n\n // Replace underscores with separator (for kebab-case)\n if (patterns.separator === '-') {\n suggested = suggested.replace(/_+/g, '-');\n }\n\n // Replace hyphens with separator (for snake_case)\n if (patterns.separator === '_') {\n suggested = suggested.replace(/-+/g, '_');\n }\n\n // Remove invalid characters\n suggested = suggested.replace(/[^a-z0-9-_]/g, '');\n\n // Remove consecutive separators\n const sepRegex = new RegExp(`${patterns.separator}+`, 'g');\n suggested = suggested.replace(sepRegex, patterns.separator);\n\n // Remove leading/trailing separators\n suggested = suggested.replace(new RegExp(`^${patterns.separator}+|${patterns.separator}+$`, 'g'), '');\n\n // Truncate if too long\n if (suggested.length > patterns.maxLength) {\n suggested = suggested.substring(0, patterns.maxLength);\n // Clean up any trailing separator after truncation\n suggested = suggested.replace(new RegExp(`${patterns.separator}+$`, 'g'), '');\n }\n\n return suggested || 'untitled';\n }\n\n /**\n * Rename files (with safety checks)\n */\n async renameFiles(\n vaultPath: string,\n invalidFiles: InvalidFile[],\n dryRun = false\n ): Promise<Array<{ from: string; to: string; success: boolean; error?: string }>> {\n const resolvedPath = path.resolve(vaultPath);\n const results: Array<{ from: string; to: string; success: boolean; error?: string }> = [];\n\n for (const { file, suggested } of invalidFiles) {\n const fromPath = path.join(resolvedPath, file);\n const toPath = path.join(resolvedPath, suggested);\n\n // Check if target already exists\n try {\n await access(toPath);\n results.push({\n from: file,\n to: suggested,\n success: false,\n error: 'Target file already exists',\n });\n continue;\n } catch {\n // Target doesn't exist, we can proceed\n }\n\n if (dryRun) {\n results.push({\n from: file,\n to: suggested,\n success: true,\n });\n } else {\n try {\n // Ensure target directory exists\n await mkdir(path.dirname(toPath), { recursive: true });\n await rename(fromPath, toPath);\n results.push({\n from: file,\n to: suggested,\n success: true,\n });\n } catch (error) {\n results.push({\n from: file,\n to: suggested,\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n }\n }\n }\n\n return results;\n }\n\n /**\n * Get current schema\n */\n getSchema(): NamingSchema {\n return this.schema;\n }\n\n /**\n * Generate report\n */\n generateReport(result: ValidationResult): string {\n const lines: string[] = [];\n\n lines.push('# File Naming Validation Report\\n');\n lines.push(`Generated: ${new Date().toISOString()}\\n`);\n lines.push(`Schema: ${this.schema.name}\\n`);\n lines.push('');\n\n lines.push('## Summary\\n');\n lines.push(`| Metric | Value |`);\n lines.push(`|--------|-------|`);\n lines.push(`| Total Files | ${result.statistics.totalFiles} |`);\n lines.push(`| Valid | ${result.statistics.validCount} |`);\n lines.push(`| Invalid | ${result.statistics.invalidCount} |`);\n lines.push(`| Compliance | ${((result.statistics.validCount / result.statistics.totalFiles) * 100).toFixed(1)}% |`);\n lines.push('');\n\n if (result.statistics.commonIssues.size > 0) {\n lines.push('## Common Issues\\n');\n const sortedIssues = [...result.statistics.commonIssues.entries()]\n .sort((a, b) => b[1] - a[1]);\n for (const [issue, count] of sortedIssues) {\n lines.push(`- ${issue}: ${count} files`);\n }\n lines.push('');\n }\n\n if (result.invalid.length > 0) {\n lines.push('## Files to Rename\\n');\n lines.push('| Current | Suggested | Issues |');\n lines.push('|---------|-----------|--------|');\n for (const { file, suggested, issues } of result.invalid.slice(0, 50)) {\n lines.push(`| \\`${file}\\` | \\`${suggested}\\` | ${issues.join(', ')} |`);\n }\n if (result.invalid.length > 50) {\n lines.push(`\\n*... and ${result.invalid.length - 50} more*`);\n }\n }\n\n return lines.join('\\n');\n }\n}\n\n// ============================================================================\n// CLI Command\n// ============================================================================\n\nexport function createValidateNamesCommand(): Command {\n const command = new Command('validate-names')\n .description('Validate file naming schema')\n .argument('<vault-path>', 'Path to Obsidian vault or docs directory')\n .option('--schema <name>', 'Naming schema (kebab, snake, obsidian)', 'kebab')\n .option('--fix', 'Auto-rename invalid files')\n .option('--dry-run', 'Show what would be renamed without making changes')\n .option('-o, --output <file>', 'Output file for report')\n .option('--json', 'Output as JSON')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (vaultPath: string, options: ValidateNamesOptions) => {\n const validator = new NameValidator(options.schema || 'kebab');\n\n console.log(chalk.cyan(`\\nValidating file names (${validator.getSchema().name})...\\n`));\n\n try {\n const result = await validator.validateVault(vaultPath, options);\n\n if (options.json) {\n const jsonResult = {\n ...result,\n statistics: {\n ...result.statistics,\n commonIssues: Object.fromEntries(result.statistics.commonIssues),\n },\n };\n\n if (options.output) {\n await writeFile(options.output, JSON.stringify(jsonResult, null, 2));\n console.log(chalk.green(`Results written to: ${options.output}`));\n } else {\n console.log(JSON.stringify(jsonResult, null, 2));\n }\n } else {\n // Display summary\n const compliance = (result.statistics.validCount / result.statistics.totalFiles) * 100;\n const complianceColor = compliance === 100 ? chalk.green : compliance >= 80 ? chalk.yellow : chalk.red;\n\n console.log(chalk.bold('Summary:'));\n console.log(chalk.white(` Total Files: ${result.statistics.totalFiles}`));\n console.log(chalk.green(` Valid: ${result.statistics.validCount}`));\n console.log(chalk.red(` Invalid: ${result.statistics.invalidCount}`));\n console.log(complianceColor(` Compliance: ${compliance.toFixed(1)}%`));\n console.log('');\n\n if (result.statistics.commonIssues.size > 0) {\n console.log(chalk.bold('Common Issues:'));\n const sortedIssues = [...result.statistics.commonIssues.entries()]\n .sort((a, b) => b[1] - a[1]);\n for (const [issue, count] of sortedIssues) {\n console.log(chalk.yellow(` ${count}x ${issue}`));\n }\n console.log('');\n }\n\n if (result.invalid.length > 0 && (options.verbose || options.fix || options.dryRun)) {\n console.log(chalk.bold('Files to Rename:'));\n for (const { file, suggested, issues } of result.invalid.slice(0, options.verbose ? 50 : 10)) {\n console.log(chalk.red(` ${file}`));\n console.log(chalk.green(` -> ${suggested}`));\n if (options.verbose) {\n console.log(chalk.gray(` Issues: ${issues.join(', ')}`));\n }\n }\n if (result.invalid.length > (options.verbose ? 50 : 10)) {\n console.log(chalk.gray(` ... and ${result.invalid.length - (options.verbose ? 50 : 10)} more`));\n }\n console.log('');\n }\n\n // Handle fix/dry-run\n if (options.fix || options.dryRun) {\n const action = options.dryRun ? 'Preview' : 'Rename';\n console.log(chalk.bold(`${action}ing files...`));\n\n const renameResults = await validator.renameFiles(\n vaultPath,\n result.invalid,\n options.dryRun || false\n );\n\n const success = renameResults.filter(r => r.success);\n const failed = renameResults.filter(r => !r.success);\n\n if (options.dryRun) {\n console.log(chalk.cyan(` Would rename ${success.length} files`));\n } else {\n console.log(chalk.green(` Renamed ${success.length} files`));\n }\n\n if (failed.length > 0) {\n console.log(chalk.red(` Failed: ${failed.length} files`));\n if (options.verbose) {\n for (const { from, to, error } of failed) {\n console.log(chalk.red(` ${from} -> ${to}: ${error}`));\n }\n }\n }\n console.log('');\n }\n\n // Write report if output specified\n if (options.output && !options.json) {\n const report = validator.generateReport(result);\n await writeFile(options.output, report);\n console.log(chalk.green(`Report written to: ${options.output}`));\n }\n\n // Show next steps\n if (result.invalid.length > 0 && !options.fix) {\n console.log(chalk.bold('Next Steps:'));\n console.log(chalk.gray(' 1. Review invalid file names'));\n console.log(chalk.gray(' 2. Run with --dry-run to preview changes'));\n console.log(chalk.gray(' 3. Run with --fix to auto-rename files'));\n console.log(chalk.gray(' 4. Update links after renaming with kg analyze-links'));\n console.log('');\n }\n }\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\n return command;\n}\n\nexport default createValidateNamesCommand;\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"validate-names.js","sources":["../../../../src/cli/commands/hive-mind/validate-names.ts"],"sourcesContent":["/**\n * Hive Mind - Name Validator\n *\n * Validates file naming schema in a vault to ensure consistent, linkable names.\n * Supports kebab-case, lowercase, and other naming conventions.\n *\n * SPEC-003: Hive Mind Reconnection Tools\n */\n\nimport { Command } from 'commander';\nimport chalk from 'chalk';\nimport * as path from 'path';\nimport { readdir, rename, access, mkdir, writeFile } from 'fs/promises';\nimport fg from 'fast-glob';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ValidateNamesOptions {\n fix?: boolean;\n dryRun?: boolean;\n schema?: string;\n output?: string;\n json?: boolean;\n verbose?: boolean;\n}\n\nexport interface InvalidFile {\n file: string;\n issues: string[];\n suggested: string;\n}\n\nexport interface ValidationResult {\n valid: string[];\n invalid: InvalidFile[];\n statistics: {\n totalFiles: number;\n validCount: number;\n invalidCount: number;\n commonIssues: Map<string, number>;\n };\n}\n\nexport interface NamingSchema {\n name: string;\n patterns: {\n lowercase: boolean;\n separator: '-' | '_' | '.';\n maxLength: number;\n allowedChars: RegExp;\n reservedNames: string[];\n allowMixedSeparators?: boolean;\n };\n}\n\n// ============================================================================\n// Predefined Schemas\n// ============================================================================\n\nconst SCHEMAS: Record<string, NamingSchema> = {\n kebab: {\n name: 'kebab-case',\n patterns: {\n lowercase: true,\n separator: '-',\n maxLength: 100,\n allowedChars: /^[a-z0-9-]+$/,\n reservedNames: ['index', 'readme', 'changelog'],\n },\n },\n snake: {\n name: 'snake_case',\n patterns: {\n lowercase: true,\n separator: '_',\n maxLength: 100,\n allowedChars: /^[a-z0-9_]+$/,\n reservedNames: ['index', 'readme', 'changelog'],\n },\n },\n obsidian: {\n name: 'Obsidian-friendly',\n patterns: {\n lowercase: false,\n separator: '-',\n maxLength: 200,\n allowedChars: /^[a-zA-Z0-9-_ ]+$/,\n reservedNames: [],\n allowMixedSeparators: true,\n },\n },\n};\n\n// ============================================================================\n// Name Validator Class\n// ============================================================================\n\nexport class NameValidator {\n private schema: NamingSchema;\n\n constructor(schemaName = 'kebab') {\n this.schema = SCHEMAS[schemaName] || SCHEMAS.kebab;\n }\n\n /**\n * Validate all files in a vault\n */\n async validateVault(vaultPath: string, options: ValidateNamesOptions = {}): Promise<ValidationResult> {\n const resolvedPath = path.resolve(vaultPath);\n\n // Find all markdown files\n const files = await fg('**/*.md', {\n cwd: resolvedPath,\n ignore: ['node_modules/**', '.git/**', 'dist/**'],\n absolute: false,\n });\n\n if (files.length === 0) {\n throw new Error(`No markdown files found in: ${resolvedPath}`);\n }\n\n const valid: string[] = [];\n const invalid: InvalidFile[] = [];\n const commonIssues = new Map<string, number>();\n\n for (const file of files) {\n const basename = path.basename(file, '.md');\n const issues = this.validateFilename(basename);\n\n if (issues.length === 0) {\n valid.push(file);\n } else {\n const suggested = this.suggestRename(basename);\n\n invalid.push({\n file,\n issues,\n suggested: file.replace(basename + '.md', suggested + '.md'),\n });\n\n // Count issues\n for (const issue of issues) {\n commonIssues.set(issue, (commonIssues.get(issue) || 0) + 1);\n }\n }\n }\n\n return {\n valid,\n invalid,\n statistics: {\n totalFiles: files.length,\n validCount: valid.length,\n invalidCount: invalid.length,\n commonIssues,\n },\n };\n }\n\n /**\n * Validate a single filename\n */\n validateFilename(filename: string): string[] {\n const issues: string[] = [];\n const { patterns } = this.schema;\n\n // Check for uppercase (if lowercase required)\n if (patterns.lowercase && filename !== filename.toLowerCase()) {\n issues.push('Contains uppercase characters');\n }\n\n // Check for spaces (unless pattern allows spaces)\n const allowsSpaces = patterns.allowedChars.test('a b');\n if (!allowsSpaces && filename.includes(' ')) {\n issues.push('Contains spaces');\n }\n\n // Check for special characters\n const testName = patterns.lowercase\n ? filename.toLowerCase()\n : filename;\n if (!patterns.allowedChars.test(testName)) {\n issues.push(`Contains invalid characters (allowed: ${patterns.allowedChars})`);\n }\n\n // Check length\n if (filename.length > patterns.maxLength) {\n issues.push(`Exceeds maximum length (${patterns.maxLength})`);\n }\n\n // Check for consecutive separators\n const doubleSep = patterns.separator + patterns.separator;\n if (filename.includes(doubleSep)) {\n issues.push(`Contains consecutive separators (${doubleSep})`);\n }\n\n // Check for leading/trailing separators\n if (filename.startsWith(patterns.separator) || filename.endsWith(patterns.separator)) {\n issues.push('Starts or ends with separator');\n }\n\n // Check for underscores when using kebab-case (skip if mixed separators allowed)\n if (!patterns.allowMixedSeparators && patterns.separator === '-' && filename.includes('_')) {\n issues.push('Contains underscores (use hyphens)');\n }\n\n // Check for mixed separators (skip if allowed)\n if (!patterns.allowMixedSeparators) {\n if (patterns.separator === '-' && filename.includes('_')) {\n issues.push('Mixed separators (hyphens and underscores)');\n }\n if (patterns.separator === '_' && filename.includes('-')) {\n issues.push('Mixed separators (underscores and hyphens)');\n }\n }\n\n return issues;\n }\n\n /**\n * Suggest a valid filename\n */\n suggestRename(filename: string): string {\n const { patterns } = this.schema;\n\n let suggested = filename;\n\n // Convert to lowercase if required\n if (patterns.lowercase) {\n suggested = suggested.toLowerCase();\n }\n\n // Replace spaces with separator\n suggested = suggested.replace(/\\s+/g, patterns.separator);\n\n // Replace underscores with separator (for kebab-case)\n if (patterns.separator === '-') {\n suggested = suggested.replace(/_+/g, '-');\n }\n\n // Replace hyphens with separator (for snake_case)\n if (patterns.separator === '_') {\n suggested = suggested.replace(/-+/g, '_');\n }\n\n // Remove invalid characters\n suggested = suggested.replace(/[^a-z0-9-_]/g, '');\n\n // Remove consecutive separators\n const sepRegex = new RegExp(`${patterns.separator}+`, 'g');\n suggested = suggested.replace(sepRegex, patterns.separator);\n\n // Remove leading/trailing separators\n suggested = suggested.replace(new RegExp(`^${patterns.separator}+|${patterns.separator}+$`, 'g'), '');\n\n // Truncate if too long\n if (suggested.length > patterns.maxLength) {\n suggested = suggested.substring(0, patterns.maxLength);\n // Clean up any trailing separator after truncation\n suggested = suggested.replace(new RegExp(`${patterns.separator}+$`, 'g'), '');\n }\n\n return suggested || 'untitled';\n }\n\n /**\n * Rename files (with safety checks)\n */\n async renameFiles(\n vaultPath: string,\n invalidFiles: InvalidFile[],\n dryRun = false\n ): Promise<Array<{ from: string; to: string; success: boolean; error?: string }>> {\n const resolvedPath = path.resolve(vaultPath);\n const results: Array<{ from: string; to: string; success: boolean; error?: string }> = [];\n\n for (const { file, suggested } of invalidFiles) {\n const fromPath = path.join(resolvedPath, file);\n const toPath = path.join(resolvedPath, suggested);\n\n // Check if target already exists\n try {\n await access(toPath);\n results.push({\n from: file,\n to: suggested,\n success: false,\n error: 'Target file already exists',\n });\n continue;\n } catch {\n // Target doesn't exist, we can proceed\n }\n\n if (dryRun) {\n results.push({\n from: file,\n to: suggested,\n success: true,\n });\n } else {\n try {\n // Ensure target directory exists\n await mkdir(path.dirname(toPath), { recursive: true });\n await rename(fromPath, toPath);\n results.push({\n from: file,\n to: suggested,\n success: true,\n });\n } catch (error) {\n results.push({\n from: file,\n to: suggested,\n success: false,\n error: error instanceof Error ? error.message : 'Unknown error',\n });\n }\n }\n }\n\n return results;\n }\n\n /**\n * Get current schema\n */\n getSchema(): NamingSchema {\n return this.schema;\n }\n\n /**\n * Generate report\n */\n generateReport(result: ValidationResult): string {\n const lines: string[] = [];\n\n lines.push('# File Naming Validation Report\\n');\n lines.push(`Generated: ${new Date().toISOString()}\\n`);\n lines.push(`Schema: ${this.schema.name}\\n`);\n lines.push('');\n\n lines.push('## Summary\\n');\n lines.push(`| Metric | Value |`);\n lines.push(`|--------|-------|`);\n lines.push(`| Total Files | ${result.statistics.totalFiles} |`);\n lines.push(`| Valid | ${result.statistics.validCount} |`);\n lines.push(`| Invalid | ${result.statistics.invalidCount} |`);\n lines.push(`| Compliance | ${((result.statistics.validCount / result.statistics.totalFiles) * 100).toFixed(1)}% |`);\n lines.push('');\n\n if (result.statistics.commonIssues.size > 0) {\n lines.push('## Common Issues\\n');\n const sortedIssues = [...result.statistics.commonIssues.entries()]\n .sort((a, b) => b[1] - a[1]);\n for (const [issue, count] of sortedIssues) {\n lines.push(`- ${issue}: ${count} files`);\n }\n lines.push('');\n }\n\n if (result.invalid.length > 0) {\n lines.push('## Files to Rename\\n');\n lines.push('| Current | Suggested | Issues |');\n lines.push('|---------|-----------|--------|');\n for (const { file, suggested, issues } of result.invalid.slice(0, 50)) {\n lines.push(`| \\`${file}\\` | \\`${suggested}\\` | ${issues.join(', ')} |`);\n }\n if (result.invalid.length > 50) {\n lines.push(`\\n*... and ${result.invalid.length - 50} more*`);\n }\n }\n\n return lines.join('\\n');\n }\n}\n\n// ============================================================================\n// CLI Command\n// ============================================================================\n\nexport function createValidateNamesCommand(): Command {\n const command = new Command('validate-names')\n .description('Validate file naming schema')\n .argument('<vault-path>', 'Path to Obsidian vault or docs directory')\n .option('--schema <name>', 'Naming schema (kebab, snake, obsidian)', 'kebab')\n .option('--fix', 'Auto-rename invalid files')\n .option('--dry-run', 'Show what would be renamed without making changes')\n .option('-o, --output <file>', 'Output file for report')\n .option('--json', 'Output as JSON')\n .option('-v, --verbose', 'Show detailed output')\n .action(async (vaultPath: string, options: ValidateNamesOptions) => {\n const validator = new NameValidator(options.schema || 'kebab');\n\n console.log(chalk.cyan(`\\nValidating file names (${validator.getSchema().name})...\\n`));\n\n try {\n const result = await validator.validateVault(vaultPath, options);\n\n if (options.json) {\n const jsonResult = {\n ...result,\n statistics: {\n ...result.statistics,\n commonIssues: Object.fromEntries(result.statistics.commonIssues),\n },\n };\n\n if (options.output) {\n await writeFile(options.output, JSON.stringify(jsonResult, null, 2));\n console.log(chalk.green(`Results written to: ${options.output}`));\n } else {\n console.log(JSON.stringify(jsonResult, null, 2));\n }\n } else {\n // Display summary\n const compliance = (result.statistics.validCount / result.statistics.totalFiles) * 100;\n const complianceColor = compliance === 100 ? chalk.green : compliance >= 80 ? chalk.yellow : chalk.red;\n\n console.log(chalk.bold('Summary:'));\n console.log(chalk.white(` Total Files: ${result.statistics.totalFiles}`));\n console.log(chalk.green(` Valid: ${result.statistics.validCount}`));\n console.log(chalk.red(` Invalid: ${result.statistics.invalidCount}`));\n console.log(complianceColor(` Compliance: ${compliance.toFixed(1)}%`));\n console.log('');\n\n if (result.statistics.commonIssues.size > 0) {\n console.log(chalk.bold('Common Issues:'));\n const sortedIssues = [...result.statistics.commonIssues.entries()]\n .sort((a, b) => b[1] - a[1]);\n for (const [issue, count] of sortedIssues) {\n console.log(chalk.yellow(` ${count}x ${issue}`));\n }\n console.log('');\n }\n\n if (result.invalid.length > 0 && (options.verbose || options.fix || options.dryRun)) {\n console.log(chalk.bold('Files to Rename:'));\n for (const { file, suggested, issues } of result.invalid.slice(0, options.verbose ? 50 : 10)) {\n console.log(chalk.red(` ${file}`));\n console.log(chalk.green(` -> ${suggested}`));\n if (options.verbose) {\n console.log(chalk.gray(` Issues: ${issues.join(', ')}`));\n }\n }\n if (result.invalid.length > (options.verbose ? 50 : 10)) {\n console.log(chalk.gray(` ... and ${result.invalid.length - (options.verbose ? 50 : 10)} more`));\n }\n console.log('');\n }\n\n // Handle fix/dry-run\n if (options.fix || options.dryRun) {\n const action = options.dryRun ? 'Preview' : 'Rename';\n console.log(chalk.bold(`${action}ing files...`));\n\n const renameResults = await validator.renameFiles(\n vaultPath,\n result.invalid,\n options.dryRun || false\n );\n\n const success = renameResults.filter(r => r.success);\n const failed = renameResults.filter(r => !r.success);\n\n if (options.dryRun) {\n console.log(chalk.cyan(` Would rename ${success.length} files`));\n } else {\n console.log(chalk.green(` Renamed ${success.length} files`));\n }\n\n if (failed.length > 0) {\n console.log(chalk.red(` Failed: ${failed.length} files`));\n if (options.verbose) {\n for (const { from, to, error } of failed) {\n console.log(chalk.red(` ${from} -> ${to}: ${error}`));\n }\n }\n }\n console.log('');\n }\n\n // Write report if output specified\n if (options.output && !options.json) {\n const report = validator.generateReport(result);\n await writeFile(options.output, report);\n console.log(chalk.green(`Report written to: ${options.output}`));\n }\n\n // Show next steps\n if (result.invalid.length > 0 && !options.fix) {\n console.log(chalk.bold('Next Steps:'));\n console.log(chalk.gray(' 1. Review invalid file names'));\n console.log(chalk.gray(' 2. Run with --dry-run to preview changes'));\n console.log(chalk.gray(' 3. Run with --fix to auto-rename files'));\n console.log(chalk.gray(' 4. Update links after renaming with kg analyze-links'));\n console.log('');\n }\n }\n } catch (error) {\n console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error');\n process.exit(1);\n }\n });\n\n return command;\n}\n\nexport default createValidateNamesCommand;\n"],"names":[],"mappings":"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"}
|
package/dist/graphql/server.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createServer } from "http";
|
|
|
2
2
|
import { existsSync, readFileSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
4
|
import { useServer } from "../node_modules/graphql-ws/lib/use/ws.js";
|
|
5
|
-
import {
|
|
5
|
+
import { WebSocketServer } from "ws";
|
|
6
6
|
import { createLogger } from "../utils/logger.js";
|
|
7
7
|
import { createContextFactory } from "./context.js";
|
|
8
8
|
import { customScalars } from "./scalars.js";
|
|
@@ -92,7 +92,7 @@ function createGraphQLServer(config) {
|
|
|
92
92
|
yoga.handle(req, res);
|
|
93
93
|
});
|
|
94
94
|
if (enableSubscriptions) {
|
|
95
|
-
wsServer = new
|
|
95
|
+
wsServer = new WebSocketServer({
|
|
96
96
|
server: httpServer,
|
|
97
97
|
path: graphqlPath
|
|
98
98
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","sources":["../../src/graphql/server.ts"],"sourcesContent":["/**\n * GraphQL Server Setup\n *\n * Provides GraphQL server configuration using graphql-yoga with WebSocket\n * subscriptions, CORS configuration, health checks, and GraphiQL playground.\n *\n * @module graphql/server\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse, type Server } from 'http';\nimport { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { createYoga, type YogaServerInstance } from 'graphql-yoga';\nimport { useServer } from 'graphql-ws/lib/use/ws';\nimport { WebSocketServer } from 'ws';\nimport { makeExecutableSchema } from '@graphql-tools/schema';\n\nimport { createLogger } from '../utils/index.js';\nimport { createContextFactory, type GraphQLContext, type ContextFactoryConfig } from './context.js';\nimport { customScalars } from './scalars.js';\nimport type { KnowledgeGraphDatabase } from '../core/database.js';\nimport type { AdvancedCache } from '../caching/lru-cache.js';\nimport type { ServiceManager } from '../services/index.js';\n\nconst logger = createLogger('graphql-server');\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * GraphQL server configuration options\n */\nexport interface GraphQLServerConfig {\n /** Port to listen on */\n port: number;\n /** Host to bind to */\n host?: string;\n /** Path for GraphQL endpoint */\n graphqlPath?: string;\n /** Path for health check endpoint */\n healthPath?: string;\n /** Enable GraphiQL playground */\n enableGraphiQL?: boolean;\n /** Enable WebSocket subscriptions */\n enableSubscriptions?: boolean;\n /** CORS configuration */\n cors?: CorsConfig;\n /** Database instance */\n database: KnowledgeGraphDatabase;\n /** Cache instance */\n cache: AdvancedCache<unknown>;\n /** Service manager instance */\n serviceManager: ServiceManager;\n /** API key for authentication (optional) */\n apiKey?: string;\n /** Whether to require authentication */\n requireAuth?: boolean;\n /** Custom resolvers (merged with defaults) */\n resolvers?: Record<string, unknown>;\n /** Custom schema path (default: src/graphql/schema.graphql) */\n schemaPath?: string;\n}\n\n/**\n * CORS configuration options\n */\nexport interface CorsConfig {\n /** Allowed origins (string or array) */\n origin?: string | string[] | boolean;\n /** Allowed methods */\n methods?: string[];\n /** Allowed headers */\n allowedHeaders?: string[];\n /** Exposed headers */\n exposedHeaders?: string[];\n /** Allow credentials */\n credentials?: boolean;\n /** Max age for preflight cache (seconds) */\n maxAge?: number;\n}\n\n/**\n * Health check response\n */\nexport interface HealthCheckResponse {\n status: 'healthy' | 'degraded' | 'unhealthy';\n timestamp: string;\n uptime: number;\n version: string;\n components: {\n database: boolean;\n cache: boolean;\n services: boolean;\n };\n}\n\n/**\n * GraphQL server instance with lifecycle methods\n */\nexport interface GraphQLServerInstance {\n /** Start the server */\n start(): Promise<void>;\n /** Stop the server gracefully */\n stop(): Promise<void>;\n /** Get the HTTP server instance */\n getHttpServer(): Server;\n /** Get the Yoga instance */\n getYoga(): YogaServerInstance<Record<string, unknown>, GraphQLContext>;\n /** Check if server is running */\n isRunning(): boolean;\n /** Get server URL */\n getUrl(): string;\n}\n\n// ============================================================================\n// Server Factory\n// ============================================================================\n\n/**\n * Create a GraphQL server with all configured features\n *\n * @param config - Server configuration\n * @returns GraphQL server instance\n *\n * @example\n * ```typescript\n * const server = createGraphQLServer({\n * port: 4000,\n * database,\n * cache,\n * serviceManager,\n * enableGraphiQL: true,\n * enableSubscriptions: true,\n * cors: {\n * origin: ['http://localhost:3000'],\n * credentials: true,\n * },\n * });\n *\n * await server.start();\n * console.log(`GraphQL server running at ${server.getUrl()}`);\n * ```\n */\nexport function createGraphQLServer(config: GraphQLServerConfig): GraphQLServerInstance {\n const {\n port,\n host = '0.0.0.0',\n graphqlPath = '/graphql',\n healthPath = '/health',\n enableGraphiQL = true,\n enableSubscriptions = true,\n cors = {},\n database,\n cache,\n serviceManager,\n apiKey,\n requireAuth = false,\n resolvers: customResolvers = {},\n schemaPath,\n } = config;\n\n let httpServer: Server | null = null;\n let wsServer: WebSocketServer | null = null;\n let isServerRunning = false;\n const startTime = Date.now();\n\n // Load schema\n const typeDefs = loadSchema(schemaPath);\n\n // Create resolvers with custom scalars\n const resolvers = {\n ...customScalars,\n ...createDefaultResolvers(),\n ...customResolvers,\n };\n\n // Create executable schema\n const schema = makeExecutableSchema({\n typeDefs,\n resolvers,\n });\n\n // Create context factory\n const contextFactory = createContextFactory({\n database,\n cache,\n serviceManager,\n apiKey,\n requireAuth,\n });\n\n // Create CORS configuration\n const corsConfig = buildCorsConfig(cors);\n\n // Create Yoga instance\n const yoga = createYoga<Record<string, unknown>, GraphQLContext>({\n schema,\n context: contextFactory,\n graphqlEndpoint: graphqlPath,\n graphiql: enableGraphiQL\n ? {\n title: 'Knowledge Graph API',\n defaultQuery: DEFAULT_QUERY,\n subscriptionsProtocol: 'WS',\n }\n : false,\n cors: {\n origin: Array.isArray(corsConfig.origin) ? corsConfig.origin : corsConfig.origin === true ? '*' : [],\n methods: corsConfig.methods,\n allowedHeaders: corsConfig.allowedHeaders,\n exposedHeaders: corsConfig.exposedHeaders,\n credentials: corsConfig.credentials,\n maxAge: corsConfig.maxAge,\n },\n logging: {\n debug: (...args) => logger.debug('GraphQL', { args }),\n info: (...args) => logger.info(args.join(' ')),\n warn: (...args) => logger.warn(args.join(' ')),\n error: (...args) => logger.error(args.join(' ')),\n },\n maskedErrors: process.env.NODE_ENV === 'production',\n landingPage: false,\n });\n\n // Create HTTP server with health check\n httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // Handle health check\n if (req.url === healthPath && req.method === 'GET') {\n const health = await getHealthCheck(database, cache, serviceManager, startTime);\n const statusCode = health.status === 'healthy' ? 200 : health.status === 'degraded' ? 200 : 503;\n\n res.writeHead(statusCode, {\n 'Content-Type': 'application/json',\n 'Cache-Control': 'no-cache, no-store, must-revalidate',\n });\n res.end(JSON.stringify(health));\n return;\n }\n\n // Handle CORS preflight\n if (req.method === 'OPTIONS') {\n handleCorsPreFlight(req, res, corsConfig);\n return;\n }\n\n // Delegate to Yoga - yoga.handle returns void and writes directly to response\n yoga.handle(req, res);\n });\n\n // Setup WebSocket server for subscriptions\n if (enableSubscriptions) {\n wsServer = new WebSocketServer({\n server: httpServer,\n path: graphqlPath,\n });\n\n useServer(\n {\n schema,\n context: async (ctx) => {\n // Create context from WebSocket connection\n const request = new Request(`ws://${host}:${port}${graphqlPath}`, {\n headers: ctx.connectionParams as Record<string, string> | undefined,\n });\n return contextFactory({ request });\n },\n onConnect: () => {\n logger.info('WebSocket client connected');\n return true;\n },\n onDisconnect: (ctx, code, reason) => {\n logger.info('WebSocket client disconnected', { code, reason: reason?.toString() });\n },\n onError: (ctx, message, errors) => {\n logger.error('WebSocket error', undefined, { message, errors });\n },\n },\n wsServer\n );\n\n logger.info('WebSocket subscriptions enabled', { path: graphqlPath });\n }\n\n // Return server instance\n return {\n async start(): Promise<void> {\n if (isServerRunning) {\n logger.warn('Server already running');\n return;\n }\n\n return new Promise((resolve, reject) => {\n httpServer!.listen(port, host, () => {\n isServerRunning = true;\n logger.info('GraphQL server started', {\n url: `http://${host}:${port}${graphqlPath}`,\n graphiql: enableGraphiQL ? `http://${host}:${port}${graphqlPath}` : 'disabled',\n health: `http://${host}:${port}${healthPath}`,\n subscriptions: enableSubscriptions ? `ws://${host}:${port}${graphqlPath}` : 'disabled',\n });\n resolve();\n });\n\n httpServer!.on('error', (error) => {\n logger.error('Server error', error);\n reject(error);\n });\n });\n },\n\n async stop(): Promise<void> {\n if (!isServerRunning) {\n return;\n }\n\n logger.info('Stopping GraphQL server...');\n\n // Close WebSocket server\n if (wsServer) {\n await new Promise<void>((resolve) => {\n wsServer!.close(() => {\n logger.debug('WebSocket server closed');\n resolve();\n });\n });\n }\n\n // Close HTTP server\n await new Promise<void>((resolve, reject) => {\n httpServer!.close((error) => {\n if (error) {\n reject(error);\n } else {\n isServerRunning = false;\n logger.info('GraphQL server stopped');\n resolve();\n }\n });\n });\n },\n\n getHttpServer(): Server {\n return httpServer!;\n },\n\n getYoga() {\n return yoga;\n },\n\n isRunning(): boolean {\n return isServerRunning;\n },\n\n getUrl(): string {\n return `http://${host}:${port}${graphqlPath}`;\n },\n };\n}\n\n// ============================================================================\n// Schema Loading\n// ============================================================================\n\n/**\n * Load GraphQL schema from file\n */\nfunction loadSchema(customPath?: string): string {\n // Try custom path first\n if (customPath && existsSync(customPath)) {\n logger.debug('Loading schema from custom path', { path: customPath });\n return readFileSync(customPath, 'utf-8');\n }\n\n // Schema search paths (relative to cwd)\n const searchPaths = [\n join(process.cwd(), 'src/graphql/schema.graphql'),\n join(process.cwd(), 'graphql/schema.graphql'),\n join(process.cwd(), 'schema.graphql'),\n // For dist builds\n join(process.cwd(), 'dist/graphql/schema.graphql'),\n ];\n\n for (const schemaPath of searchPaths) {\n if (existsSync(schemaPath)) {\n logger.debug('Loading schema from path', { path: schemaPath });\n return readFileSync(schemaPath, 'utf-8');\n }\n }\n\n throw new Error(\n `GraphQL schema not found. Tried: ${[customPath, ...searchPaths].filter(Boolean).join(', ')}`\n );\n}\n\n// ============================================================================\n// Default Resolvers\n// ============================================================================\n\n/**\n * Create default resolvers for system queries\n */\nfunction createDefaultResolvers(): Record<string, unknown> {\n return {\n Query: {\n // System health query\n health: async (\n _parent: unknown,\n _args: unknown,\n context: GraphQLContext\n ): Promise<{\n status: string;\n components: { database: boolean; cache: boolean; agents: boolean; vectorStore: boolean };\n uptime: number;\n requestCount: number;\n toolCount: number;\n }> => {\n const { services } = context;\n const dbHealthy = !!services.database;\n const cacheHealthy = !!services.cache;\n\n return {\n status: dbHealthy && cacheHealthy ? 'HEALTHY' : 'DEGRADED',\n components: {\n database: dbHealthy,\n cache: cacheHealthy,\n agents: true, // Would check agent registry\n vectorStore: false, // Not implemented yet\n },\n uptime: process.uptime() * 1000,\n requestCount: 0, // Would track in context\n toolCount: 0, // Would count MCP tools\n };\n },\n\n // Version info query\n version: (): {\n version: string;\n buildTime: Date | null;\n gitCommit: string | null;\n nodeVersion: string;\n schemaVersion: string;\n } => ({\n version: process.env.npm_package_version ?? '0.0.0',\n buildTime: null,\n gitCommit: process.env.GIT_COMMIT ?? null,\n nodeVersion: process.version,\n schemaVersion: '1.0.0',\n }),\n },\n\n // Subscription resolvers would be added here\n Subscription: {},\n };\n}\n\n// ============================================================================\n// CORS Helpers\n// ============================================================================\n\n/**\n * Build CORS configuration for Yoga\n */\nfunction buildCorsConfig(cors: CorsConfig): {\n origin: string[] | boolean;\n methods: string[];\n allowedHeaders: string[];\n exposedHeaders: string[];\n credentials: boolean;\n maxAge: number;\n} {\n const origin = cors.origin === true\n ? true\n : cors.origin === false\n ? false\n : Array.isArray(cors.origin)\n ? cors.origin\n : cors.origin\n ? [cors.origin]\n : ['http://localhost:3000', 'http://localhost:5173']; // Default dashboard origins\n\n return {\n origin: origin === false ? [] : origin,\n methods: cors.methods ?? ['GET', 'POST', 'OPTIONS'],\n allowedHeaders: cors.allowedHeaders ?? ['Content-Type', 'Authorization', 'X-Request-ID'],\n exposedHeaders: cors.exposedHeaders ?? ['X-Request-ID'],\n credentials: cors.credentials ?? true,\n maxAge: cors.maxAge ?? 86400, // 24 hours\n };\n}\n\n/**\n * Handle CORS preflight request\n */\nfunction handleCorsPreFlight(\n req: import('http').IncomingMessage,\n res: import('http').ServerResponse,\n corsConfig: ReturnType<typeof buildCorsConfig>\n): void {\n const origin = req.headers.origin;\n\n if (origin && (corsConfig.origin === true || (Array.isArray(corsConfig.origin) && corsConfig.origin.includes(origin)))) {\n res.setHeader('Access-Control-Allow-Origin', origin);\n }\n\n res.setHeader('Access-Control-Allow-Methods', corsConfig.methods.join(', '));\n res.setHeader('Access-Control-Allow-Headers', corsConfig.allowedHeaders.join(', '));\n res.setHeader('Access-Control-Max-Age', corsConfig.maxAge.toString());\n\n if (corsConfig.credentials) {\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n }\n\n res.writeHead(204);\n res.end();\n}\n\n// ============================================================================\n// Health Check\n// ============================================================================\n\n/**\n * Get health check response\n */\nasync function getHealthCheck(\n database: KnowledgeGraphDatabase,\n cache: AdvancedCache<unknown>,\n serviceManager: ServiceManager,\n startTime: number\n): Promise<HealthCheckResponse> {\n let dbHealthy = false;\n let cacheHealthy = false;\n let servicesHealthy = false;\n\n try {\n // Check database\n const db = database.getDatabase();\n db.prepare('SELECT 1').get();\n dbHealthy = true;\n } catch (error) {\n logger.error('Database health check failed', error instanceof Error ? error : undefined);\n }\n\n try {\n // Check cache\n cache.set('_health_check', Date.now(), { ttl: 1000 });\n const value = cache.get('_health_check');\n cacheHealthy = value !== undefined;\n cache.delete('_health_check');\n } catch (error) {\n logger.error('Cache health check failed', error instanceof Error ? error : undefined);\n }\n\n try {\n // Check services\n const states = serviceManager.getAllStates();\n const runningCount = Array.from(states.values()).filter((s) => s.status === 'running').length;\n servicesHealthy = runningCount >= 0; // At least no errors\n } catch (error) {\n logger.error('Services health check failed', error instanceof Error ? error : undefined);\n }\n\n const allHealthy = dbHealthy && cacheHealthy && servicesHealthy;\n const anyHealthy = dbHealthy || cacheHealthy || servicesHealthy;\n\n return {\n status: allHealthy ? 'healthy' : anyHealthy ? 'degraded' : 'unhealthy',\n timestamp: new Date().toISOString(),\n uptime: Date.now() - startTime,\n version: process.env.npm_package_version ?? '0.0.0',\n components: {\n database: dbHealthy,\n cache: cacheHealthy,\n services: servicesHealthy,\n },\n };\n}\n\n// ============================================================================\n// Default Query for GraphiQL\n// ============================================================================\n\nconst DEFAULT_QUERY = `# Welcome to the Knowledge Graph API\n#\n# Try these example queries:\n\nquery SystemHealth {\n health {\n status\n components {\n database\n cache\n agents\n vectorStore\n }\n uptime\n requestCount\n toolCount\n }\n}\n\nquery SystemVersion {\n version {\n version\n nodeVersion\n schemaVersion\n }\n}\n\n# Uncomment to query graph stats:\n# query GraphStats {\n# graphStats {\n# totalNodes\n# totalEdges\n# nodesByType {\n# type\n# count\n# }\n# orphanNodes\n# avgLinksPerNode\n# }\n# }\n\n# Uncomment to search nodes:\n# query SearchNodes($query: String!) {\n# search(query: $query) {\n# totalMatches\n# nodes {\n# id\n# title\n# type\n# tags\n# }\n# }\n# }\n`;\n"],"names":["WebSocketServer"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","sources":["../../src/graphql/server.ts"],"sourcesContent":["/**\n * GraphQL Server Setup\n *\n * Provides GraphQL server configuration using graphql-yoga with WebSocket\n * subscriptions, CORS configuration, health checks, and GraphiQL playground.\n *\n * @module graphql/server\n */\n\nimport { createServer, type IncomingMessage, type ServerResponse, type Server } from 'http';\nimport { readFileSync, existsSync } from 'fs';\nimport { join } from 'path';\nimport { createYoga, type YogaServerInstance } from 'graphql-yoga';\nimport { useServer } from 'graphql-ws/lib/use/ws';\nimport { WebSocketServer } from 'ws';\nimport { makeExecutableSchema } from '@graphql-tools/schema';\n\nimport { createLogger } from '../utils/index.js';\nimport { createContextFactory, type GraphQLContext, type ContextFactoryConfig } from './context.js';\nimport { customScalars } from './scalars.js';\nimport type { KnowledgeGraphDatabase } from '../core/database.js';\nimport type { AdvancedCache } from '../caching/lru-cache.js';\nimport type { ServiceManager } from '../services/index.js';\n\nconst logger = createLogger('graphql-server');\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * GraphQL server configuration options\n */\nexport interface GraphQLServerConfig {\n /** Port to listen on */\n port: number;\n /** Host to bind to */\n host?: string;\n /** Path for GraphQL endpoint */\n graphqlPath?: string;\n /** Path for health check endpoint */\n healthPath?: string;\n /** Enable GraphiQL playground */\n enableGraphiQL?: boolean;\n /** Enable WebSocket subscriptions */\n enableSubscriptions?: boolean;\n /** CORS configuration */\n cors?: CorsConfig;\n /** Database instance */\n database: KnowledgeGraphDatabase;\n /** Cache instance */\n cache: AdvancedCache<unknown>;\n /** Service manager instance */\n serviceManager: ServiceManager;\n /** API key for authentication (optional) */\n apiKey?: string;\n /** Whether to require authentication */\n requireAuth?: boolean;\n /** Custom resolvers (merged with defaults) */\n resolvers?: Record<string, unknown>;\n /** Custom schema path (default: src/graphql/schema.graphql) */\n schemaPath?: string;\n}\n\n/**\n * CORS configuration options\n */\nexport interface CorsConfig {\n /** Allowed origins (string or array) */\n origin?: string | string[] | boolean;\n /** Allowed methods */\n methods?: string[];\n /** Allowed headers */\n allowedHeaders?: string[];\n /** Exposed headers */\n exposedHeaders?: string[];\n /** Allow credentials */\n credentials?: boolean;\n /** Max age for preflight cache (seconds) */\n maxAge?: number;\n}\n\n/**\n * Health check response\n */\nexport interface HealthCheckResponse {\n status: 'healthy' | 'degraded' | 'unhealthy';\n timestamp: string;\n uptime: number;\n version: string;\n components: {\n database: boolean;\n cache: boolean;\n services: boolean;\n };\n}\n\n/**\n * GraphQL server instance with lifecycle methods\n */\nexport interface GraphQLServerInstance {\n /** Start the server */\n start(): Promise<void>;\n /** Stop the server gracefully */\n stop(): Promise<void>;\n /** Get the HTTP server instance */\n getHttpServer(): Server;\n /** Get the Yoga instance */\n getYoga(): YogaServerInstance<Record<string, unknown>, GraphQLContext>;\n /** Check if server is running */\n isRunning(): boolean;\n /** Get server URL */\n getUrl(): string;\n}\n\n// ============================================================================\n// Server Factory\n// ============================================================================\n\n/**\n * Create a GraphQL server with all configured features\n *\n * @param config - Server configuration\n * @returns GraphQL server instance\n *\n * @example\n * ```typescript\n * const server = createGraphQLServer({\n * port: 4000,\n * database,\n * cache,\n * serviceManager,\n * enableGraphiQL: true,\n * enableSubscriptions: true,\n * cors: {\n * origin: ['http://localhost:3000'],\n * credentials: true,\n * },\n * });\n *\n * await server.start();\n * console.log(`GraphQL server running at ${server.getUrl()}`);\n * ```\n */\nexport function createGraphQLServer(config: GraphQLServerConfig): GraphQLServerInstance {\n const {\n port,\n host = '0.0.0.0',\n graphqlPath = '/graphql',\n healthPath = '/health',\n enableGraphiQL = true,\n enableSubscriptions = true,\n cors = {},\n database,\n cache,\n serviceManager,\n apiKey,\n requireAuth = false,\n resolvers: customResolvers = {},\n schemaPath,\n } = config;\n\n let httpServer: Server | null = null;\n let wsServer: WebSocketServer | null = null;\n let isServerRunning = false;\n const startTime = Date.now();\n\n // Load schema\n const typeDefs = loadSchema(schemaPath);\n\n // Create resolvers with custom scalars\n const resolvers = {\n ...customScalars,\n ...createDefaultResolvers(),\n ...customResolvers,\n };\n\n // Create executable schema\n const schema = makeExecutableSchema({\n typeDefs,\n resolvers,\n });\n\n // Create context factory\n const contextFactory = createContextFactory({\n database,\n cache,\n serviceManager,\n apiKey,\n requireAuth,\n });\n\n // Create CORS configuration\n const corsConfig = buildCorsConfig(cors);\n\n // Create Yoga instance\n const yoga = createYoga<Record<string, unknown>, GraphQLContext>({\n schema,\n context: contextFactory,\n graphqlEndpoint: graphqlPath,\n graphiql: enableGraphiQL\n ? {\n title: 'Knowledge Graph API',\n defaultQuery: DEFAULT_QUERY,\n subscriptionsProtocol: 'WS',\n }\n : false,\n cors: {\n origin: Array.isArray(corsConfig.origin) ? corsConfig.origin : corsConfig.origin === true ? '*' : [],\n methods: corsConfig.methods,\n allowedHeaders: corsConfig.allowedHeaders,\n exposedHeaders: corsConfig.exposedHeaders,\n credentials: corsConfig.credentials,\n maxAge: corsConfig.maxAge,\n },\n logging: {\n debug: (...args) => logger.debug('GraphQL', { args }),\n info: (...args) => logger.info(args.join(' ')),\n warn: (...args) => logger.warn(args.join(' ')),\n error: (...args) => logger.error(args.join(' ')),\n },\n maskedErrors: process.env.NODE_ENV === 'production',\n landingPage: false,\n });\n\n // Create HTTP server with health check\n httpServer = createServer(async (req: IncomingMessage, res: ServerResponse) => {\n // Handle health check\n if (req.url === healthPath && req.method === 'GET') {\n const health = await getHealthCheck(database, cache, serviceManager, startTime);\n const statusCode = health.status === 'healthy' ? 200 : health.status === 'degraded' ? 200 : 503;\n\n res.writeHead(statusCode, {\n 'Content-Type': 'application/json',\n 'Cache-Control': 'no-cache, no-store, must-revalidate',\n });\n res.end(JSON.stringify(health));\n return;\n }\n\n // Handle CORS preflight\n if (req.method === 'OPTIONS') {\n handleCorsPreFlight(req, res, corsConfig);\n return;\n }\n\n // Delegate to Yoga - yoga.handle returns void and writes directly to response\n yoga.handle(req, res);\n });\n\n // Setup WebSocket server for subscriptions\n if (enableSubscriptions) {\n wsServer = new WebSocketServer({\n server: httpServer,\n path: graphqlPath,\n });\n\n useServer(\n {\n schema,\n context: async (ctx) => {\n // Create context from WebSocket connection\n const request = new Request(`ws://${host}:${port}${graphqlPath}`, {\n headers: ctx.connectionParams as Record<string, string> | undefined,\n });\n return contextFactory({ request });\n },\n onConnect: () => {\n logger.info('WebSocket client connected');\n return true;\n },\n onDisconnect: (ctx, code, reason) => {\n logger.info('WebSocket client disconnected', { code, reason: reason?.toString() });\n },\n onError: (ctx, message, errors) => {\n logger.error('WebSocket error', undefined, { message, errors });\n },\n },\n wsServer\n );\n\n logger.info('WebSocket subscriptions enabled', { path: graphqlPath });\n }\n\n // Return server instance\n return {\n async start(): Promise<void> {\n if (isServerRunning) {\n logger.warn('Server already running');\n return;\n }\n\n return new Promise((resolve, reject) => {\n httpServer!.listen(port, host, () => {\n isServerRunning = true;\n logger.info('GraphQL server started', {\n url: `http://${host}:${port}${graphqlPath}`,\n graphiql: enableGraphiQL ? `http://${host}:${port}${graphqlPath}` : 'disabled',\n health: `http://${host}:${port}${healthPath}`,\n subscriptions: enableSubscriptions ? `ws://${host}:${port}${graphqlPath}` : 'disabled',\n });\n resolve();\n });\n\n httpServer!.on('error', (error) => {\n logger.error('Server error', error);\n reject(error);\n });\n });\n },\n\n async stop(): Promise<void> {\n if (!isServerRunning) {\n return;\n }\n\n logger.info('Stopping GraphQL server...');\n\n // Close WebSocket server\n if (wsServer) {\n await new Promise<void>((resolve) => {\n wsServer!.close(() => {\n logger.debug('WebSocket server closed');\n resolve();\n });\n });\n }\n\n // Close HTTP server\n await new Promise<void>((resolve, reject) => {\n httpServer!.close((error) => {\n if (error) {\n reject(error);\n } else {\n isServerRunning = false;\n logger.info('GraphQL server stopped');\n resolve();\n }\n });\n });\n },\n\n getHttpServer(): Server {\n return httpServer!;\n },\n\n getYoga() {\n return yoga;\n },\n\n isRunning(): boolean {\n return isServerRunning;\n },\n\n getUrl(): string {\n return `http://${host}:${port}${graphqlPath}`;\n },\n };\n}\n\n// ============================================================================\n// Schema Loading\n// ============================================================================\n\n/**\n * Load GraphQL schema from file\n */\nfunction loadSchema(customPath?: string): string {\n // Try custom path first\n if (customPath && existsSync(customPath)) {\n logger.debug('Loading schema from custom path', { path: customPath });\n return readFileSync(customPath, 'utf-8');\n }\n\n // Schema search paths (relative to cwd)\n const searchPaths = [\n join(process.cwd(), 'src/graphql/schema.graphql'),\n join(process.cwd(), 'graphql/schema.graphql'),\n join(process.cwd(), 'schema.graphql'),\n // For dist builds\n join(process.cwd(), 'dist/graphql/schema.graphql'),\n ];\n\n for (const schemaPath of searchPaths) {\n if (existsSync(schemaPath)) {\n logger.debug('Loading schema from path', { path: schemaPath });\n return readFileSync(schemaPath, 'utf-8');\n }\n }\n\n throw new Error(\n `GraphQL schema not found. Tried: ${[customPath, ...searchPaths].filter(Boolean).join(', ')}`\n );\n}\n\n// ============================================================================\n// Default Resolvers\n// ============================================================================\n\n/**\n * Create default resolvers for system queries\n */\nfunction createDefaultResolvers(): Record<string, unknown> {\n return {\n Query: {\n // System health query\n health: async (\n _parent: unknown,\n _args: unknown,\n context: GraphQLContext\n ): Promise<{\n status: string;\n components: { database: boolean; cache: boolean; agents: boolean; vectorStore: boolean };\n uptime: number;\n requestCount: number;\n toolCount: number;\n }> => {\n const { services } = context;\n const dbHealthy = !!services.database;\n const cacheHealthy = !!services.cache;\n\n return {\n status: dbHealthy && cacheHealthy ? 'HEALTHY' : 'DEGRADED',\n components: {\n database: dbHealthy,\n cache: cacheHealthy,\n agents: true, // Would check agent registry\n vectorStore: false, // Not implemented yet\n },\n uptime: process.uptime() * 1000,\n requestCount: 0, // Would track in context\n toolCount: 0, // Would count MCP tools\n };\n },\n\n // Version info query\n version: (): {\n version: string;\n buildTime: Date | null;\n gitCommit: string | null;\n nodeVersion: string;\n schemaVersion: string;\n } => ({\n version: process.env.npm_package_version ?? '0.0.0',\n buildTime: null,\n gitCommit: process.env.GIT_COMMIT ?? null,\n nodeVersion: process.version,\n schemaVersion: '1.0.0',\n }),\n },\n\n // Subscription resolvers would be added here\n Subscription: {},\n };\n}\n\n// ============================================================================\n// CORS Helpers\n// ============================================================================\n\n/**\n * Build CORS configuration for Yoga\n */\nfunction buildCorsConfig(cors: CorsConfig): {\n origin: string[] | boolean;\n methods: string[];\n allowedHeaders: string[];\n exposedHeaders: string[];\n credentials: boolean;\n maxAge: number;\n} {\n const origin = cors.origin === true\n ? true\n : cors.origin === false\n ? false\n : Array.isArray(cors.origin)\n ? cors.origin\n : cors.origin\n ? [cors.origin]\n : ['http://localhost:3000', 'http://localhost:5173']; // Default dashboard origins\n\n return {\n origin: origin === false ? [] : origin,\n methods: cors.methods ?? ['GET', 'POST', 'OPTIONS'],\n allowedHeaders: cors.allowedHeaders ?? ['Content-Type', 'Authorization', 'X-Request-ID'],\n exposedHeaders: cors.exposedHeaders ?? ['X-Request-ID'],\n credentials: cors.credentials ?? true,\n maxAge: cors.maxAge ?? 86400, // 24 hours\n };\n}\n\n/**\n * Handle CORS preflight request\n */\nfunction handleCorsPreFlight(\n req: import('http').IncomingMessage,\n res: import('http').ServerResponse,\n corsConfig: ReturnType<typeof buildCorsConfig>\n): void {\n const origin = req.headers.origin;\n\n if (origin && (corsConfig.origin === true || (Array.isArray(corsConfig.origin) && corsConfig.origin.includes(origin)))) {\n res.setHeader('Access-Control-Allow-Origin', origin);\n }\n\n res.setHeader('Access-Control-Allow-Methods', corsConfig.methods.join(', '));\n res.setHeader('Access-Control-Allow-Headers', corsConfig.allowedHeaders.join(', '));\n res.setHeader('Access-Control-Max-Age', corsConfig.maxAge.toString());\n\n if (corsConfig.credentials) {\n res.setHeader('Access-Control-Allow-Credentials', 'true');\n }\n\n res.writeHead(204);\n res.end();\n}\n\n// ============================================================================\n// Health Check\n// ============================================================================\n\n/**\n * Get health check response\n */\nasync function getHealthCheck(\n database: KnowledgeGraphDatabase,\n cache: AdvancedCache<unknown>,\n serviceManager: ServiceManager,\n startTime: number\n): Promise<HealthCheckResponse> {\n let dbHealthy = false;\n let cacheHealthy = false;\n let servicesHealthy = false;\n\n try {\n // Check database\n const db = database.getDatabase();\n db.prepare('SELECT 1').get();\n dbHealthy = true;\n } catch (error) {\n logger.error('Database health check failed', error instanceof Error ? error : undefined);\n }\n\n try {\n // Check cache\n cache.set('_health_check', Date.now(), { ttl: 1000 });\n const value = cache.get('_health_check');\n cacheHealthy = value !== undefined;\n cache.delete('_health_check');\n } catch (error) {\n logger.error('Cache health check failed', error instanceof Error ? error : undefined);\n }\n\n try {\n // Check services\n const states = serviceManager.getAllStates();\n const runningCount = Array.from(states.values()).filter((s) => s.status === 'running').length;\n servicesHealthy = runningCount >= 0; // At least no errors\n } catch (error) {\n logger.error('Services health check failed', error instanceof Error ? error : undefined);\n }\n\n const allHealthy = dbHealthy && cacheHealthy && servicesHealthy;\n const anyHealthy = dbHealthy || cacheHealthy || servicesHealthy;\n\n return {\n status: allHealthy ? 'healthy' : anyHealthy ? 'degraded' : 'unhealthy',\n timestamp: new Date().toISOString(),\n uptime: Date.now() - startTime,\n version: process.env.npm_package_version ?? '0.0.0',\n components: {\n database: dbHealthy,\n cache: cacheHealthy,\n services: servicesHealthy,\n },\n };\n}\n\n// ============================================================================\n// Default Query for GraphiQL\n// ============================================================================\n\nconst DEFAULT_QUERY = `# Welcome to the Knowledge Graph API\n#\n# Try these example queries:\n\nquery SystemHealth {\n health {\n status\n components {\n database\n cache\n agents\n vectorStore\n }\n uptime\n requestCount\n toolCount\n }\n}\n\nquery SystemVersion {\n version {\n version\n nodeVersion\n schemaVersion\n }\n}\n\n# Uncomment to query graph stats:\n# query GraphStats {\n# graphStats {\n# totalNodes\n# totalEdges\n# nodesByType {\n# type\n# count\n# }\n# orphanNodes\n# avgLinksPerNode\n# }\n# }\n\n# Uncomment to search nodes:\n# query SearchNodes($query: String!) {\n# search(query: $query) {\n# totalMatches\n# nodes {\n# id\n# title\n# type\n# tags\n# }\n# }\n# }\n`;\n"],"names":[],"mappings":"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"}
|
|
@@ -40,6 +40,9 @@ export declare const auditToolDefinitions: {
|
|
|
40
40
|
idempotentHint?: boolean | undefined;
|
|
41
41
|
openWorldHint?: boolean | undefined;
|
|
42
42
|
} | undefined;
|
|
43
|
+
execution?: {
|
|
44
|
+
taskSupport?: "required" | "optional" | "forbidden" | undefined;
|
|
45
|
+
} | undefined;
|
|
43
46
|
_meta?: {
|
|
44
47
|
[x: string]: unknown;
|
|
45
48
|
} | undefined;
|
|
@@ -47,6 +50,7 @@ export declare const auditToolDefinitions: {
|
|
|
47
50
|
src: string;
|
|
48
51
|
mimeType?: string | undefined;
|
|
49
52
|
sizes?: string[] | undefined;
|
|
53
|
+
theme?: "light" | "dark" | undefined;
|
|
50
54
|
}[] | undefined;
|
|
51
55
|
title?: string | undefined;
|
|
52
56
|
}[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/audit/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,mBAAmB,EACnB,4BAA4B,EAC5B,mBAAmB,EACnB,KAAK,qBAAqB,GAC3B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,iBAAiB,EACjB,KAAK,gBAAgB,GACtB,MAAM,WAAW,CAAC;AAMnB;;GAEG;AACH,eAAO,MAAM,oBAAoB
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/audit/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,mBAAmB,EACnB,4BAA4B,EAC5B,mBAAmB,EACnB,KAAK,qBAAqB,GAC3B,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EACL,cAAc,EACd,uBAAuB,EACvB,iBAAiB,EACjB,KAAK,gBAAgB,GACtB,MAAM,WAAW,CAAC;AAMnB;;GAEG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAIhC,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as dist } from "../../../../_virtual/
|
|
1
|
+
import { __exports as dist } from "../../../../_virtual/index7.js";
|
|
2
2
|
import { __require as requireCreateProjectService } from "./createProjectService.js";
|
|
3
3
|
var hasRequiredDist;
|
|
4
4
|
function requireDist() {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { __exports as dist } from "../../../_virtual/
|
|
1
|
+
import { __exports as dist } from "../../../_virtual/index10.js";
|
|
2
2
|
import path__default from "path";
|
|
3
|
-
import
|
|
3
|
+
import require$$1 from "fs";
|
|
4
4
|
import { __require as requirePicomatch } from "../../picomatch/index.js";
|
|
5
5
|
var hasRequiredDist;
|
|
6
6
|
function requireDist() {
|
|
@@ -27,7 +27,7 @@ function requireDist() {
|
|
|
27
27
|
enumerable: true
|
|
28
28
|
}) : target, mod));
|
|
29
29
|
const path = __toESM(path__default);
|
|
30
|
-
const fs
|
|
30
|
+
const fs = __toESM(require$$1);
|
|
31
31
|
function cleanPath(path$1) {
|
|
32
32
|
let normalized = (0, path.normalize)(path$1);
|
|
33
33
|
if (normalized.length > 1 && normalized[normalized.length - 1] === path.sep) normalized = normalized.substring(0, normalized.length - 1);
|
|
@@ -139,11 +139,11 @@ function requireDist() {
|
|
|
139
139
|
return options.group ? groupFiles : empty;
|
|
140
140
|
}
|
|
141
141
|
const resolveSymlinksAsync = function(path$1, state, callback$1) {
|
|
142
|
-
const { queue, fs: fs$
|
|
142
|
+
const { queue, fs: fs$1, options: { suppressErrors } } = state;
|
|
143
143
|
queue.enqueue();
|
|
144
|
-
fs$
|
|
144
|
+
fs$1.realpath(path$1, (error, resolvedPath) => {
|
|
145
145
|
if (error) return queue.dequeue(suppressErrors ? null : error, state);
|
|
146
|
-
fs$
|
|
146
|
+
fs$1.stat(resolvedPath, (error$1, stat) => {
|
|
147
147
|
if (error$1) return queue.dequeue(suppressErrors ? null : error$1, state);
|
|
148
148
|
if (stat.isDirectory() && isRecursive(path$1, resolvedPath, state)) return queue.dequeue(null, state);
|
|
149
149
|
callback$1(stat, resolvedPath);
|
|
@@ -152,11 +152,11 @@ function requireDist() {
|
|
|
152
152
|
});
|
|
153
153
|
};
|
|
154
154
|
const resolveSymlinks = function(path$1, state, callback$1) {
|
|
155
|
-
const { queue, fs: fs$
|
|
155
|
+
const { queue, fs: fs$1, options: { suppressErrors } } = state;
|
|
156
156
|
queue.enqueue();
|
|
157
157
|
try {
|
|
158
|
-
const resolvedPath = fs$
|
|
159
|
-
const stat = fs$
|
|
158
|
+
const resolvedPath = fs$1.realpathSync(path$1);
|
|
159
|
+
const stat = fs$1.statSync(resolvedPath);
|
|
160
160
|
if (stat.isDirectory() && isRecursive(path$1, resolvedPath, state)) return;
|
|
161
161
|
callback$1(stat, resolvedPath);
|
|
162
162
|
} catch (e) {
|
|
@@ -226,22 +226,22 @@ function requireDist() {
|
|
|
226
226
|
const walkAsync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
|
|
227
227
|
state.queue.enqueue();
|
|
228
228
|
if (currentDepth < 0) return state.queue.dequeue(null, state);
|
|
229
|
-
const { fs: fs$
|
|
229
|
+
const { fs: fs$1 } = state;
|
|
230
230
|
state.visited.push(crawlPath);
|
|
231
231
|
state.counts.directories++;
|
|
232
|
-
fs$
|
|
232
|
+
fs$1.readdir(crawlPath || ".", readdirOpts, (error, entries = []) => {
|
|
233
233
|
callback$1(entries, directoryPath, currentDepth);
|
|
234
234
|
state.queue.dequeue(state.options.suppressErrors ? null : error, state);
|
|
235
235
|
});
|
|
236
236
|
};
|
|
237
237
|
const walkSync = (state, crawlPath, directoryPath, currentDepth, callback$1) => {
|
|
238
|
-
const { fs: fs$
|
|
238
|
+
const { fs: fs$1 } = state;
|
|
239
239
|
if (currentDepth < 0) return;
|
|
240
240
|
state.visited.push(crawlPath);
|
|
241
241
|
state.counts.directories++;
|
|
242
242
|
let entries = [];
|
|
243
243
|
try {
|
|
244
|
-
entries = fs$
|
|
244
|
+
entries = fs$1.readdirSync(crawlPath || ".", readdirOpts);
|
|
245
245
|
} catch (e) {
|
|
246
246
|
if (!state.options.suppressErrors) throw e;
|
|
247
247
|
}
|
|
@@ -324,7 +324,7 @@ function requireDist() {
|
|
|
324
324
|
symlinks: /* @__PURE__ */ new Map(),
|
|
325
325
|
visited: [""].slice(0, 0),
|
|
326
326
|
controller: new Aborter(),
|
|
327
|
-
fs: options.fs || fs
|
|
327
|
+
fs: options.fs || fs
|
|
328
328
|
};
|
|
329
329
|
this.joinPath = build$7(this.root, options);
|
|
330
330
|
this.pushDirectory = build$6(this.root, options);
|