All checks were successful
ci/woodpecker/push/api Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
600 lines
16 KiB
TypeScript
600 lines
16 KiB
TypeScript
import {
|
|
PrismaClient,
|
|
TaskStatus,
|
|
TaskPriority,
|
|
ProjectStatus,
|
|
WorkspaceMemberRole,
|
|
EntryStatus,
|
|
Visibility,
|
|
} from "@prisma/client";
|
|
|
|
const prisma = new PrismaClient();
|
|
|
|
async function main() {
|
|
console.log("Seeding database...");
|
|
|
|
// IMPORTANT: This seed script should not be run concurrently
|
|
// If running in CI/CD, ensure serialization of seed operations
|
|
// to prevent race conditions and data corruption
|
|
|
|
// Create test user
|
|
const user = await prisma.user.upsert({
|
|
where: { email: "dev@mosaic.local" },
|
|
update: {},
|
|
create: {
|
|
email: "dev@mosaic.local",
|
|
name: "Development User",
|
|
preferences: {
|
|
theme: "system",
|
|
notifications: true,
|
|
},
|
|
},
|
|
});
|
|
|
|
console.log(`Created user: ${user.email}`);
|
|
|
|
// Create workspace
|
|
const workspace = await prisma.workspace.upsert({
|
|
where: { id: "00000000-0000-0000-0000-000000000001" },
|
|
update: {},
|
|
create: {
|
|
id: "00000000-0000-0000-0000-000000000001",
|
|
name: "Development Workspace",
|
|
ownerId: user.id,
|
|
settings: {
|
|
timezone: "America/New_York",
|
|
},
|
|
},
|
|
});
|
|
|
|
console.log(`Created workspace: ${workspace.name}`);
|
|
|
|
// Add user as workspace owner
|
|
await prisma.workspaceMember.upsert({
|
|
where: {
|
|
workspaceId_userId: {
|
|
workspaceId: workspace.id,
|
|
userId: user.id,
|
|
},
|
|
},
|
|
update: {},
|
|
create: {
|
|
workspaceId: workspace.id,
|
|
userId: user.id,
|
|
role: WorkspaceMemberRole.OWNER,
|
|
},
|
|
});
|
|
|
|
// ============================================
|
|
// WIDGET DEFINITIONS (global, not workspace-scoped)
|
|
// ============================================
|
|
const widgetDefs = [
|
|
{
|
|
name: "TasksWidget",
|
|
displayName: "Tasks",
|
|
description: "View and manage your tasks",
|
|
component: "TasksWidget",
|
|
defaultWidth: 2,
|
|
defaultHeight: 2,
|
|
minWidth: 1,
|
|
minHeight: 2,
|
|
maxWidth: 4,
|
|
maxHeight: null,
|
|
configSchema: {},
|
|
},
|
|
{
|
|
name: "CalendarWidget",
|
|
displayName: "Calendar",
|
|
description: "View upcoming events and schedule",
|
|
component: "CalendarWidget",
|
|
defaultWidth: 2,
|
|
defaultHeight: 2,
|
|
minWidth: 2,
|
|
minHeight: 2,
|
|
maxWidth: 4,
|
|
maxHeight: null,
|
|
configSchema: {},
|
|
},
|
|
{
|
|
name: "QuickCaptureWidget",
|
|
displayName: "Quick Capture",
|
|
description: "Quickly capture notes and tasks",
|
|
component: "QuickCaptureWidget",
|
|
defaultWidth: 2,
|
|
defaultHeight: 1,
|
|
minWidth: 2,
|
|
minHeight: 1,
|
|
maxWidth: 4,
|
|
maxHeight: 2,
|
|
configSchema: {},
|
|
},
|
|
{
|
|
name: "AgentStatusWidget",
|
|
displayName: "Agent Status",
|
|
description: "Monitor agent activity and status",
|
|
component: "AgentStatusWidget",
|
|
defaultWidth: 2,
|
|
defaultHeight: 2,
|
|
minWidth: 1,
|
|
minHeight: 2,
|
|
maxWidth: 3,
|
|
maxHeight: null,
|
|
configSchema: {},
|
|
},
|
|
{
|
|
name: "ActiveProjectsWidget",
|
|
displayName: "Active Projects & Agent Chains",
|
|
description: "View active projects and running agent sessions",
|
|
component: "ActiveProjectsWidget",
|
|
defaultWidth: 2,
|
|
defaultHeight: 3,
|
|
minWidth: 2,
|
|
minHeight: 2,
|
|
maxWidth: 4,
|
|
maxHeight: null,
|
|
configSchema: {},
|
|
},
|
|
{
|
|
name: "TaskProgressWidget",
|
|
displayName: "Task Progress",
|
|
description: "Live progress of orchestrator agent tasks",
|
|
component: "TaskProgressWidget",
|
|
defaultWidth: 2,
|
|
defaultHeight: 2,
|
|
minWidth: 1,
|
|
minHeight: 2,
|
|
maxWidth: 3,
|
|
maxHeight: null,
|
|
configSchema: {},
|
|
},
|
|
{
|
|
name: "OrchestratorEventsWidget",
|
|
displayName: "Orchestrator Events",
|
|
description: "Recent orchestration events with stream/Matrix visibility",
|
|
component: "OrchestratorEventsWidget",
|
|
defaultWidth: 2,
|
|
defaultHeight: 2,
|
|
minWidth: 1,
|
|
minHeight: 2,
|
|
maxWidth: 4,
|
|
maxHeight: null,
|
|
configSchema: {},
|
|
},
|
|
];
|
|
|
|
for (const wd of widgetDefs) {
|
|
await prisma.widgetDefinition.upsert({
|
|
where: { name: wd.name },
|
|
update: {
|
|
displayName: wd.displayName,
|
|
description: wd.description,
|
|
component: wd.component,
|
|
defaultWidth: wd.defaultWidth,
|
|
defaultHeight: wd.defaultHeight,
|
|
minWidth: wd.minWidth,
|
|
minHeight: wd.minHeight,
|
|
maxWidth: wd.maxWidth,
|
|
maxHeight: wd.maxHeight,
|
|
configSchema: wd.configSchema,
|
|
},
|
|
create: {
|
|
name: wd.name,
|
|
displayName: wd.displayName,
|
|
description: wd.description,
|
|
component: wd.component,
|
|
defaultWidth: wd.defaultWidth,
|
|
defaultHeight: wd.defaultHeight,
|
|
minWidth: wd.minWidth,
|
|
minHeight: wd.minHeight,
|
|
maxWidth: wd.maxWidth,
|
|
maxHeight: wd.maxHeight,
|
|
configSchema: wd.configSchema,
|
|
},
|
|
});
|
|
}
|
|
|
|
console.log(`Seeded ${widgetDefs.length} widget definitions`);
|
|
|
|
// Use transaction for atomic seed data reset and creation
|
|
await prisma.$transaction(async (tx) => {
|
|
// Delete existing seed data for idempotency (avoids duplicates on re-run)
|
|
await tx.task.deleteMany({ where: { workspaceId: workspace.id } });
|
|
await tx.event.deleteMany({ where: { workspaceId: workspace.id } });
|
|
await tx.project.deleteMany({ where: { workspaceId: workspace.id } });
|
|
|
|
// Create sample project
|
|
const project = await tx.project.create({
|
|
data: {
|
|
workspaceId: workspace.id,
|
|
name: "Sample Project",
|
|
description: "A sample project for development",
|
|
status: ProjectStatus.ACTIVE,
|
|
creatorId: user.id,
|
|
color: "#3B82F6",
|
|
},
|
|
});
|
|
|
|
console.log(`Created project: ${project.name}`);
|
|
|
|
// Create sample tasks
|
|
const tasks = [
|
|
{
|
|
title: "Set up development environment",
|
|
status: TaskStatus.COMPLETED,
|
|
priority: TaskPriority.HIGH,
|
|
},
|
|
{
|
|
title: "Review project requirements",
|
|
status: TaskStatus.IN_PROGRESS,
|
|
priority: TaskPriority.MEDIUM,
|
|
},
|
|
{
|
|
title: "Design database schema",
|
|
status: TaskStatus.COMPLETED,
|
|
priority: TaskPriority.HIGH,
|
|
},
|
|
{
|
|
title: "Implement NestJS integration",
|
|
status: TaskStatus.COMPLETED,
|
|
priority: TaskPriority.HIGH,
|
|
},
|
|
{
|
|
title: "Create seed data",
|
|
status: TaskStatus.IN_PROGRESS,
|
|
priority: TaskPriority.MEDIUM,
|
|
},
|
|
];
|
|
|
|
// Use createMany for batch insertion (better performance)
|
|
await tx.task.createMany({
|
|
data: tasks.map((taskData) => ({
|
|
workspaceId: workspace.id,
|
|
title: taskData.title,
|
|
status: taskData.status,
|
|
priority: taskData.priority,
|
|
creatorId: user.id,
|
|
projectId: project.id,
|
|
})),
|
|
});
|
|
|
|
console.log(`Created ${tasks.length} sample tasks`);
|
|
|
|
// Create sample event
|
|
const tomorrow = new Date();
|
|
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
tomorrow.setHours(10, 0, 0, 0);
|
|
|
|
await tx.event.create({
|
|
data: {
|
|
workspaceId: workspace.id,
|
|
title: "Morning standup",
|
|
description: "Daily team sync",
|
|
startTime: tomorrow,
|
|
endTime: new Date(tomorrow.getTime() + 30 * 60000), // 30 minutes later
|
|
creatorId: user.id,
|
|
projectId: project.id,
|
|
},
|
|
});
|
|
|
|
console.log("Created sample event");
|
|
|
|
// ============================================
|
|
// KNOWLEDGE MODULE SEED DATA
|
|
// ============================================
|
|
|
|
// Delete existing knowledge data
|
|
await tx.knowledgeEmbedding.deleteMany({ where: { entry: { workspaceId: workspace.id } } });
|
|
await tx.knowledgeEntryTag.deleteMany({ where: { entry: { workspaceId: workspace.id } } });
|
|
await tx.knowledgeLink.deleteMany({ where: { source: { workspaceId: workspace.id } } });
|
|
await tx.knowledgeEntryVersion.deleteMany({ where: { entry: { workspaceId: workspace.id } } });
|
|
await tx.knowledgeEntry.deleteMany({ where: { workspaceId: workspace.id } });
|
|
await tx.knowledgeTag.deleteMany({ where: { workspaceId: workspace.id } });
|
|
|
|
// Create knowledge tags
|
|
const tags = await Promise.all([
|
|
tx.knowledgeTag.create({
|
|
data: {
|
|
workspaceId: workspace.id,
|
|
name: "Architecture",
|
|
slug: "architecture",
|
|
color: "#3B82F6",
|
|
description: "System architecture and design decisions",
|
|
},
|
|
}),
|
|
tx.knowledgeTag.create({
|
|
data: {
|
|
workspaceId: workspace.id,
|
|
name: "Development",
|
|
slug: "development",
|
|
color: "#10B981",
|
|
description: "Development practices and guidelines",
|
|
},
|
|
}),
|
|
tx.knowledgeTag.create({
|
|
data: {
|
|
workspaceId: workspace.id,
|
|
name: "Getting Started",
|
|
slug: "getting-started",
|
|
color: "#F59E0B",
|
|
description: "Onboarding and setup guides",
|
|
},
|
|
}),
|
|
]);
|
|
|
|
console.log(`Created ${tags.length} knowledge tags`);
|
|
|
|
// Create knowledge entries
|
|
const entries = [
|
|
{
|
|
slug: "welcome",
|
|
title: "Welcome to Mosaic Stack Knowledge Base",
|
|
content: `# Welcome to Mosaic Stack
|
|
|
|
This is the knowledge base for the Mosaic Stack project. Here you'll find:
|
|
|
|
- **[[architecture-overview]]** - High-level system architecture
|
|
- **[[development-setup]]** - Getting started with development
|
|
- **[[database-schema]]** - Database design and conventions
|
|
|
|
## About This Knowledge Base
|
|
|
|
The Knowledge Module provides:
|
|
- Wiki-style linking between entries
|
|
- Full-text and semantic search
|
|
- Version history and change tracking
|
|
- Tag-based organization
|
|
|
|
Start exploring by following the links above!`,
|
|
summary: "Introduction to the Mosaic Stack knowledge base and navigation guide",
|
|
status: EntryStatus.PUBLISHED,
|
|
visibility: Visibility.WORKSPACE,
|
|
tags: ["getting-started"],
|
|
},
|
|
{
|
|
slug: "architecture-overview",
|
|
title: "Architecture Overview",
|
|
content: `# Architecture Overview
|
|
|
|
The Mosaic Stack is built on a modern, scalable architecture:
|
|
|
|
## Stack Components
|
|
|
|
- **Frontend**: Next.js 15+ with React 19
|
|
- **Backend**: NestJS with Prisma ORM
|
|
- **Database**: PostgreSQL 17 with pgvector
|
|
- **Cache**: Valkey (Redis fork)
|
|
|
|
## Key Modules
|
|
|
|
1. **Task Management** - See [[development-setup]] for local setup
|
|
2. **Event Calendar** - Integrated scheduling
|
|
3. **Knowledge Base** - This module! See [[database-schema]]
|
|
4. **Agent Orchestration** - AI agent coordination
|
|
|
|
The database schema is documented in [[database-schema]].`,
|
|
summary: "High-level overview of Mosaic Stack architecture and components",
|
|
status: EntryStatus.PUBLISHED,
|
|
visibility: Visibility.WORKSPACE,
|
|
tags: ["architecture"],
|
|
},
|
|
{
|
|
slug: "development-setup",
|
|
title: "Development Setup Guide",
|
|
content: `# Development Setup Guide
|
|
|
|
## Prerequisites
|
|
|
|
- Node.js 22+
|
|
- PostgreSQL 17
|
|
- pnpm 9+
|
|
|
|
## Quick Start
|
|
|
|
\`\`\`bash
|
|
# Clone the repository
|
|
git clone https://git.mosaicstack.dev/mosaic/stack.git
|
|
|
|
# Install dependencies
|
|
pnpm install
|
|
|
|
# Set up database
|
|
cd apps/api
|
|
pnpm prisma migrate dev
|
|
pnpm prisma:seed
|
|
|
|
# Start development servers
|
|
pnpm dev
|
|
\`\`\`
|
|
|
|
## Architecture
|
|
|
|
Before diving in, review the [[architecture-overview]] to understand the system design.
|
|
|
|
## Database
|
|
|
|
The database schema is documented in [[database-schema]]. All models use Prisma ORM.
|
|
|
|
## Next Steps
|
|
|
|
1. Read the [[architecture-overview]]
|
|
2. Explore the codebase
|
|
3. Check out the [[database-schema]] documentation`,
|
|
summary: "Step-by-step guide to setting up the Mosaic Stack development environment",
|
|
status: EntryStatus.PUBLISHED,
|
|
visibility: Visibility.WORKSPACE,
|
|
tags: ["development", "getting-started"],
|
|
},
|
|
{
|
|
slug: "database-schema",
|
|
title: "Database Schema Documentation",
|
|
content: `# Database Schema Documentation
|
|
|
|
## Overview
|
|
|
|
Mosaic Stack uses PostgreSQL 17 with Prisma ORM. See [[architecture-overview]] for context.
|
|
|
|
## Key Conventions
|
|
|
|
- All IDs are UUIDs
|
|
- Timestamps use \`@db.Timestamptz\` (timezone-aware)
|
|
- Soft deletes via status fields (e.g., ARCHIVED)
|
|
- Relations enforce cascade deletes for data integrity
|
|
|
|
## Core Models
|
|
|
|
### Task Management
|
|
- \`Task\` - Individual work items
|
|
- \`Project\` - Task containers
|
|
- \`Domain\` - High-level categorization
|
|
|
|
### Knowledge Module
|
|
- \`KnowledgeEntry\` - Wiki pages
|
|
- \`KnowledgeLink\` - Page connections
|
|
- \`KnowledgeTag\` - Categorization
|
|
- \`KnowledgeEmbedding\` - Semantic search (pgvector)
|
|
|
|
### Agent System
|
|
- \`Agent\` - AI agent instances
|
|
- \`AgentSession\` - Conversation sessions
|
|
|
|
## Migrations
|
|
|
|
Migrations are managed via Prisma:
|
|
|
|
\`\`\`bash
|
|
# Create migration
|
|
pnpm prisma migrate dev --name my_migration
|
|
|
|
# Apply in production
|
|
pnpm prisma migrate deploy
|
|
\`\`\`
|
|
|
|
For setup instructions, see [[development-setup]].`,
|
|
summary:
|
|
"Comprehensive documentation of the Mosaic Stack database schema and Prisma conventions",
|
|
status: EntryStatus.PUBLISHED,
|
|
visibility: Visibility.WORKSPACE,
|
|
tags: ["architecture", "development"],
|
|
},
|
|
{
|
|
slug: "future-ideas",
|
|
title: "Future Ideas and Roadmap",
|
|
content: `# Future Ideas and Roadmap
|
|
|
|
## Planned Features
|
|
|
|
- Real-time collaboration (CRDT)
|
|
- Advanced graph visualizations
|
|
- AI-powered summarization
|
|
- Mobile app
|
|
|
|
## Research Areas
|
|
|
|
- Vector search optimization
|
|
- Knowledge graph algorithms
|
|
- Agent memory systems
|
|
|
|
This is a draft document. See [[architecture-overview]] for current state.`,
|
|
summary: "Brainstorming document for future features and research directions",
|
|
status: EntryStatus.DRAFT,
|
|
visibility: Visibility.PRIVATE,
|
|
tags: [],
|
|
},
|
|
];
|
|
|
|
// Create entries and track them for linking
|
|
const createdEntries = new Map<string, any>();
|
|
|
|
for (const entryData of entries) {
|
|
const entry = await tx.knowledgeEntry.create({
|
|
data: {
|
|
workspaceId: workspace.id,
|
|
slug: entryData.slug,
|
|
title: entryData.title,
|
|
content: entryData.content,
|
|
summary: entryData.summary,
|
|
status: entryData.status,
|
|
visibility: entryData.visibility,
|
|
createdBy: user.id,
|
|
updatedBy: user.id,
|
|
},
|
|
});
|
|
|
|
createdEntries.set(entryData.slug, entry);
|
|
|
|
// Create initial version
|
|
await tx.knowledgeEntryVersion.create({
|
|
data: {
|
|
entryId: entry.id,
|
|
version: 1,
|
|
title: entry.title,
|
|
content: entry.content,
|
|
summary: entry.summary,
|
|
createdBy: user.id,
|
|
changeNote: "Initial version",
|
|
},
|
|
});
|
|
|
|
// Add tags
|
|
for (const tagSlug of entryData.tags) {
|
|
const tag = tags.find((t) => t.slug === tagSlug);
|
|
if (tag) {
|
|
await tx.knowledgeEntryTag.create({
|
|
data: {
|
|
entryId: entry.id,
|
|
tagId: tag.id,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log(`Created ${entries.length} knowledge entries`);
|
|
|
|
// Create wiki-links between entries
|
|
const links = [
|
|
{ source: "welcome", target: "architecture-overview", text: "architecture-overview" },
|
|
{ source: "welcome", target: "development-setup", text: "development-setup" },
|
|
{ source: "welcome", target: "database-schema", text: "database-schema" },
|
|
{ source: "architecture-overview", target: "development-setup", text: "development-setup" },
|
|
{ source: "architecture-overview", target: "database-schema", text: "database-schema" },
|
|
{
|
|
source: "development-setup",
|
|
target: "architecture-overview",
|
|
text: "architecture-overview",
|
|
},
|
|
{ source: "development-setup", target: "database-schema", text: "database-schema" },
|
|
{ source: "database-schema", target: "architecture-overview", text: "architecture-overview" },
|
|
{ source: "database-schema", target: "development-setup", text: "development-setup" },
|
|
{ source: "future-ideas", target: "architecture-overview", text: "architecture-overview" },
|
|
];
|
|
|
|
for (const link of links) {
|
|
const sourceEntry = createdEntries.get(link.source);
|
|
const targetEntry = createdEntries.get(link.target);
|
|
|
|
if (sourceEntry && targetEntry) {
|
|
await tx.knowledgeLink.create({
|
|
data: {
|
|
sourceId: sourceEntry.id,
|
|
targetId: targetEntry.id,
|
|
linkText: link.text,
|
|
},
|
|
});
|
|
}
|
|
}
|
|
|
|
console.log(`Created ${links.length} knowledge links`);
|
|
});
|
|
console.log("Seeding completed successfully!");
|
|
}
|
|
|
|
main()
|
|
.catch((e) => {
|
|
console.error("Error seeding database:", e);
|
|
process.exit(1);
|
|
})
|
|
.finally(async () => {
|
|
await prisma.$disconnect();
|
|
});
|