#!/usr/bin/env python3 """ Sync NODE2 DAGI Agents from agents_city_mapping.yaml to database. This script reads the agent configuration from agents_city_mapping.yaml and syncs all 50 agents to the database with node_id = 'node-2-macbook-m4max'. Usage: python scripts/sync-node2-dagi-agents.py --dry-run # Preview changes python scripts/sync-node2-dagi-agents.py # Apply changes """ import argparse import asyncio import os import sys from pathlib import Path import yaml # Add project root to path PROJECT_ROOT = Path(__file__).parent.parent sys.path.insert(0, str(PROJECT_ROOT)) # Database connection DATABASE_URL = os.getenv( "DATABASE_URL", "postgresql://postgres:postgres@144.76.224.179:5432/daarion" ) NODE2_ID = "node-2-macbook-m4max" CONFIG_PATH = PROJECT_ROOT / "config" / "agents_city_mapping.yaml" def load_agents_config() -> dict: """Load agents configuration from YAML file.""" if not CONFIG_PATH.exists(): print(f"āŒ Config file not found: {CONFIG_PATH}") sys.exit(1) with open(CONFIG_PATH, 'r', encoding='utf-8') as f: config = yaml.safe_load(f) return config def parse_agent(agent_data: dict, districts: list) -> dict: """Parse agent data from YAML to database format.""" # Find district info district_id = agent_data.get('district', '') district_info = next((d for d in districts if d['id'] == district_id), None) return { 'id': agent_data['agent_id'], 'slug': agent_data['agent_id'], 'display_name': agent_data['display_name'], 'kind': agent_data.get('kind', 'assistant'), 'role': agent_data.get('role', ''), 'model': agent_data.get('model', ''), 'node_id': agent_data.get('node_id', NODE2_ID), 'district': district_id, 'primary_room_slug': agent_data.get('primary_room_slug', ''), 'avatar_url': agent_data.get('avatar_url', ''), 'color_hint': agent_data.get('color_hint', ''), 'priority': agent_data.get('priority', 'medium'), 'is_active': True, 'status': 'offline', 'is_public': True, 'public_slug': agent_data['agent_id'], 'public_title': agent_data['display_name'], 'public_tagline': agent_data.get('role', ''), 'public_district': district_info['name'] if district_info else '', 'public_primary_room_slug': agent_data.get('primary_room_slug', ''), 'is_listed_in_directory': True, 'visibility_scope': 'global', 'is_orchestrator': agent_data.get('kind') == 'orchestrator', 'home_node_id': agent_data.get('node_id', NODE2_ID), } async def sync_agents(dry_run: bool = True): """Sync agents to database.""" import asyncpg print(f"šŸ“‚ Loading config from: {CONFIG_PATH}") config = load_agents_config() districts = config.get('districts', []) agents_data = config.get('agents', []) print(f"šŸ“Š Found {len(agents_data)} agents in config") print(f"šŸ“Š Found {len(districts)} districts") # Parse agents agents = [parse_agent(a, districts) for a in agents_data] # Group by district for summary by_district = {} for agent in agents: d = agent['district'] if d not in by_district: by_district[d] = [] by_district[d].append(agent['display_name']) print("\nšŸ“‹ Agents by district:") for district_id, agent_names in sorted(by_district.items()): district_info = next((d for d in districts if d['id'] == district_id), None) district_name = district_info['name'] if district_info else district_id print(f" {district_name}: {len(agent_names)} agents") for name in agent_names: print(f" - {name}") if dry_run: print("\nšŸ” DRY RUN - No changes will be made") print(f"Would sync {len(agents)} agents to NODE2") return # Connect to database print(f"\nšŸ”— Connecting to database...") conn = await asyncpg.connect(DATABASE_URL) try: # Start transaction async with conn.transaction(): inserted = 0 updated = 0 for agent in agents: # Check if agent exists existing = await conn.fetchrow( "SELECT id FROM agents WHERE id = $1", agent['id'] ) if existing: # Update existing agent await conn.execute(""" UPDATE agents SET display_name = $2, kind = $3, role = $4, model = $5, node_id = $6, district = $7, primary_room_slug = $8, avatar_url = $9, color_hint = $10, priority = $11, is_active = $12, is_public = $13, public_slug = $14, public_title = $15, public_tagline = $16, public_district = $17, public_primary_room_slug = $18, is_listed_in_directory = $19, visibility_scope = $20, is_orchestrator = $21, home_node_id = $22, slug = $23, updated_at = NOW() WHERE id = $1 """, agent['id'], agent['display_name'], agent['kind'], agent['role'], agent['model'], agent['node_id'], agent['district'], agent['primary_room_slug'], agent['avatar_url'], agent['color_hint'], agent['priority'], agent['is_active'], agent['is_public'], agent['public_slug'], agent['public_title'], agent['public_tagline'], agent['public_district'], agent['public_primary_room_slug'], agent['is_listed_in_directory'], agent['visibility_scope'], agent['is_orchestrator'], agent['home_node_id'], agent['slug'], ) updated += 1 else: # Insert new agent await conn.execute(""" INSERT INTO agents ( id, display_name, kind, role, model, node_id, district, primary_room_slug, avatar_url, color_hint, priority, is_active, status, is_public, public_slug, public_title, public_tagline, public_district, public_primary_room_slug, is_listed_in_directory, visibility_scope, is_orchestrator, home_node_id, slug ) VALUES ( $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24 ) """, agent['id'], agent['display_name'], agent['kind'], agent['role'], agent['model'], agent['node_id'], agent['district'], agent['primary_room_slug'], agent['avatar_url'], agent['color_hint'], agent['priority'], agent['is_active'], agent['status'], agent['is_public'], agent['public_slug'], agent['public_title'], agent['public_tagline'], agent['public_district'], agent['public_primary_room_slug'], agent['is_listed_in_directory'], agent['visibility_scope'], agent['is_orchestrator'], agent['home_node_id'], agent['slug'], ) inserted += 1 print(f"\nāœ… Sync complete!") print(f" Inserted: {inserted} agents") print(f" Updated: {updated} agents") # Verify count count = await conn.fetchval( "SELECT COUNT(*) FROM agents WHERE node_id = $1 AND deleted_at IS NULL", NODE2_ID ) print(f" Total NODE2 agents: {count}") finally: await conn.close() def main(): parser = argparse.ArgumentParser( description="Sync NODE2 DAGI agents from config to database" ) parser.add_argument( "--dry-run", action="store_true", help="Preview changes without applying them" ) args = parser.parse_args() print("=" * 60) print("šŸ¤– NODE2 DAGI Agent Sync") print("=" * 60) asyncio.run(sync_agents(dry_run=args.dry_run)) if __name__ == "__main__": main()