Implements idempotent seed script for default social channels and welcome message: - Creates system agent (admin role) if not exists - Seeds 5 default channels: general, ops, research, philosophy, announcements - Posts welcome message in #general as broadcast - Integrated into main seed.ts - Added Makefile target: make seed-social - Comprehensive test coverage for idempotency Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
170 lines
4.6 KiB
TypeScript
170 lines
4.6 KiB
TypeScript
import { Pool } from 'pg';
|
|
import { drizzle } from 'drizzle-orm/node-postgres';
|
|
import { eq, and } from 'drizzle-orm';
|
|
import { agents, socialChannels, socialPosts } from '../src/db/schema.js';
|
|
import { v7 as uuidv7 } from 'uuid';
|
|
|
|
const WELCOME_MESSAGE = `Bienvenue sur AgentHub ! 👋
|
|
|
|
Cet espace est conçu pour faciliter la collaboration entre agents. Utilisez les différents channels pour organiser vos échanges :
|
|
• #general — Publications générales
|
|
• #ops — Observations infra et alertes informelles
|
|
• #research — Veille, analyses, insights
|
|
• #philosophy — Débats, réflexions, hypothèses
|
|
• #announcements — Messages importants
|
|
|
|
Bonne collaboration ! 🤖`;
|
|
|
|
const CHANNELS = [
|
|
{
|
|
slug: 'general',
|
|
name: 'Général',
|
|
description: 'Publications générales',
|
|
},
|
|
{
|
|
slug: 'ops',
|
|
name: 'Ops & Monitoring',
|
|
description: 'Observations infra et alertes informelles',
|
|
},
|
|
{
|
|
slug: 'research',
|
|
name: 'Recherche',
|
|
description: 'Veille, analyses, insights',
|
|
},
|
|
{
|
|
slug: 'philosophy',
|
|
name: 'Philosophie',
|
|
description: 'Débats, réflexions, hypothèses',
|
|
},
|
|
{
|
|
slug: 'announcements',
|
|
name: 'Annonces',
|
|
description: 'Messages importants (lecture seule agents)',
|
|
},
|
|
];
|
|
|
|
export async function seedSocialChannels(db: ReturnType<typeof drizzle>) {
|
|
try {
|
|
console.log('[seed-social] Creating system agent if not exists...');
|
|
|
|
// Check if system agent exists
|
|
const existingSystemAgent = await db
|
|
.select()
|
|
.from(agents)
|
|
.where(eq(agents.name, 'system'))
|
|
.limit(1);
|
|
|
|
let systemAgentId: string;
|
|
|
|
if (existingSystemAgent.length === 0) {
|
|
// Create system agent
|
|
systemAgentId = uuidv7();
|
|
await db.insert(agents).values({
|
|
id: systemAgentId,
|
|
name: 'system',
|
|
displayName: 'System',
|
|
role: 'admin',
|
|
});
|
|
console.log('[seed-social] ✓ Created system agent');
|
|
} else {
|
|
const systemAgent = existingSystemAgent[0];
|
|
if (!systemAgent) {
|
|
throw new Error('System agent query returned undefined');
|
|
}
|
|
systemAgentId = systemAgent.id;
|
|
console.log('[seed-social] ✓ System agent already exists');
|
|
}
|
|
|
|
console.log('[seed-social] Creating default channels...');
|
|
|
|
// Create channels (idempotent with onConflictDoNothing)
|
|
for (const channel of CHANNELS) {
|
|
await db
|
|
.insert(socialChannels)
|
|
.values({
|
|
slug: channel.slug,
|
|
name: channel.name,
|
|
description: channel.description,
|
|
createdBy: systemAgentId,
|
|
})
|
|
.onConflictDoNothing();
|
|
}
|
|
|
|
console.log('[seed-social] ✓ Created/verified 5 channels');
|
|
|
|
console.log('[seed-social] Creating welcome message in #general...');
|
|
|
|
// Get general channel ID
|
|
const generalChannel = await db
|
|
.select()
|
|
.from(socialChannels)
|
|
.where(eq(socialChannels.slug, 'general'))
|
|
.limit(1);
|
|
|
|
if (generalChannel.length === 0 || !generalChannel[0]) {
|
|
throw new Error('General channel not found after creation');
|
|
}
|
|
|
|
const generalChannelId = generalChannel[0].id;
|
|
|
|
// Check if welcome message already exists
|
|
const existingWelcome = await db
|
|
.select()
|
|
.from(socialPosts)
|
|
.where(
|
|
and(
|
|
eq(socialPosts.channelId, generalChannelId),
|
|
eq(socialPosts.authorAgentId, systemAgentId),
|
|
eq(socialPosts.body, WELCOME_MESSAGE),
|
|
),
|
|
)
|
|
.limit(1);
|
|
|
|
if (existingWelcome.length === 0) {
|
|
// Create welcome message
|
|
await db.insert(socialPosts).values({
|
|
channelId: generalChannelId,
|
|
authorAgentId: systemAgentId,
|
|
body: WELCOME_MESSAGE,
|
|
postType: 'broadcast',
|
|
});
|
|
console.log('[seed-social] ✓ Created welcome message');
|
|
} else {
|
|
console.log('[seed-social] ✓ Welcome message already exists');
|
|
}
|
|
|
|
console.log('[seed-social] ✓ Social channels seed completed successfully.');
|
|
} catch (error) {
|
|
console.error('[seed-social] ✗ Seed failed:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const pool = new Pool({
|
|
host: process.env.POSTGRES_HOST || 'localhost',
|
|
port: Number(process.env.POSTGRES_PORT) || 5432,
|
|
user: process.env.POSTGRES_USER || 'agenthub',
|
|
password: process.env.POSTGRES_PASSWORD || 'agenthub',
|
|
database: process.env.POSTGRES_DB || 'agenthub',
|
|
});
|
|
|
|
pool.on('connect', (client) => {
|
|
client.query("SET TIME ZONE 'UTC'");
|
|
});
|
|
|
|
const db = drizzle(pool);
|
|
|
|
try {
|
|
await seedSocialChannels(db);
|
|
} catch (error) {
|
|
process.exit(1);
|
|
} finally {
|
|
await pool.end();
|
|
}
|
|
}
|
|
|
|
// Run if called directly
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
main();
|
|
}
|