Skip to main content
Glama
rebuild.js11.4 kB
#!/usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); const database_adapter_1 = require("../database/database-adapter"); const node_loader_1 = require("../loaders/node-loader"); const node_parser_1 = require("../parsers/node-parser"); const docs_mapper_1 = require("../mappers/docs-mapper"); const node_repository_1 = require("../database/node-repository"); const tool_variant_generator_1 = require("../services/tool-variant-generator"); const template_sanitizer_1 = require("../utils/template-sanitizer"); const fs = __importStar(require("fs")); const path = __importStar(require("path")); async function rebuild() { console.log('🔄 Rebuilding n8n node database...\n'); const dbPath = process.env.NODE_DB_PATH || './data/nodes.db'; const db = await (0, database_adapter_1.createDatabaseAdapter)(dbPath); const loader = new node_loader_1.N8nNodeLoader(); const parser = new node_parser_1.NodeParser(); const mapper = new docs_mapper_1.DocsMapper(); const repository = new node_repository_1.NodeRepository(db); const toolVariantGenerator = new tool_variant_generator_1.ToolVariantGenerator(); const schema = fs.readFileSync(path.join(__dirname, '../../src/database/schema.sql'), 'utf8'); db.exec(schema); db.exec('DELETE FROM nodes'); console.log('🗑️ Cleared existing data\n'); const nodes = await loader.loadAllNodes(); console.log(`📦 Loaded ${nodes.length} nodes from packages\n`); const stats = { successful: 0, failed: 0, aiTools: 0, triggers: 0, webhooks: 0, withProperties: 0, withOperations: 0, withDocs: 0, toolVariants: 0 }; console.log('🔄 Processing nodes...'); const processedNodes = []; for (const { packageName, nodeName, NodeClass } of nodes) { try { const parsed = parser.parse(NodeClass, packageName); if (!parsed.nodeType || !parsed.displayName) { throw new Error(`Missing required fields - nodeType: ${parsed.nodeType}, displayName: ${parsed.displayName}, packageName: ${parsed.packageName}`); } if (!parsed.packageName) { throw new Error(`Missing packageName for node ${nodeName}`); } const docs = await mapper.fetchDocumentation(parsed.nodeType); parsed.documentation = docs || undefined; if (parsed.isAITool && !parsed.isTrigger) { const toolVariant = toolVariantGenerator.generateToolVariant(parsed); if (toolVariant) { parsed.hasToolVariant = true; processedNodes.push({ parsed: toolVariant, docs: undefined, nodeName: `${nodeName}Tool` }); stats.toolVariants++; } } processedNodes.push({ parsed, docs: docs || undefined, nodeName }); } catch (error) { stats.failed++; const errorMessage = error.message; console.error(`❌ Failed to process ${nodeName}: ${errorMessage}`); } } console.log(`\n💾 Saving ${processedNodes.length} processed nodes to database...`); let saved = 0; for (const { parsed, docs, nodeName } of processedNodes) { try { repository.saveNode(parsed); saved++; stats.successful++; if (parsed.isAITool) stats.aiTools++; if (parsed.isTrigger) stats.triggers++; if (parsed.isWebhook) stats.webhooks++; if (parsed.properties.length > 0) stats.withProperties++; if (parsed.operations.length > 0) stats.withOperations++; if (docs) stats.withDocs++; console.log(`✅ ${parsed.nodeType} [Props: ${parsed.properties.length}, Ops: ${parsed.operations.length}]`); } catch (error) { stats.failed++; const errorMessage = error.message; console.error(`❌ Failed to save ${nodeName}: ${errorMessage}`); } } console.log(`💾 Save completed: ${saved} nodes saved successfully`); console.log('\n🔍 Running validation checks...'); try { const validationResults = validateDatabase(repository); if (!validationResults.passed) { console.log('⚠️ Validation Issues:'); validationResults.issues.forEach(issue => console.log(` - ${issue}`)); } else { console.log('✅ All validation checks passed'); } } catch (validationError) { console.error('❌ Validation failed:', validationError.message); console.log('⚠️ Skipping validation due to database compatibility issues'); } console.log('\n📊 Summary:'); console.log(` Total nodes: ${nodes.length}`); console.log(` Successful: ${stats.successful}`); console.log(` Failed: ${stats.failed}`); console.log(` AI Tools: ${stats.aiTools}`); console.log(` Tool Variants: ${stats.toolVariants}`); console.log(` Triggers: ${stats.triggers}`); console.log(` Webhooks: ${stats.webhooks}`); console.log(` With Properties: ${stats.withProperties}`); console.log(` With Operations: ${stats.withOperations}`); console.log(` With Documentation: ${stats.withDocs}`); console.log('\n🧹 Checking for templates to sanitize...'); const templateCount = db.prepare('SELECT COUNT(*) as count FROM templates').get(); if (templateCount && templateCount.count > 0) { console.log(` Found ${templateCount.count} templates, sanitizing...`); const sanitizer = new template_sanitizer_1.TemplateSanitizer(); let sanitizedCount = 0; const templates = db.prepare('SELECT id, name, workflow_json FROM templates').all(); for (const template of templates) { const originalWorkflow = JSON.parse(template.workflow_json); const { sanitized: sanitizedWorkflow, wasModified } = sanitizer.sanitizeWorkflow(originalWorkflow); if (wasModified) { const stmt = db.prepare('UPDATE templates SET workflow_json = ? WHERE id = ?'); stmt.run(JSON.stringify(sanitizedWorkflow), template.id); sanitizedCount++; console.log(` ✅ Sanitized template ${template.id}: ${template.name}`); } } console.log(` Sanitization complete: ${sanitizedCount} templates cleaned`); } else { console.log(' No templates found in database'); } console.log('\n✨ Rebuild complete!'); db.close(); } const MIN_EXPECTED_TOOL_VARIANTS = 200; function validateDatabase(repository) { const issues = []; try { const db = repository.db; const nodeCount = db.prepare('SELECT COUNT(*) as count FROM nodes').get(); if (nodeCount.count === 0) { issues.push('CRITICAL: Database is empty - no nodes found! Rebuild failed or was interrupted.'); return { passed: false, issues }; } if (nodeCount.count < 500) { issues.push(`WARNING: Only ${nodeCount.count} nodes found - expected at least 500 (both n8n packages)`); } const criticalNodes = ['nodes-base.httpRequest', 'nodes-base.code', 'nodes-base.webhook', 'nodes-base.slack']; for (const nodeType of criticalNodes) { const node = repository.getNode(nodeType); if (!node) { issues.push(`Critical node ${nodeType} not found`); continue; } if (node.properties.length === 0) { issues.push(`Node ${nodeType} has no properties`); } } const aiTools = repository.getAITools(); if (aiTools.length === 0) { issues.push('No AI tools found - check detection logic'); } const toolVariantCount = repository.getToolVariantCount(); if (toolVariantCount === 0) { issues.push('No Tool variants found - check ToolVariantGenerator'); } else if (toolVariantCount < MIN_EXPECTED_TOOL_VARIANTS) { issues.push(`Only ${toolVariantCount} Tool variants found - expected at least ${MIN_EXPECTED_TOOL_VARIANTS}`); } const ftsTableCheck = db.prepare(` SELECT name FROM sqlite_master WHERE type='table' AND name='nodes_fts' `).get(); if (!ftsTableCheck) { issues.push('CRITICAL: FTS5 table (nodes_fts) does not exist - searches will fail or be very slow'); } else { const ftsCount = db.prepare('SELECT COUNT(*) as count FROM nodes_fts').get(); if (ftsCount.count === 0) { issues.push('CRITICAL: FTS5 index is empty - searches will return zero results'); } else if (nodeCount.count !== ftsCount.count) { issues.push(`FTS5 index out of sync: ${nodeCount.count} nodes but ${ftsCount.count} FTS5 entries`); } const searchableNodes = ['webhook', 'merge', 'split']; for (const searchTerm of searchableNodes) { const searchResult = db.prepare(` SELECT COUNT(*) as count FROM nodes_fts WHERE nodes_fts MATCH ? `).get(searchTerm); if (searchResult.count === 0) { issues.push(`CRITICAL: Search for "${searchTerm}" returns zero results in FTS5 index`); } } } } catch (error) { const errorMessage = error.message; issues.push(`Validation error: ${errorMessage}`); } return { passed: issues.length === 0, issues }; } if (require.main === module) { rebuild().catch(console.error); } //# sourceMappingURL=rebuild.js.map

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/czlonkowski/n8n-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server