Checkpoint: Phase 13: Seed data for agents and orchestrator
- server/seed.ts: 6 default system agents (Orchestrator, Browser, Tool Builder, Agent Compiler, Coder, Researcher) - Idempotent: runs only when agents table is empty - Integrated into server/_core/index.ts startup - server/seed.test.ts: 18 vitest tests, all pass - Total: 69 tests pass (7 test files)
This commit is contained in:
@@ -7,6 +7,7 @@ import { registerOAuthRoutes } from "./oauth";
|
||||
import { appRouter } from "../routers";
|
||||
import { createContext } from "./context";
|
||||
import { serveStatic, setupVite } from "./vite";
|
||||
import { seedDefaults } from "../seed";
|
||||
|
||||
function isPortAvailable(port: number): Promise<boolean> {
|
||||
return new Promise(resolve => {
|
||||
@@ -57,6 +58,9 @@ async function startServer() {
|
||||
console.log(`Port ${preferredPort} is busy, using port ${port} instead`);
|
||||
}
|
||||
|
||||
// Run idempotent seed on every startup (no-op if data already exists)
|
||||
await seedDefaults();
|
||||
|
||||
server.listen(port, () => {
|
||||
console.log(`Server running on http://localhost:${port}/`);
|
||||
});
|
||||
|
||||
194
server/seed.test.ts
Normal file
194
server/seed.test.ts
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Tests for server/seed.ts
|
||||
*
|
||||
* Strategy: mock `./db` so getDb() returns a fake Drizzle-like object,
|
||||
* then assert that seedDefaults() inserts exactly the right agents
|
||||
* (or skips when agents already exist).
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from "vitest";
|
||||
import { DEFAULT_AGENTS, seedDefaults } from "./seed";
|
||||
|
||||
// ─── Mock getDb ───────────────────────────────────────────────────────────────
|
||||
|
||||
const mockInsert = vi.fn();
|
||||
const mockSelect = vi.fn();
|
||||
|
||||
vi.mock("./db", () => ({
|
||||
getDb: vi.fn(),
|
||||
}));
|
||||
|
||||
import { getDb } from "./db";
|
||||
|
||||
// Helper: build a minimal fake db that tracks inserts and returns a given count
|
||||
function makeFakeDb(existingAgentCount: number) {
|
||||
const insertValues = vi.fn().mockResolvedValue([{ insertId: 1 }]);
|
||||
const insertInto = vi.fn().mockReturnValue({ values: insertValues });
|
||||
|
||||
// select().from(). → [{ value: count }]
|
||||
const fromFn = vi.fn().mockResolvedValue([{ value: existingAgentCount }]);
|
||||
const selectFn = vi.fn().mockReturnValue({ from: fromFn });
|
||||
|
||||
return {
|
||||
insert: insertInto,
|
||||
select: selectFn,
|
||||
_insertValues: insertValues,
|
||||
_fromFn: fromFn,
|
||||
};
|
||||
}
|
||||
|
||||
// ─── Tests ────────────────────────────────────────────────────────────────────
|
||||
|
||||
describe("DEFAULT_AGENTS", () => {
|
||||
it("should contain exactly 6 default agents", () => {
|
||||
expect(DEFAULT_AGENTS).toHaveLength(6);
|
||||
});
|
||||
|
||||
it("should have exactly one orchestrator agent", () => {
|
||||
const orchestrators = DEFAULT_AGENTS.filter((a) => a.isOrchestrator);
|
||||
expect(orchestrators).toHaveLength(1);
|
||||
expect(orchestrators[0].name).toBe("GoClaw Orchestrator");
|
||||
});
|
||||
|
||||
it("should mark all agents as system agents", () => {
|
||||
const nonSystem = DEFAULT_AGENTS.filter((a) => !a.isSystem);
|
||||
expect(nonSystem).toHaveLength(0);
|
||||
});
|
||||
|
||||
it("should have unique roles", () => {
|
||||
const roles = DEFAULT_AGENTS.map((a) => a.role);
|
||||
const uniqueRoles = new Set(roles);
|
||||
expect(uniqueRoles.size).toBe(roles.length);
|
||||
});
|
||||
|
||||
it("each agent should have allowedTools array", () => {
|
||||
for (const agent of DEFAULT_AGENTS) {
|
||||
expect(Array.isArray(agent.allowedTools)).toBe(true);
|
||||
expect(agent.allowedTools.length).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
it("orchestrator should have the most tools", () => {
|
||||
const orch = DEFAULT_AGENTS.find((a) => a.isOrchestrator)!;
|
||||
const maxTools = Math.max(...DEFAULT_AGENTS.map((a) => a.allowedTools.length));
|
||||
expect(orch.allowedTools.length).toBe(maxTools);
|
||||
});
|
||||
|
||||
it("each agent should have a non-empty systemPrompt", () => {
|
||||
for (const agent of DEFAULT_AGENTS) {
|
||||
expect(agent.systemPrompt.trim().length).toBeGreaterThan(50);
|
||||
}
|
||||
});
|
||||
|
||||
it("each agent should have valid temperature (0-1 as string)", () => {
|
||||
for (const agent of DEFAULT_AGENTS) {
|
||||
const temp = parseFloat(agent.temperature);
|
||||
expect(temp).toBeGreaterThanOrEqual(0);
|
||||
expect(temp).toBeLessThanOrEqual(1);
|
||||
}
|
||||
});
|
||||
|
||||
it("each agent should have maxTokens > 0", () => {
|
||||
for (const agent of DEFAULT_AGENTS) {
|
||||
expect(agent.maxTokens).toBeGreaterThan(0);
|
||||
}
|
||||
});
|
||||
|
||||
it("orchestrator should have the highest maxTokens", () => {
|
||||
const orch = DEFAULT_AGENTS.find((a) => a.isOrchestrator)!;
|
||||
const maxTok = Math.max(...DEFAULT_AGENTS.map((a) => a.maxTokens));
|
||||
expect(orch.maxTokens).toBe(maxTok);
|
||||
});
|
||||
|
||||
it("each agent should have seeded metadata flag", () => {
|
||||
for (const agent of DEFAULT_AGENTS) {
|
||||
expect(agent.metadata.seeded).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("seedDefaults()", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it("should skip seeding when DB is unavailable", async () => {
|
||||
vi.mocked(getDb).mockResolvedValue(null as any);
|
||||
await expect(seedDefaults()).resolves.toBeUndefined();
|
||||
// No inserts should happen
|
||||
expect(mockInsert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should skip seeding when agents already exist", async () => {
|
||||
const fakeDb = makeFakeDb(3); // 3 agents already exist
|
||||
vi.mocked(getDb).mockResolvedValue(fakeDb as any);
|
||||
|
||||
await seedDefaults();
|
||||
|
||||
// select was called to check count
|
||||
expect(fakeDb.select).toHaveBeenCalled();
|
||||
// insert was NOT called
|
||||
expect(fakeDb.insert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should insert all default agents when table is empty", async () => {
|
||||
const fakeDb = makeFakeDb(0); // empty table
|
||||
vi.mocked(getDb).mockResolvedValue(fakeDb as any);
|
||||
|
||||
await seedDefaults();
|
||||
|
||||
// insert should be called once per agent
|
||||
expect(fakeDb.insert).toHaveBeenCalledTimes(DEFAULT_AGENTS.length);
|
||||
expect(fakeDb._insertValues).toHaveBeenCalledTimes(DEFAULT_AGENTS.length);
|
||||
});
|
||||
|
||||
it("should insert orchestrator agent with isOrchestrator=true", async () => {
|
||||
const fakeDb = makeFakeDb(0);
|
||||
vi.mocked(getDb).mockResolvedValue(fakeDb as any);
|
||||
|
||||
await seedDefaults();
|
||||
|
||||
// Find the call that inserted the orchestrator
|
||||
const calls = fakeDb._insertValues.mock.calls;
|
||||
const orchCall = calls.find((call: any[]) => call[0]?.isOrchestrator === true);
|
||||
expect(orchCall).toBeDefined();
|
||||
expect(orchCall![0].isSystem).toBe(true);
|
||||
expect(orchCall![0].name).toBe("GoClaw Orchestrator");
|
||||
});
|
||||
|
||||
it("should insert all agents with isSystem=true", async () => {
|
||||
const fakeDb = makeFakeDb(0);
|
||||
vi.mocked(getDb).mockResolvedValue(fakeDb as any);
|
||||
|
||||
await seedDefaults();
|
||||
|
||||
const calls = fakeDb._insertValues.mock.calls;
|
||||
for (const call of calls) {
|
||||
expect(call[0].isSystem).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it("should be idempotent — second call with existing data does nothing", async () => {
|
||||
// First call: empty DB → seeds
|
||||
const fakeDb1 = makeFakeDb(0);
|
||||
vi.mocked(getDb).mockResolvedValue(fakeDb1 as any);
|
||||
await seedDefaults();
|
||||
expect(fakeDb1.insert).toHaveBeenCalledTimes(DEFAULT_AGENTS.length);
|
||||
|
||||
// Second call: DB now has agents → skips
|
||||
const fakeDb2 = makeFakeDb(DEFAULT_AGENTS.length);
|
||||
vi.mocked(getDb).mockResolvedValue(fakeDb2 as any);
|
||||
await seedDefaults();
|
||||
expect(fakeDb2.insert).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should not throw when DB insert fails — just log error", async () => {
|
||||
const fakeDb = makeFakeDb(0);
|
||||
// Make insert throw
|
||||
fakeDb._insertValues.mockRejectedValue(new Error("DB error"));
|
||||
vi.mocked(getDb).mockResolvedValue(fakeDb as any);
|
||||
|
||||
// Should not throw
|
||||
await expect(seedDefaults()).resolves.toBeUndefined();
|
||||
});
|
||||
});
|
||||
372
server/seed.ts
Normal file
372
server/seed.ts
Normal file
@@ -0,0 +1,372 @@
|
||||
/**
|
||||
* GoClaw — Default Seed Data
|
||||
*
|
||||
* Idempotent: runs only when the agents table is empty.
|
||||
* Seeds the following system agents:
|
||||
* 1. Orchestrator — main chat agent (isOrchestrator=true, isSystem=true)
|
||||
* 2. Browser Agent — web browsing & scraping
|
||||
* 3. Tool Builder — LLM-powered tool generator
|
||||
* 4. Agent Compiler — LLM-powered agent factory
|
||||
* 5. Coder Agent — code writing & review
|
||||
* 6. Researcher — deep research & summarisation
|
||||
*
|
||||
* All system agents use userId=0 (reserved system owner).
|
||||
*/
|
||||
|
||||
import { eq, count } from "drizzle-orm";
|
||||
import { getDb } from "./db";
|
||||
import { agents } from "../drizzle/schema";
|
||||
|
||||
// ─── Seed Definitions ────────────────────────────────────────────────────────
|
||||
|
||||
const SYSTEM_USER_ID = 0; // Reserved: system-owned agents
|
||||
|
||||
export interface SeedAgent {
|
||||
name: string;
|
||||
description: string;
|
||||
role: string;
|
||||
model: string;
|
||||
provider: string;
|
||||
temperature: string;
|
||||
maxTokens: number;
|
||||
topP: string;
|
||||
frequencyPenalty: string;
|
||||
presencePenalty: string;
|
||||
systemPrompt: string;
|
||||
allowedTools: string[];
|
||||
allowedDomains: string[];
|
||||
maxRequestsPerHour: number;
|
||||
isActive: boolean;
|
||||
isPublic: boolean;
|
||||
isSystem: boolean;
|
||||
isOrchestrator: boolean;
|
||||
tags: string[];
|
||||
metadata: Record<string, any>;
|
||||
}
|
||||
|
||||
export const DEFAULT_AGENTS: SeedAgent[] = [
|
||||
// ── 1. Orchestrator ────────────────────────────────────────────────────────
|
||||
{
|
||||
name: "GoClaw Orchestrator",
|
||||
description:
|
||||
"Главный AI-агент системы. Управляет всеми специализированными агентами, выполняет системные задачи, отвечает на вопросы пользователей.",
|
||||
role: "orchestrator",
|
||||
model: "qwen2.5:7b",
|
||||
provider: "ollama",
|
||||
temperature: "0.50",
|
||||
maxTokens: 8192,
|
||||
topP: "1.00",
|
||||
frequencyPenalty: "0.00",
|
||||
presencePenalty: "0.00",
|
||||
systemPrompt: `You are GoClaw Orchestrator — the main AI agent managing the GoClaw distributed AI system.
|
||||
|
||||
You have full access to:
|
||||
1. **Specialized Agents**: Browser Agent (web browsing), Tool Builder (create tools), Agent Compiler (create agents)
|
||||
2. **System Tools**: shell_exec (run commands), file_read/write (manage files), http_request (web requests), docker_exec (Docker management)
|
||||
3. **Skills Registry**: list_skills (see capabilities), install_skill (add new capabilities)
|
||||
|
||||
Your responsibilities:
|
||||
- Answer user questions directly when possible
|
||||
- Delegate complex web tasks to Browser Agent
|
||||
- Delegate tool creation to Tool Builder agent
|
||||
- Delegate agent creation to Agent Compiler
|
||||
- Execute shell commands to manage the system, install packages, run scripts
|
||||
- Read and write files to modify the codebase
|
||||
- Monitor Docker containers and services
|
||||
|
||||
Decision making:
|
||||
- For simple questions: answer directly without tools
|
||||
- For web research: use delegate_to_agent with Browser Agent
|
||||
- For creating tools: use delegate_to_agent with Tool Builder
|
||||
- For creating agents: use delegate_to_agent with Agent Compiler
|
||||
- For system tasks: use shell_exec, file_read/write
|
||||
- For Docker: use docker_exec
|
||||
- Always use list_agents first if you're unsure which agent to delegate to
|
||||
|
||||
Response style:
|
||||
- Be concise and actionable
|
||||
- Show what tools you used and their results
|
||||
- If a task requires multiple steps, execute them in sequence
|
||||
- Respond in the same language as the user
|
||||
|
||||
You are running on a Linux server with Node.js, Docker, and full internet access.`,
|
||||
allowedTools: [
|
||||
"shell_exec",
|
||||
"file_read",
|
||||
"file_write",
|
||||
"http_get",
|
||||
"http_post",
|
||||
"docker_list",
|
||||
"docker_exec",
|
||||
"docker_logs",
|
||||
"browser_navigate",
|
||||
"browser_screenshot",
|
||||
],
|
||||
allowedDomains: [],
|
||||
maxRequestsPerHour: 500,
|
||||
isActive: true,
|
||||
isPublic: false,
|
||||
isSystem: true,
|
||||
isOrchestrator: true,
|
||||
tags: ["system", "orchestrator", "main"],
|
||||
metadata: { seeded: true, version: "1.0" },
|
||||
},
|
||||
|
||||
// ── 2. Browser Agent ───────────────────────────────────────────────────────
|
||||
{
|
||||
name: "Browser Agent",
|
||||
description:
|
||||
"Специализированный агент для веб-навигации, парсинга сайтов и сбора информации из интернета.",
|
||||
role: "browser",
|
||||
model: "qwen2.5:7b",
|
||||
provider: "ollama",
|
||||
temperature: "0.30",
|
||||
maxTokens: 4096,
|
||||
topP: "1.00",
|
||||
frequencyPenalty: "0.00",
|
||||
presencePenalty: "0.00",
|
||||
systemPrompt: `You are the Browser Agent for GoClaw. Your specialty is web browsing, scraping, and information gathering.
|
||||
|
||||
Your capabilities:
|
||||
- Navigate to any URL and extract content
|
||||
- Take screenshots of web pages
|
||||
- Search for information online
|
||||
- Parse and summarise web content
|
||||
|
||||
Guidelines:
|
||||
- Always verify information from multiple sources when possible
|
||||
- Return structured, clean data
|
||||
- Report errors clearly if a page is inaccessible
|
||||
- Respect robots.txt and rate limits
|
||||
- Respond in the same language as the user`,
|
||||
allowedTools: ["browser_navigate", "browser_screenshot", "http_get"],
|
||||
allowedDomains: [],
|
||||
maxRequestsPerHour: 200,
|
||||
isActive: true,
|
||||
isPublic: false,
|
||||
isSystem: true,
|
||||
isOrchestrator: false,
|
||||
tags: ["system", "browser", "web"],
|
||||
metadata: { seeded: true, version: "1.0" },
|
||||
},
|
||||
|
||||
// ── 3. Tool Builder ────────────────────────────────────────────────────────
|
||||
{
|
||||
name: "Tool Builder",
|
||||
description:
|
||||
"Создаёт новые инструменты для агентов с помощью LLM. Генерирует код, тестирует и регистрирует инструменты в реестре.",
|
||||
role: "tool_builder",
|
||||
model: "qwen2.5:7b",
|
||||
provider: "ollama",
|
||||
temperature: "0.20",
|
||||
maxTokens: 4096,
|
||||
topP: "1.00",
|
||||
frequencyPenalty: "0.00",
|
||||
presencePenalty: "0.00",
|
||||
systemPrompt: `You are the Tool Builder Agent for GoClaw. You create new tools that extend the capabilities of other agents.
|
||||
|
||||
Your workflow:
|
||||
1. Understand the tool requirement from the user description
|
||||
2. Generate a clean JavaScript/TypeScript function implementation
|
||||
3. Define proper parameter schemas (JSON Schema format)
|
||||
4. Validate the implementation for safety and correctness
|
||||
5. Register the tool in the tool registry
|
||||
|
||||
Guidelines:
|
||||
- Write clean, well-commented code
|
||||
- Always validate input parameters
|
||||
- Handle errors gracefully with descriptive messages
|
||||
- Mark dangerous tools explicitly (file system writes, shell execution, network calls)
|
||||
- Test with edge cases before finalising
|
||||
- Respond in the same language as the user`,
|
||||
allowedTools: ["file_read", "file_write", "shell_exec"],
|
||||
allowedDomains: [],
|
||||
maxRequestsPerHour: 100,
|
||||
isActive: true,
|
||||
isPublic: false,
|
||||
isSystem: true,
|
||||
isOrchestrator: false,
|
||||
tags: ["system", "tool-builder", "meta"],
|
||||
metadata: { seeded: true, version: "1.0" },
|
||||
},
|
||||
|
||||
// ── 4. Agent Compiler ──────────────────────────────────────────────────────
|
||||
{
|
||||
name: "Agent Compiler",
|
||||
description:
|
||||
"Создаёт новых специализированных агентов по техническому заданию. Настраивает модель, системный промпт, инструменты и параметры.",
|
||||
role: "agent_compiler",
|
||||
model: "qwen2.5:7b",
|
||||
provider: "ollama",
|
||||
temperature: "0.30",
|
||||
maxTokens: 4096,
|
||||
topP: "1.00",
|
||||
frequencyPenalty: "0.00",
|
||||
presencePenalty: "0.00",
|
||||
systemPrompt: `You are the Agent Compiler for GoClaw. You create new specialised AI agents from natural language specifications.
|
||||
|
||||
Your workflow:
|
||||
1. Parse the user's technical specification (ТЗ)
|
||||
2. Determine the optimal LLM model and parameters
|
||||
3. Write a focused system prompt for the new agent
|
||||
4. Select appropriate tools from the tool registry
|
||||
5. Configure access controls and rate limits
|
||||
6. Deploy the agent to the system
|
||||
|
||||
Guidelines:
|
||||
- Keep system prompts focused and specific
|
||||
- Choose the minimum set of tools needed for the agent's purpose
|
||||
- Set conservative rate limits for new agents
|
||||
- Validate the agent configuration before deployment
|
||||
- Respond in the same language as the user`,
|
||||
allowedTools: ["file_read", "file_write"],
|
||||
allowedDomains: [],
|
||||
maxRequestsPerHour: 50,
|
||||
isActive: true,
|
||||
isPublic: false,
|
||||
isSystem: true,
|
||||
isOrchestrator: false,
|
||||
tags: ["system", "agent-compiler", "meta"],
|
||||
metadata: { seeded: true, version: "1.0" },
|
||||
},
|
||||
|
||||
// ── 5. Coder Agent ─────────────────────────────────────────────────────────
|
||||
{
|
||||
name: "Coder Agent",
|
||||
description:
|
||||
"Специализированный агент для написания, проверки и рефакторинга кода. Поддерживает TypeScript, Python, Go, Bash и другие языки.",
|
||||
role: "coder",
|
||||
model: "qwen2.5:7b",
|
||||
provider: "ollama",
|
||||
temperature: "0.10",
|
||||
maxTokens: 8192,
|
||||
topP: "1.00",
|
||||
frequencyPenalty: "0.00",
|
||||
presencePenalty: "0.00",
|
||||
systemPrompt: `You are the Coder Agent for GoClaw. You specialise in writing, reviewing, and refactoring code.
|
||||
|
||||
Your capabilities:
|
||||
- Write clean, production-ready code in TypeScript, JavaScript, Python, Go, Bash, SQL
|
||||
- Review code for bugs, security issues, and performance problems
|
||||
- Refactor existing code for clarity and maintainability
|
||||
- Write unit tests and documentation
|
||||
- Execute code and verify output
|
||||
|
||||
Guidelines:
|
||||
- Always write type-safe code with proper error handling
|
||||
- Follow language-specific best practices and conventions
|
||||
- Add meaningful comments for complex logic
|
||||
- Prefer readable code over clever one-liners
|
||||
- Test your code before presenting it as final
|
||||
- Respond in the same language as the user`,
|
||||
allowedTools: ["file_read", "file_write", "shell_exec"],
|
||||
allowedDomains: [],
|
||||
maxRequestsPerHour: 300,
|
||||
isActive: true,
|
||||
isPublic: false,
|
||||
isSystem: true,
|
||||
isOrchestrator: false,
|
||||
tags: ["system", "coder", "development"],
|
||||
metadata: { seeded: true, version: "1.0" },
|
||||
},
|
||||
|
||||
// ── 6. Researcher ──────────────────────────────────────────────────────────
|
||||
{
|
||||
name: "Researcher",
|
||||
description:
|
||||
"Агент для глубокого исследования тем. Собирает информацию из множества источников, анализирует и создаёт структурированные отчёты.",
|
||||
role: "researcher",
|
||||
model: "qwen2.5:7b",
|
||||
provider: "ollama",
|
||||
temperature: "0.40",
|
||||
maxTokens: 8192,
|
||||
topP: "1.00",
|
||||
frequencyPenalty: "0.00",
|
||||
presencePenalty: "0.00",
|
||||
systemPrompt: `You are the Researcher Agent for GoClaw. You specialise in deep research and knowledge synthesis.
|
||||
|
||||
Your capabilities:
|
||||
- Search and browse multiple web sources
|
||||
- Analyse and cross-reference information
|
||||
- Create structured research reports
|
||||
- Summarise complex topics clearly
|
||||
- Identify key facts, trends, and insights
|
||||
|
||||
Guidelines:
|
||||
- Always cite your sources
|
||||
- Distinguish between facts and opinions
|
||||
- Present information in a structured, readable format
|
||||
- Acknowledge uncertainty when sources conflict
|
||||
- Provide actionable insights when possible
|
||||
- Respond in the same language as the user`,
|
||||
allowedTools: ["browser_navigate", "browser_screenshot", "http_get"],
|
||||
allowedDomains: [],
|
||||
maxRequestsPerHour: 200,
|
||||
isActive: true,
|
||||
isPublic: false,
|
||||
isSystem: true,
|
||||
isOrchestrator: false,
|
||||
tags: ["system", "researcher", "analysis"],
|
||||
metadata: { seeded: true, version: "1.0" },
|
||||
},
|
||||
];
|
||||
|
||||
// ─── Seed Function ────────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Seed default agents if the agents table is empty.
|
||||
* Idempotent: safe to call on every server startup.
|
||||
*/
|
||||
export async function seedDefaults(): Promise<void> {
|
||||
const db = await getDb();
|
||||
if (!db) {
|
||||
console.warn("[Seed] Database not available — skipping seed");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if any agents already exist (including system agents)
|
||||
const [{ value: existingCount }] = await db
|
||||
.select({ value: count() })
|
||||
.from(agents);
|
||||
|
||||
if (Number(existingCount) > 0) {
|
||||
console.log(`[Seed] Skipping — ${existingCount} agent(s) already exist`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("[Seed] No agents found — seeding default agents...");
|
||||
|
||||
for (const agentDef of DEFAULT_AGENTS) {
|
||||
await db.insert(agents).values({
|
||||
userId: SYSTEM_USER_ID,
|
||||
name: agentDef.name,
|
||||
description: agentDef.description,
|
||||
role: agentDef.role,
|
||||
model: agentDef.model,
|
||||
provider: agentDef.provider,
|
||||
temperature: agentDef.temperature,
|
||||
maxTokens: agentDef.maxTokens,
|
||||
topP: agentDef.topP,
|
||||
frequencyPenalty: agentDef.frequencyPenalty,
|
||||
presencePenalty: agentDef.presencePenalty,
|
||||
systemPrompt: agentDef.systemPrompt,
|
||||
allowedTools: agentDef.allowedTools,
|
||||
allowedDomains: agentDef.allowedDomains,
|
||||
maxRequestsPerHour: agentDef.maxRequestsPerHour,
|
||||
isActive: agentDef.isActive,
|
||||
isPublic: agentDef.isPublic,
|
||||
isSystem: agentDef.isSystem,
|
||||
isOrchestrator: agentDef.isOrchestrator,
|
||||
tags: agentDef.tags,
|
||||
metadata: agentDef.metadata,
|
||||
});
|
||||
console.log(`[Seed] ✓ Created agent: ${agentDef.name}`);
|
||||
}
|
||||
|
||||
console.log(`[Seed] Done — ${DEFAULT_AGENTS.length} default agents created`);
|
||||
} catch (error) {
|
||||
console.error("[Seed] Failed to seed default agents:", error);
|
||||
// Non-fatal: server continues even if seed fails
|
||||
}
|
||||
}
|
||||
14
todo.md
14
todo.md
@@ -173,4 +173,16 @@
|
||||
- [ ] Update Nodes.tsx: real data from tRPC + auto-refresh every 5 seconds
|
||||
- [ ] Show: node ID, hostname, status, role (manager/worker), availability, CPU, RAM, Docker version, IP
|
||||
- [ ] Show live indicator (green pulse) when data is fresh
|
||||
- [ ] Deploy to server 2.59.219.61
|
||||
- [x] Deploy to server 2.59.219.61
|
||||
- [x] Docker API client: /api/nodes, /api/nodes/stats
|
||||
- [x] tRPC nodes.list, nodes.stats procedures
|
||||
- [x] Nodes.tsx rewritten with real data + auto-refresh 10s/15s
|
||||
- [x] 14 vitest tests for nodes procedures
|
||||
|
||||
## Phase 13: Seed Data for Agents & Orchestrator
|
||||
- [x] Create server/seed.ts with default agents (orchestrator, coder, browser, researcher)
|
||||
- [x] Create default orchestrator config seed
|
||||
- [x] Integrate seed into server startup (idempotent — runs only when tables are empty)
|
||||
- [x] Write vitest tests for seed logic (18 tests, all pass)
|
||||
- [ ] Commit to Gitea and deploy to production server
|
||||
- [ ] Verify seed data on production DB
|
||||
|
||||
Reference in New Issue
Block a user