import mysql from 'mysql2/promise'; async function main() { const conn = await mysql.createConnection(process.env.DATABASE_URL); const statements = [ // agentAccessControl `CREATE TABLE IF NOT EXISTS \`agentAccessControl\` ( \`id\` int AUTO_INCREMENT NOT NULL, \`agentId\` int NOT NULL, \`tool\` varchar(50) NOT NULL, \`isAllowed\` boolean DEFAULT true, \`maxExecutionsPerHour\` int DEFAULT 100, \`timeoutSeconds\` int DEFAULT 30, \`allowedPatterns\` json, \`blockedPatterns\` json, \`createdAt\` timestamp NOT NULL DEFAULT (now()), \`updatedAt\` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT \`agentAccessControl_id\` PRIMARY KEY(\`id\`) )`, // agentMetrics `CREATE TABLE IF NOT EXISTS \`agentMetrics\` ( \`id\` int AUTO_INCREMENT NOT NULL, \`agentId\` int NOT NULL, \`requestId\` varchar(64) NOT NULL, \`userMessage\` text, \`agentResponse\` text, \`inputTokens\` int DEFAULT 0, \`outputTokens\` int DEFAULT 0, \`totalTokens\` int DEFAULT 0, \`processingTimeMs\` int NOT NULL, \`status\` enum('success','error','timeout','rate_limited') NOT NULL, \`errorMessage\` text, \`toolsCalled\` json, \`model\` varchar(100), \`temperature\` decimal(3,2), \`createdAt\` timestamp NOT NULL DEFAULT (now()), CONSTRAINT \`agentMetrics_id\` PRIMARY KEY(\`id\`), CONSTRAINT \`agentMetrics_requestId_unique\` UNIQUE(\`requestId\`) )`, // agents — full schema with isSystem and isOrchestrator `CREATE TABLE IF NOT EXISTS \`agents\` ( \`id\` int AUTO_INCREMENT NOT NULL, \`userId\` int NOT NULL, \`name\` varchar(255) NOT NULL, \`description\` text, \`role\` varchar(100) NOT NULL, \`model\` varchar(100) NOT NULL, \`provider\` varchar(50) NOT NULL, \`temperature\` decimal(3,2) DEFAULT '0.7', \`maxTokens\` int DEFAULT 2048, \`topP\` decimal(3,2) DEFAULT '1.0', \`frequencyPenalty\` decimal(3,2) DEFAULT '0.0', \`presencePenalty\` decimal(3,2) DEFAULT '0.0', \`systemPrompt\` text, \`allowedTools\` json, \`allowedDomains\` json, \`maxRequestsPerHour\` int DEFAULT 100, \`isActive\` boolean DEFAULT true, \`isPublic\` boolean DEFAULT false, \`isSystem\` boolean DEFAULT false, \`isOrchestrator\` boolean DEFAULT false, \`tags\` json, \`metadata\` json, \`createdAt\` timestamp NOT NULL DEFAULT (now()), \`updatedAt\` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT \`agents_id\` PRIMARY KEY(\`id\`) )`, // toolDefinitions `CREATE TABLE IF NOT EXISTS \`toolDefinitions\` ( \`id\` int AUTO_INCREMENT NOT NULL, \`toolId\` varchar(100) NOT NULL, \`name\` varchar(255) NOT NULL, \`description\` text NOT NULL, \`category\` varchar(50) NOT NULL DEFAULT 'custom', \`dangerous\` boolean DEFAULT false, \`parameters\` json, \`implementation\` text NOT NULL, \`isActive\` boolean DEFAULT true, \`createdBy\` int, \`createdAt\` timestamp NOT NULL DEFAULT (now()), \`updatedAt\` timestamp NOT NULL DEFAULT (now()) ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT \`toolDefinitions_id\` PRIMARY KEY(\`id\`), CONSTRAINT \`toolDefinitions_toolId_unique\` UNIQUE(\`toolId\`) )`, // browserSessions `CREATE TABLE IF NOT EXISTS \`browserSessions\` ( \`id\` int AUTO_INCREMENT NOT NULL, \`sessionId\` varchar(64) NOT NULL, \`agentId\` int NOT NULL, \`currentUrl\` text, \`title\` text, \`status\` enum('active','idle','closed','error') DEFAULT 'idle', \`screenshotUrl\` text, \`lastActionAt\` timestamp DEFAULT (now()), \`createdAt\` timestamp NOT NULL DEFAULT (now()), \`closedAt\` timestamp, CONSTRAINT \`browserSessions_id\` PRIMARY KEY(\`id\`), CONSTRAINT \`browserSessions_sessionId_unique\` UNIQUE(\`sessionId\`) )`, // agentHistory — conversation history per agent `CREATE TABLE IF NOT EXISTS \`agentHistory\` ( \`id\` int AUTO_INCREMENT NOT NULL, \`agentId\` int NOT NULL, \`sessionId\` varchar(64), \`role\` enum('user','assistant','system','tool') NOT NULL, \`content\` text NOT NULL, \`toolCalls\` json, \`metadata\` json, \`createdAt\` timestamp NOT NULL DEFAULT (now()), CONSTRAINT \`agentHistory_id\` PRIMARY KEY(\`id\`) )`, // Indexes `CREATE INDEX IF NOT EXISTS \`agentAccessControl_agentId_tool_idx\` ON \`agentAccessControl\` (\`agentId\`,\`tool\`)`, `CREATE INDEX IF NOT EXISTS \`agentMetrics_agentId_idx\` ON \`agentMetrics\` (\`agentId\`)`, `CREATE INDEX IF NOT EXISTS \`agentMetrics_createdAt_idx\` ON \`agentMetrics\` (\`createdAt\`)`, `CREATE INDEX IF NOT EXISTS \`agents_userId_idx\` ON \`agents\` (\`userId\`)`, `CREATE INDEX IF NOT EXISTS \`agents_model_idx\` ON \`agents\` (\`model\`)`, `CREATE INDEX IF NOT EXISTS \`browserSessions_agentId_idx\` ON \`browserSessions\` (\`agentId\`)`, `CREATE INDEX IF NOT EXISTS \`agentHistory_agentId_idx\` ON \`agentHistory\` (\`agentId\`)`, ]; for (const stmt of statements) { try { await conn.query(stmt); const tableName = stmt.match(/TABLE.*?`(\w+)`/)?.[1] || stmt.match(/INDEX.*?ON `(\w+)`/)?.[1] || 'statement'; console.log('✓', tableName); } catch (e) { if (e.code === 'ER_DUP_KEYNAME' || e.message.includes('Duplicate key name')) { console.log('⚠ Index already exists (ok)'); } else if (e.message.includes('already exists')) { console.log('⚠ Already exists (ok)'); } else { console.error('✗ Error:', e.message.slice(0, 120)); } } } // ALTER TABLE to add missing columns to existing tables const alterStatements = [ `ALTER TABLE \`agents\` ADD COLUMN IF NOT EXISTS \`isSystem\` boolean DEFAULT false`, `ALTER TABLE \`agents\` ADD COLUMN IF NOT EXISTS \`isOrchestrator\` boolean DEFAULT false`, ]; console.log('\n--- Applying ALTER TABLE migrations ---'); for (const stmt of alterStatements) { try { await conn.query(stmt); const col = stmt.match(/ADD COLUMN.*?`(\w+)`/)?.[1] || 'column'; console.log('✓ Added column:', col); } catch (e) { if (e.message.includes('Duplicate column name') || e.message.includes('already exists')) { const col = stmt.match(/ADD COLUMN.*?`(\w+)`/)?.[1] || 'column'; console.log('⚠ Column already exists:', col, '(ok)'); } else { console.error('✗ ALTER error:', e.message.slice(0, 120)); } } } const [tables] = await conn.query('SHOW TABLES'); console.log('\n✅ All tables:', tables.map(t => Object.values(t)[0]).join(', ')); await conn.end(); } main().catch(console.error);