@humanclaw/humanclaw 1.2.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/db/connection.ts","../src/db/schema.ts","../src/routes/nodes.ts","../src/models/agent.ts","../src/utils/trace-id.ts","../src/routes/jobs.ts","../src/models/task.ts","../src/models/job.ts","../src/services/dispatch.ts","../src/llm/claude.ts","../src/llm/openai.ts","../src/models/config.ts","../src/llm/index.ts","../src/services/planner.ts","../src/routes/tasks.ts","../src/services/resume.ts","../src/services/simulator.ts","../src/routes/sync.ts","../src/services/reviewer.ts","../src/routes/config.ts","../src/dashboard.ts"],"sourcesContent":["import { Command } from 'commander';\nimport * as p from '@clack/prompts';\nimport chalk from 'chalk';\nimport { startServer } from './server.js';\nimport { getDb } from './db/connection.js';\nimport { initSchema } from './db/schema.js';\nimport { createAgent, listAgents } from './models/agent.js';\nimport { listActiveJobs } from './models/job.js';\nimport { markOverdueTasks } from './models/task.js';\nimport { dispatchJob } from './services/dispatch.js';\nimport { planJob } from './services/planner.js';\nimport { generateId } from './utils/trace-id.js';\nimport type { AgentStatus } from './models/types.js';\n\nconst program = new Command();\n\nprogram\n .name('humanclaw')\n .description(\n 'Carbon-based node orchestration framework - treating humans as distributed worker nodes'\n )\n .version('1.0.0');\n\n// ─── serve ───────────────────────────────────────────────────────────────────\n\nprogram\n .command('serve')\n .description('Start the HumanClaw server')\n .option('-p, --port <port>', 'Server port', '2026')\n .action(opts => {\n const port = parseInt(opts.port, 10);\n startServer(port);\n });\n\n// ─── agent ───────────────────────────────────────────────────────────────────\n\nconst agentCmd = program\n .command('agent')\n .description('Manage carbon-based nodes (HumanAgent)');\n\nagentCmd\n .command('add')\n .description('Register a new carbon-based node')\n .action(async () => {\n const db = getDb();\n initSchema(db);\n\n p.intro(chalk.bgCyan(' Register New Carbon-Based Node '));\n\n const name = await p.text({\n message: 'Node alias (name):',\n placeholder: 'e.g. Frontend Lao Li',\n validate: v => (!v ? 'Name is required' : undefined),\n });\n\n if (p.isCancel(name)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n\n const capInput = await p.text({\n message: 'Capabilities (comma-separated):',\n placeholder: 'e.g. UI/UX, Frontend Dev, Stress Resistant',\n validate: v => (!v ? 'At least one capability required' : undefined),\n });\n\n if (p.isCancel(capInput)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n\n const capabilities = (capInput as string)\n .split(',')\n .map(s => s.trim())\n .filter(Boolean);\n\n const relInput = await p.text({\n message: 'Relationship with you (optional):',\n placeholder: 'e.g. direct report / intern / contractor',\n defaultValue: '',\n });\n\n if (p.isCancel(relInput)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n\n const agent = createAgent({\n agent_id: generateId('emp'),\n name: name as string,\n capabilities,\n relationship: (relInput as string) || '',\n status: 'IDLE',\n });\n\n p.outro(\n `${chalk.green('Node registered!')} ID: ${chalk.bold(agent.agent_id)}`\n );\n });\n\nagentCmd\n .command('list')\n .description('Show fleet status')\n .action(() => {\n const db = getDb();\n initSchema(db);\n\n const agents = listAgents();\n if (agents.length === 0) {\n console.log(chalk.dim(' No carbon-based nodes registered.'));\n return;\n }\n\n const statusIcon: Record<AgentStatus, string> = {\n IDLE: '🟢',\n BUSY: '🟡',\n OFFLINE: '🔴',\n OOM: '🟣',\n };\n\n console.log(chalk.bold('\\n Carbon Compute Pool\\n'));\n\n for (const agent of agents) {\n const icon = statusIcon[agent.status];\n const caps = agent.capabilities.join(', ');\n console.log(\n ` ${icon} ${chalk.bold(agent.name)} (${chalk.dim(agent.agent_id)})`\n );\n console.log(` Capabilities: ${chalk.cyan(caps)}`);\n console.log(` Status: ${agent.status}\\n`);\n }\n });\n\n// ─── status ──────────────────────────────────────────────────────────────────\n\nprogram\n .command('status')\n .description('Show active jobs overview')\n .action(() => {\n const db = getDb();\n initSchema(db);\n\n markOverdueTasks();\n const jobs = listActiveJobs();\n\n if (jobs.length === 0) {\n console.log(chalk.dim('\\n No active jobs.\\n'));\n return;\n }\n\n console.log(chalk.bold('\\n Async Orchestration Dashboard\\n'));\n\n for (const job of jobs) {\n const resolved = job.tasks.filter(t => t.status === 'RESOLVED').length;\n const total = job.tasks.length;\n const pct = Math.round((resolved / total) * 100);\n const bar = '█'.repeat(Math.round(pct / 5)) + '░'.repeat(20 - Math.round(pct / 5));\n\n console.log(` ${chalk.bold(job.job_id)} - ${job.original_prompt}`);\n console.log(` Progress: [${bar}] ${resolved}/${total} (${pct}%)`);\n\n for (const task of job.tasks) {\n const statusColor =\n task.status === 'RESOLVED'\n ? chalk.green\n : task.status === 'OVERDUE'\n ? chalk.red\n : chalk.yellow;\n console.log(\n ` ${statusColor(task.status.padEnd(10))} ${task.trace_id} -> ${chalk.dim(task.assignee_id)}`\n );\n console.log(` ${task.todo_description}`);\n }\n console.log();\n }\n });\n\n// ─── plan ───────────────────────────────────────────────────────────────────\n\nprogram\n .command('plan')\n .description('AI-powered task planning from natural language')\n .argument('[prompt]', 'What you want to get done')\n .option('--agents <ids>', 'Comma-separated agent IDs (default: all IDLE)')\n .option('--dispatch', 'Automatically dispatch after planning')\n .action(async (promptArg?: string, opts?: { agents?: string; dispatch?: boolean }) => {\n const db = getDb();\n initSchema(db);\n\n p.intro(chalk.bgCyan(' AI 智能规划 '));\n\n let prompt = promptArg;\n if (!prompt) {\n const input = await p.text({\n message: '输入你的需求:',\n placeholder: '例: 完成首页重构,包括导航栏和内容区的响应式改版',\n validate: v => (!v ? '需求描述不能为空' : undefined),\n });\n if (p.isCancel(input)) {\n p.cancel('已取消');\n process.exit(0);\n }\n prompt = input as string;\n }\n\n const agentIds = opts?.agents?.split(',').map(s => s.trim()).filter(Boolean);\n\n const spin = p.spinner();\n spin.start('AI 正在分析需求、匹配节点、生成话术...');\n\n try {\n const plan = await planJob({ prompt, agent_ids: agentIds }, undefined, db);\n spin.stop('规划完成!');\n\n console.log(chalk.bold(`\\n 需求: ${chalk.white(plan.original_prompt)}\\n`));\n\n for (const task of plan.planned_tasks) {\n console.log(chalk.cyan(` ┌─ ${chalk.bold(task.assignee_name)} (${chalk.dim(task.assignee_id)})`));\n console.log(chalk.cyan(' │') + ` 任务: ${task.todo_description}`);\n console.log(chalk.cyan(' │') + ` 话术: ${chalk.italic(task.briefing)}`);\n console.log(chalk.cyan(' │') + ` 截止: ${new Date(task.deadline).toLocaleString()}`);\n console.log(chalk.cyan(' └─'));\n console.log();\n }\n\n if (opts?.dispatch) {\n const tasks = plan.planned_tasks.map(t => ({\n assignee_id: t.assignee_id,\n todo_description: t.todo_description,\n deadline: t.deadline,\n }));\n const job = dispatchJob({\n original_prompt: plan.original_prompt,\n tasks,\n }, db);\n p.outro(`${chalk.green('已分发!')} Job: ${chalk.bold(job.job_id)}`);\n } else {\n const confirm = await p.confirm({\n message: '确认分发这些任务?',\n });\n if (p.isCancel(confirm) || !confirm) {\n p.outro(chalk.dim('已取消分发'));\n process.exit(0);\n }\n const tasks = plan.planned_tasks.map(t => ({\n assignee_id: t.assignee_id,\n todo_description: t.todo_description,\n deadline: t.deadline,\n }));\n const job = dispatchJob({\n original_prompt: plan.original_prompt,\n tasks,\n }, db);\n p.outro(`${chalk.green('已分发!')} Job: ${chalk.bold(job.job_id)}`);\n }\n } catch (error) {\n spin.stop('规划失败');\n const message = error instanceof Error ? error.message : 'Unknown error';\n p.outro(chalk.red(message));\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import express from 'express';\nimport cors from 'cors';\nimport { getDb } from './db/connection.js';\nimport { initSchema } from './db/schema.js';\nimport nodesRouter from './routes/nodes.js';\nimport jobsRouter from './routes/jobs.js';\nimport tasksRouter from './routes/tasks.js';\nimport syncRouter from './routes/sync.js';\nimport configRouter from './routes/config.js';\nimport { getDashboardHtml } from './dashboard.js';\n\nexport function createServer(port = 2026) {\n // Initialize database\n const db = getDb();\n initSchema(db);\n\n const app = express();\n\n // Middleware\n app.use(cors());\n app.use(express.json({ limit: '10mb' }));\n\n // API routes\n app.use('/api/v1/nodes', nodesRouter);\n app.use('/api/v1/jobs', jobsRouter);\n app.use('/api/v1/tasks', tasksRouter);\n app.use('/api/v1/jobs', syncRouter);\n app.use('/api/v1/config', configRouter);\n\n // Serve dashboard as inline HTML (no build step needed)\n const dashboardHtml = getDashboardHtml();\n app.get('/', (_req, res) => {\n res.type('html').send(dashboardHtml);\n });\n\n // Error handler\n app.use(\n (\n err: Error,\n _req: express.Request,\n res: express.Response,\n _next: express.NextFunction\n ) => {\n console.error('Server error:', err.message);\n res.status(500).json({ error: 'Internal server error' });\n }\n );\n\n return { app, port };\n}\n\nexport function startServer(port = 2026) {\n const { app } = createServer(port);\n\n app.listen(port, () => {\n console.log(`\\n HumanClaw server running at http://localhost:${port}`);\n console.log(` Dashboard: http://localhost:${port}`);\n console.log(` API base: http://localhost:${port}/api/v1\\n`);\n });\n}\n","import Database from 'better-sqlite3';\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nlet db: Database.Database | null = null;\n\nconst DEFAULT_DB_PATH = path.join(\n process.env.HUMANCLAW_DATA_DIR ?? process.cwd(),\n 'humanclaw.db'\n);\n\nexport function getDb(dbPath?: string): Database.Database {\n if (db) return db;\n\n const resolvedPath = dbPath ?? DEFAULT_DB_PATH;\n const dir = path.dirname(resolvedPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n db = new Database(resolvedPath);\n db.pragma('journal_mode = WAL');\n db.pragma('foreign_keys = ON');\n\n return db;\n}\n\nexport function createInMemoryDb(): Database.Database {\n const memDb = new Database(':memory:');\n memDb.pragma('foreign_keys = ON');\n return memDb;\n}\n\nexport function closeDb(): void {\n if (db) {\n db.close();\n db = null;\n }\n}\n\nexport function setDb(newDb: Database.Database): void {\n db = newDb;\n}\n","import type Database from 'better-sqlite3';\n\nexport function initSchema(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS agents (\n agent_id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n capabilities TEXT NOT NULL DEFAULT '[]',\n relationship TEXT NOT NULL DEFAULT '',\n status TEXT NOT NULL DEFAULT 'IDLE'\n CHECK (status IN ('IDLE', 'BUSY', 'OFFLINE', 'OOM')),\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE TABLE IF NOT EXISTS jobs (\n job_id TEXT PRIMARY KEY,\n original_prompt TEXT NOT NULL,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE TABLE IF NOT EXISTS tasks (\n trace_id TEXT PRIMARY KEY,\n job_id TEXT NOT NULL REFERENCES jobs(job_id) ON DELETE CASCADE,\n assignee_id TEXT NOT NULL REFERENCES agents(agent_id),\n todo_description TEXT NOT NULL,\n deadline TEXT NOT NULL,\n payload TEXT NOT NULL DEFAULT '{}',\n status TEXT NOT NULL DEFAULT 'PENDING'\n CHECK (status IN ('PENDING', 'DISPATCHED', 'RESOLVED', 'OVERDUE')),\n result_data TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE INDEX IF NOT EXISTS idx_tasks_job_id ON tasks(job_id);\n CREATE INDEX IF NOT EXISTS idx_tasks_assignee_id ON tasks(assignee_id);\n CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);\n\n CREATE TABLE IF NOT EXISTS config (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n `);\n\n // Migration: add relationship column to existing agents table\n const cols = db.prepare(\"PRAGMA table_info(agents)\").all() as Array<{ name: string }>;\n if (!cols.some(c => c.name === 'relationship')) {\n db.exec(`ALTER TABLE agents ADD COLUMN relationship TEXT NOT NULL DEFAULT ''`);\n }\n}\n","import { Router } from 'express';\nimport {\n listAgentsWithMetrics,\n createAgent,\n updateAgentStatus,\n deleteAgent,\n} from '../models/agent.js';\nimport { generateId } from '../utils/trace-id.js';\nimport type { AgentStatus } from '../models/types.js';\n\nconst router = Router();\n\n// GET /api/v1/nodes/status - Fleet status with metrics\nrouter.get('/status', (_req, res) => {\n const agents = listAgentsWithMetrics();\n res.json({\n total: agents.length,\n idle: agents.filter(a => a.status === 'IDLE').length,\n busy: agents.filter(a => a.status === 'BUSY').length,\n offline: agents.filter(a => a.status === 'OFFLINE').length,\n oom: agents.filter(a => a.status === 'OOM').length,\n agents,\n });\n});\n\n// POST /api/v1/nodes - Register a new agent\nrouter.post('/', (req, res) => {\n const { name, capabilities, relationship, status } = req.body;\n\n if (!name || !Array.isArray(capabilities)) {\n res.status(400).json({ error: 'name and capabilities[] are required' });\n return;\n }\n\n const agent = createAgent({\n agent_id: generateId('emp'),\n name,\n capabilities,\n relationship: relationship || '',\n status: (status as AgentStatus) ?? 'IDLE',\n });\n\n res.status(201).json(agent);\n});\n\n// PATCH /api/v1/nodes/:agent_id/status - Update agent status\nrouter.patch('/:agent_id/status', (req, res) => {\n const { agent_id } = req.params;\n const { status } = req.body;\n\n const validStatuses: AgentStatus[] = ['IDLE', 'BUSY', 'OFFLINE', 'OOM'];\n if (!validStatuses.includes(status)) {\n res.status(400).json({\n error: `Invalid status. Must be one of: ${validStatuses.join(', ')}`,\n });\n return;\n }\n\n const updated = updateAgentStatus(agent_id, status);\n if (!updated) {\n res.status(404).json({ error: `Agent not found: ${agent_id}` });\n return;\n }\n\n res.json({ agent_id, status });\n});\n\n// DELETE /api/v1/nodes/:agent_id\nrouter.delete('/:agent_id', (req, res) => {\n const { agent_id } = req.params;\n const deleted = deleteAgent(agent_id);\n if (!deleted) {\n res.status(404).json({ error: `Agent not found: ${agent_id}` });\n return;\n }\n res.json({ deleted: agent_id });\n});\n\nexport default router;\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport type {\n HumanAgent,\n AgentRow,\n AgentStatus,\n AgentWithMetrics,\n} from './types.js';\n\nfunction rowToAgent(row: AgentRow): HumanAgent {\n return {\n ...row,\n capabilities: JSON.parse(row.capabilities) as string[],\n };\n}\n\nexport function createAgent(\n agent: Omit<HumanAgent, 'created_at'>,\n db?: Database.Database\n): HumanAgent {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n conn\n .prepare(\n `INSERT INTO agents (agent_id, name, capabilities, relationship, status, created_at)\n VALUES (?, ?, ?, ?, ?, ?)`\n )\n .run(\n agent.agent_id,\n agent.name,\n JSON.stringify(agent.capabilities),\n agent.relationship || '',\n agent.status,\n now\n );\n return { ...agent, created_at: now };\n}\n\nexport function getAgent(\n agentId: string,\n db?: Database.Database\n): HumanAgent | undefined {\n const conn = db ?? getDb();\n const row = conn\n .prepare('SELECT * FROM agents WHERE agent_id = ?')\n .get(agentId) as AgentRow | undefined;\n return row ? rowToAgent(row) : undefined;\n}\n\nexport function listAgents(db?: Database.Database): HumanAgent[] {\n const conn = db ?? getDb();\n const rows = conn.prepare('SELECT * FROM agents ORDER BY created_at').all() as AgentRow[];\n return rows.map(rowToAgent);\n}\n\nexport function updateAgentStatus(\n agentId: string,\n status: AgentStatus,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const result = conn\n .prepare('UPDATE agents SET status = ? WHERE agent_id = ?')\n .run(status, agentId);\n return result.changes > 0;\n}\n\nexport function deleteAgent(\n agentId: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const result = conn\n .prepare('DELETE FROM agents WHERE agent_id = ?')\n .run(agentId);\n return result.changes > 0;\n}\n\nexport function listAgentsWithMetrics(\n db?: Database.Database\n): AgentWithMetrics[] {\n const conn = db ?? getDb();\n const rows = conn\n .prepare(\n `SELECT\n a.*,\n COUNT(CASE WHEN t.status IN ('DISPATCHED', 'PENDING') THEN 1 END) AS active_task_count,\n AVG(\n CASE WHEN t.status = 'RESOLVED'\n THEN (julianday(t.updated_at) - julianday(t.created_at)) * 24\n END\n ) AS avg_delivery_hours\n FROM agents a\n LEFT JOIN tasks t ON a.agent_id = t.assignee_id\n GROUP BY a.agent_id\n ORDER BY a.created_at`\n )\n .all() as (AgentRow & { active_task_count: number; avg_delivery_hours: number | null })[];\n\n return rows.map(row => ({\n ...rowToAgent(row),\n active_task_count: row.active_task_count,\n avg_delivery_hours: row.avg_delivery_hours\n ? Math.round(row.avg_delivery_hours * 100) / 100\n : null,\n }));\n}\n","import { nanoid } from 'nanoid';\n\nexport function generateTraceId(): string {\n const num = Math.floor(Math.random() * 10000)\n .toString()\n .padStart(4, '0');\n return `TK-${num}`;\n}\n\nexport function generateId(prefix: string): string {\n return `${prefix}_${nanoid(8)}`;\n}\n","import { Router } from 'express';\nimport { dispatchJob } from '../services/dispatch.js';\nimport { planJob } from '../services/planner.js';\nimport { listActiveJobs, getJobWithTasks } from '../models/job.js';\nimport { markOverdueTasks } from '../models/task.js';\nimport type { CreateJobRequest, PlanRequest } from '../models/types.js';\n\nconst router = Router();\n\n// POST /api/v1/jobs/create - Create and dispatch a new job\nrouter.post('/create', (req, res) => {\n const body = req.body as CreateJobRequest;\n\n if (!body.original_prompt || !Array.isArray(body.tasks) || body.tasks.length === 0) {\n res.status(400).json({\n error: 'original_prompt and non-empty tasks[] are required',\n });\n return;\n }\n\n for (const task of body.tasks) {\n if (!task.assignee_id || !task.todo_description || !task.deadline) {\n res.status(400).json({\n error: 'Each task requires assignee_id, todo_description, and deadline',\n });\n return;\n }\n }\n\n try {\n const job = dispatchJob(body);\n res.status(201).json(job);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n res.status(400).json({ error: message });\n }\n});\n\n// GET /api/v1/jobs/active - List active jobs for kanban\nrouter.get('/active', (_req, res) => {\n // Mark overdue tasks before returning\n markOverdueTasks();\n const jobs = listActiveJobs();\n res.json({ jobs });\n});\n\n// POST /api/v1/jobs/plan - AI-powered task planning (does NOT dispatch)\nrouter.post('/plan', async (req, res) => {\n const body = req.body as PlanRequest;\n\n if (!body.prompt || typeof body.prompt !== 'string') {\n res.status(400).json({ error: 'prompt is required' });\n return;\n }\n\n try {\n const plan = await planJob(body);\n res.json(plan);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n const status = message.includes('API Key') || message.includes('API key') ? 503 : 400;\n res.status(status).json({ error: message });\n }\n});\n\n// GET /api/v1/jobs/:job_id - Get a single job with tasks\nrouter.get('/:job_id', (req, res) => {\n const { job_id } = req.params;\n const job = getJobWithTasks(job_id);\n if (!job) {\n res.status(404).json({ error: `Job not found: ${job_id}` });\n return;\n }\n res.json(job);\n});\n\nexport default router;\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport type { HumanTask, TaskRow, TaskStatus } from './types.js';\n\nfunction rowToTask(row: TaskRow): HumanTask {\n return {\n ...row,\n payload: JSON.parse(row.payload) as Record<string, unknown>,\n result_data: row.result_data ? JSON.parse(row.result_data) : null,\n };\n}\n\nexport function createTask(\n task: Omit<HumanTask, 'created_at' | 'updated_at' | 'result_data'>,\n db?: Database.Database\n): HumanTask {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n conn\n .prepare(\n `INSERT INTO tasks (trace_id, job_id, assignee_id, todo_description, deadline, payload, status, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n task.trace_id,\n task.job_id,\n task.assignee_id,\n task.todo_description,\n task.deadline,\n JSON.stringify(task.payload),\n task.status,\n now,\n now\n );\n return { ...task, result_data: null, created_at: now, updated_at: now };\n}\n\nexport function getTask(\n traceId: string,\n db?: Database.Database\n): HumanTask | undefined {\n const conn = db ?? getDb();\n const row = conn\n .prepare('SELECT * FROM tasks WHERE trace_id = ?')\n .get(traceId) as TaskRow | undefined;\n return row ? rowToTask(row) : undefined;\n}\n\nexport function listTasksByJob(\n jobId: string,\n db?: Database.Database\n): HumanTask[] {\n const conn = db ?? getDb();\n const rows = conn\n .prepare('SELECT * FROM tasks WHERE job_id = ? ORDER BY created_at')\n .all(jobId) as TaskRow[];\n return rows.map(rowToTask);\n}\n\nexport function listTasksByAssignee(\n assigneeId: string,\n db?: Database.Database\n): HumanTask[] {\n const conn = db ?? getDb();\n const rows = conn\n .prepare('SELECT * FROM tasks WHERE assignee_id = ? ORDER BY created_at')\n .all(assigneeId) as TaskRow[];\n return rows.map(rowToTask);\n}\n\nexport function updateTaskStatus(\n traceId: string,\n status: TaskStatus,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n const result = conn\n .prepare('UPDATE tasks SET status = ?, updated_at = ? WHERE trace_id = ?')\n .run(status, now, traceId);\n return result.changes > 0;\n}\n\nexport function resolveTask(\n traceId: string,\n resultData: unknown,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n const result = conn\n .prepare(\n `UPDATE tasks\n SET status = 'RESOLVED', result_data = ?, updated_at = ?\n WHERE trace_id = ? AND status IN ('DISPATCHED', 'OVERDUE')`\n )\n .run(JSON.stringify(resultData), now, traceId);\n return result.changes > 0;\n}\n\nexport function markOverdueTasks(db?: Database.Database): number {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n const result = conn\n .prepare(\n `UPDATE tasks\n SET status = 'OVERDUE', updated_at = ?\n WHERE status = 'DISPATCHED' AND deadline < ?`\n )\n .run(now, now);\n return result.changes;\n}\n\nexport function resetTaskDeadline(\n traceId: string,\n newDeadline: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n const result = conn\n .prepare(\n `UPDATE tasks\n SET status = 'DISPATCHED', deadline = ?, result_data = NULL, updated_at = ?\n WHERE trace_id = ?`\n )\n .run(newDeadline, now, traceId);\n return result.changes > 0;\n}\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport type { OrchestrationJob, JobRow, JobWithTasks } from './types.js';\nimport { listTasksByJob } from './task.js';\n\nexport function createJob(\n job: OrchestrationJob,\n db?: Database.Database\n): OrchestrationJob {\n const conn = db ?? getDb();\n conn\n .prepare(\n `INSERT INTO jobs (job_id, original_prompt, created_at)\n VALUES (?, ?, ?)`\n )\n .run(job.job_id, job.original_prompt, job.created_at);\n return job;\n}\n\nexport function getJob(\n jobId: string,\n db?: Database.Database\n): OrchestrationJob | undefined {\n const conn = db ?? getDb();\n return conn\n .prepare('SELECT * FROM jobs WHERE job_id = ?')\n .get(jobId) as JobRow | undefined;\n}\n\nexport function getJobWithTasks(\n jobId: string,\n db?: Database.Database\n): JobWithTasks | undefined {\n const conn = db ?? getDb();\n const job = conn\n .prepare('SELECT * FROM jobs WHERE job_id = ?')\n .get(jobId) as JobRow | undefined;\n if (!job) return undefined;\n\n const tasks = listTasksByJob(jobId, conn);\n return { ...job, tasks };\n}\n\nexport function listActiveJobs(db?: Database.Database): JobWithTasks[] {\n const conn = db ?? getDb();\n const jobs = conn\n .prepare(\n `SELECT DISTINCT j.*\n FROM jobs j\n INNER JOIN tasks t ON j.job_id = t.job_id\n WHERE t.status != 'RESOLVED'\n ORDER BY j.created_at DESC`\n )\n .all() as JobRow[];\n\n // Also include jobs where all tasks are resolved but not yet synced\n const allJobs = conn\n .prepare('SELECT * FROM jobs ORDER BY created_at DESC')\n .all() as JobRow[];\n\n const activeJobIds = new Set(jobs.map(j => j.job_id));\n const result: JobWithTasks[] = [];\n\n for (const job of allJobs) {\n const tasks = listTasksByJob(job.job_id, conn);\n if (tasks.length > 0) {\n result.push({ ...job, tasks });\n }\n }\n\n return result;\n}\n\nexport function isJobComplete(\n jobId: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const row = conn\n .prepare(\n `SELECT COUNT(*) as total,\n SUM(CASE WHEN status = 'RESOLVED' THEN 1 ELSE 0 END) as resolved\n FROM tasks WHERE job_id = ?`\n )\n .get(jobId) as { total: number; resolved: number };\n return row.total > 0 && row.total === row.resolved;\n}\n\nexport function deleteJob(\n jobId: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const result = conn\n .prepare('DELETE FROM jobs WHERE job_id = ?')\n .run(jobId);\n return result.changes > 0;\n}\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { createJob } from '../models/job.js';\nimport { createTask } from '../models/task.js';\nimport { getAgent, updateAgentStatus } from '../models/agent.js';\nimport { generateTraceId, generateId } from '../utils/trace-id.js';\nimport type { CreateJobRequest, JobWithTasks } from '../models/types.js';\n\nexport function dispatchJob(\n request: CreateJobRequest,\n db?: Database.Database\n): JobWithTasks {\n const conn = db ?? getDb();\n const jobId = generateId('job');\n const now = new Date().toISOString();\n\n // Validate all assignees exist\n for (const taskReq of request.tasks) {\n const agent = getAgent(taskReq.assignee_id, conn);\n if (!agent) {\n throw new Error(`Agent not found: ${taskReq.assignee_id}`);\n }\n if (agent.status === 'OFFLINE') {\n throw new Error(`Agent is offline: ${taskReq.assignee_id} (${agent.name})`);\n }\n }\n\n // Create the job\n const job = createJob(\n {\n job_id: jobId,\n original_prompt: request.original_prompt,\n created_at: now,\n },\n conn\n );\n\n // Create and dispatch all tasks\n const tasks = request.tasks.map(taskReq => {\n const traceId = generateTraceId();\n const task = createTask(\n {\n trace_id: traceId,\n job_id: jobId,\n assignee_id: taskReq.assignee_id,\n todo_description: taskReq.todo_description,\n deadline: taskReq.deadline,\n payload: taskReq.payload ?? {},\n status: 'DISPATCHED',\n },\n conn\n );\n\n // Mark the agent as busy\n updateAgentStatus(taskReq.assignee_id, 'BUSY', conn);\n\n return task;\n });\n\n return { ...job, tasks };\n}\n","import type { LlmProvider, LlmCompletionRequest, LlmCompletionResponse } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.anthropic.com';\n\nexport class ClaudeProvider implements LlmProvider {\n private apiKey: string;\n private model: string;\n private baseUrl: string;\n\n constructor(apiKey: string, model?: string, baseUrl?: string) {\n this.apiKey = apiKey;\n this.model = model || 'claude-sonnet-4-20250514';\n this.baseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\\/+$/, '');\n }\n\n async complete(request: LlmCompletionRequest): Promise<LlmCompletionResponse> {\n const systemMessages = request.messages.filter(m => m.role === 'system');\n const nonSystemMessages = request.messages.filter(m => m.role !== 'system');\n\n const body: Record<string, unknown> = {\n model: this.model,\n max_tokens: request.max_tokens || 4096,\n messages: nonSystemMessages.map(m => ({ role: m.role, content: m.content })),\n };\n\n if (request.temperature !== undefined) {\n body.temperature = request.temperature;\n }\n\n if (systemMessages.length > 0) {\n body.system = systemMessages.map(m => m.content).join('\\n\\n');\n }\n\n const response = await fetch(`${this.baseUrl}/v1/messages`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Claude API error (${response.status}): ${errorText}`);\n }\n\n const data = await response.json() as { content: Array<{ type: string; text: string }> };\n const textBlock = data.content.find(c => c.type === 'text');\n\n if (!textBlock) {\n throw new Error('Claude API returned no text content');\n }\n\n return { content: textBlock.text };\n }\n}\n","import type { LlmProvider, LlmCompletionRequest, LlmCompletionResponse } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.openai.com';\n\nexport class OpenAIProvider implements LlmProvider {\n private apiKey: string;\n private model: string;\n private baseUrl: string;\n\n constructor(apiKey: string, model?: string, baseUrl?: string) {\n this.apiKey = apiKey;\n this.model = model || 'gpt-4o';\n this.baseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\\/+$/, '');\n }\n\n async complete(request: LlmCompletionRequest): Promise<LlmCompletionResponse> {\n const body: Record<string, unknown> = {\n model: this.model,\n max_tokens: request.max_tokens || 4096,\n messages: request.messages.map(m => ({ role: m.role, content: m.content })),\n };\n\n if (request.temperature !== undefined) {\n body.temperature = request.temperature;\n }\n\n const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenAI API error (${response.status}): ${errorText}`);\n }\n\n const data = await response.json() as {\n choices: Array<{ message: { content: string } }>;\n };\n\n const content = data.choices[0]?.message?.content;\n if (!content) {\n throw new Error('OpenAI API returned no content');\n }\n\n return { content };\n }\n}\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\n\nexport function getConfig(key: string, db?: Database.Database): string | undefined {\n const conn = db ?? getDb();\n const row = conn.prepare('SELECT value FROM config WHERE key = ?').get(key) as { value: string } | undefined;\n return row?.value;\n}\n\nexport function setConfig(key: string, value: string, db?: Database.Database): void {\n const conn = db ?? getDb();\n conn.prepare('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)').run(key, value);\n}\n\nexport function deleteConfig(key: string, db?: Database.Database): void {\n const conn = db ?? getDb();\n conn.prepare('DELETE FROM config WHERE key = ?').run(key);\n}\n\nexport function getAllConfig(db?: Database.Database): Record<string, string> {\n const conn = db ?? getDb();\n const rows = conn.prepare('SELECT key, value FROM config').all() as Array<{ key: string; value: string }>;\n const result: Record<string, string> = {};\n for (const row of rows) {\n result[row.key] = row.value;\n }\n return result;\n}\n","import type { LlmProvider, LlmProviderName } from './types.js';\nimport { ClaudeProvider } from './claude.js';\nimport { OpenAIProvider } from './openai.js';\nimport { getConfig } from '../models/config.js';\n\nexport interface LlmConfig {\n provider: LlmProviderName;\n apiKey: string;\n model?: string;\n baseUrl?: string;\n}\n\nexport function getLlmConfig(): LlmConfig {\n // DB config takes priority over env vars\n const dbProvider = getConfig('llm_provider');\n const dbApiKey = getConfig('llm_api_key');\n const dbModel = getConfig('llm_model');\n const dbBaseUrl = getConfig('llm_base_url');\n\n const provider = (dbProvider || process.env.HUMANCLAW_LLM_PROVIDER || 'claude') as LlmProviderName;\n const apiKey = dbApiKey || process.env.HUMANCLAW_LLM_API_KEY || '';\n const model = dbModel || process.env.HUMANCLAW_LLM_MODEL;\n const baseUrl = dbBaseUrl || process.env.HUMANCLAW_LLM_BASE_URL;\n\n if (!apiKey) {\n throw new Error(\n '未配置 LLM API Key。请在 Dashboard 设置中配置,或设置环境变量 HUMANCLAW_LLM_API_KEY。'\n );\n }\n\n if (provider !== 'claude' && provider !== 'openai') {\n throw new Error(`不支持的 LLM 提供商: ${provider}。支持: claude, openai`);\n }\n\n return { provider, apiKey, model: model || undefined, baseUrl: baseUrl || undefined };\n}\n\nexport function createLlmProvider(config?: LlmConfig): LlmProvider {\n const cfg = config || getLlmConfig();\n\n switch (cfg.provider) {\n case 'claude':\n return new ClaudeProvider(cfg.apiKey, cfg.model, cfg.baseUrl);\n case 'openai':\n return new OpenAIProvider(cfg.apiKey, cfg.model, cfg.baseUrl);\n }\n}\n\nexport type { LlmProvider, LlmProviderName } from './types.js';\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { listAgentsWithMetrics, getAgent } from '../models/agent.js';\nimport { createLlmProvider } from '../llm/index.js';\nimport type { PlanRequest, PlanResponse, PlannedTask, AgentWithMetrics } from '../models/types.js';\nimport type { LlmProvider } from '../llm/types.js';\n\nfunction buildSystemPrompt(): string {\n return `你是 HumanClaw 任务编排规划器。你的工作是将用户的需求拆解为可以分发给碳基节点(真实人类)执行的独立子任务。\n\n规则:\n1. 将需求拆解为扁平的、无依赖的子任务列表(每个任务可以独立执行)\n2. 根据每个 Agent 的技能(capabilities)和当前负载来匹配分配\n3. 为每个任务生成一段「话术」—— 这是直接发给该人类执行者的任务说明,语气自然、清晰、专业,包含具体的交付物要求\n4. 根据任务复杂度设置合理的截止时间(相对于当前时间)\n5. 任务分配应以最佳匹配为原则,同一个 Agent 可以承担多个任务\n\n你必须严格输出以下 JSON 格式(不要输出任何其他内容):\n\n\\`\\`\\`json\n[\n {\n \"assignee_id\": \"agent的ID\",\n \"assignee_name\": \"agent的名字\",\n \"todo_description\": \"简短的任务标题/描述\",\n \"briefing\": \"话术:详细的任务说明,包括目标、交付物要求、注意事项。语气像一个靠谱的项目经理在给组员分配任务。\",\n \"deadline\": \"ISO 8601 时间\"\n }\n]\n\\`\\`\\``;\n}\n\nfunction buildUserPrompt(prompt: string, agents: AgentWithMetrics[]): string {\n const now = new Date();\n const agentList = agents.map(a => {\n const load = a.active_task_count > 0\n ? `当前有 ${a.active_task_count} 个进行中的任务`\n : '当前空闲';\n const speed = a.avg_delivery_hours !== null\n ? `平均交付时间 ${a.avg_delivery_hours}h`\n : '暂无历史数据';\n const rel = a.relationship ? ` 关系: ${a.relationship}` : '';\n return `- ${a.name} (ID: ${a.agent_id}) 技能: [${a.capabilities.join(', ')}]${rel} ${load} ${speed}`;\n }).join('\\n');\n\n return `当前时间: ${now.toISOString()}\n\n可用的碳基节点:\n${agentList}\n\n需求:\n${prompt}\n\n请根据以上信息拆解任务并分配。输出 JSON 数组。`;\n}\n\nfunction extractJson(raw: string): string {\n // Try to extract JSON from markdown code block\n const codeBlockMatch = raw.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/);\n if (codeBlockMatch) {\n return codeBlockMatch[1].trim();\n }\n // Try to find a JSON array directly\n const arrayMatch = raw.match(/\\[[\\s\\S]*\\]/);\n if (arrayMatch) {\n return arrayMatch[0];\n }\n return raw.trim();\n}\n\nfunction validatePlannedTasks(\n parsed: unknown,\n validAgentIds: Set<string>,\n agents: AgentWithMetrics[]\n): PlannedTask[] {\n if (!Array.isArray(parsed)) {\n throw new Error('LLM 返回的不是有效的任务数组');\n }\n\n if (parsed.length === 0) {\n throw new Error('LLM 未生成任何任务');\n }\n\n return parsed.map((item: Record<string, unknown>, i: number) => {\n if (!item.todo_description || typeof item.todo_description !== 'string') {\n throw new Error(`任务 ${i + 1} 缺少 todo_description`);\n }\n if (!item.briefing || typeof item.briefing !== 'string') {\n throw new Error(`任务 ${i + 1} 缺少 briefing (话术)`);\n }\n\n // Validate or fallback assignee_id\n let assigneeId = String(item.assignee_id || '');\n let assigneeName = String(item.assignee_name || '');\n\n if (!validAgentIds.has(assigneeId)) {\n // Fallback to first available agent\n const fallback = agents[0];\n if (fallback) {\n assigneeId = fallback.agent_id;\n assigneeName = fallback.name;\n }\n }\n\n // Validate deadline or generate a default\n let deadline = String(item.deadline || '');\n if (!deadline || isNaN(Date.parse(deadline))) {\n // Default: 24 hours from now\n deadline = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();\n }\n\n return {\n assignee_id: assigneeId,\n assignee_name: assigneeName,\n todo_description: String(item.todo_description),\n briefing: String(item.briefing),\n deadline,\n };\n });\n}\n\nexport async function planJob(\n request: PlanRequest,\n provider?: LlmProvider,\n db?: Database.Database\n): Promise<PlanResponse> {\n const conn = db ?? getDb();\n\n // Get available agents\n const allAgents = listAgentsWithMetrics(conn);\n let agents: AgentWithMetrics[];\n\n if (request.agent_ids && request.agent_ids.length > 0) {\n agents = allAgents.filter(a => request.agent_ids!.includes(a.agent_id));\n if (agents.length === 0) {\n throw new Error('指定的 Agent 均不存在');\n }\n } else {\n // Default: only IDLE agents\n agents = allAgents.filter(a => a.status === 'IDLE');\n if (agents.length === 0) {\n // Fallback: include BUSY agents too (but not OFFLINE/OOM)\n agents = allAgents.filter(a => a.status !== 'OFFLINE' && a.status !== 'OOM');\n }\n if (agents.length === 0) {\n throw new Error('没有可用的碳基节点。请先在碳基算力池中添加节点。');\n }\n }\n\n // Get LLM provider\n const llm = provider ?? createLlmProvider();\n\n const systemPrompt = buildSystemPrompt();\n const userPrompt = buildUserPrompt(request.prompt, agents);\n\n const response = await llm.complete({\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n temperature: 0.3,\n max_tokens: 4096,\n });\n\n // Parse LLM response\n const jsonStr = extractJson(response.content);\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonStr);\n } catch {\n throw new Error('AI 返回的内容无法解析为 JSON,请重试');\n }\n\n const validIds = new Set(agents.map(a => a.agent_id));\n const plannedTasks = validatePlannedTasks(parsed, validIds, agents);\n\n return {\n original_prompt: request.prompt,\n planned_tasks: plannedTasks,\n };\n}\n","import { Router } from 'express';\nimport { resumeTask, rejectTask } from '../services/resume.js';\nimport { simulateDelivery } from '../services/simulator.js';\n\nconst router = Router();\n\n// POST /api/v1/tasks/resume - Submit result for a task\nrouter.post('/resume', (req, res) => {\n const { trace_id, result_data } = req.body;\n\n if (!trace_id) {\n res.status(400).json({ error: 'trace_id is required' });\n return;\n }\n\n if (result_data === undefined) {\n res.status(400).json({ error: 'result_data is required' });\n return;\n }\n\n try {\n const result = resumeTask(trace_id, result_data);\n res.json({\n task: result.task,\n job_complete: result.jobComplete,\n job: result.job ?? null,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n res.status(400).json({ error: message });\n }\n});\n\n// POST /api/v1/tasks/reject - Reject and retry a task\nrouter.post('/reject', (req, res) => {\n const { trace_id, new_deadline } = req.body;\n\n if (!trace_id) {\n res.status(400).json({ error: 'trace_id is required' });\n return;\n }\n\n try {\n const task = rejectTask(trace_id, new_deadline);\n res.json({ task });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n res.status(400).json({ error: message });\n }\n});\n\n// POST /api/v1/tasks/simulate - AI simulate delivery from agent's perspective\nrouter.post('/simulate', async (req, res) => {\n const { trace_id } = req.body;\n\n if (!trace_id) {\n res.status(400).json({ error: 'trace_id is required' });\n return;\n }\n\n try {\n const result = await simulateDelivery(trace_id);\n res.json(result);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n const status = message.includes('API Key') || message.includes('API key') ? 503 : 400;\n res.status(status).json({ error: message });\n }\n});\n\nexport default router;\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { getTask, resolveTask, resetTaskDeadline } from '../models/task.js';\nimport { isJobComplete, getJobWithTasks } from '../models/job.js';\nimport { updateAgentStatus } from '../models/agent.js';\nimport { listTasksByAssignee } from '../models/task.js';\nimport type { HumanTask, JobWithTasks } from '../models/types.js';\n\nexport interface ResumeResult {\n task: HumanTask;\n jobComplete: boolean;\n job: JobWithTasks | undefined;\n}\n\nexport function resumeTask(\n traceId: string,\n resultData: unknown,\n db?: Database.Database\n): ResumeResult {\n const conn = db ?? getDb();\n\n // Validate trace_id exists\n const task = getTask(traceId, conn);\n if (!task) {\n throw new Error(`Task not found: ${traceId}`);\n }\n\n if (task.status === 'RESOLVED') {\n throw new Error(`Task already resolved: ${traceId}`);\n }\n\n if (task.status === 'PENDING') {\n throw new Error(`Task not yet dispatched: ${traceId}`);\n }\n\n // Resolve the task\n const updated = resolveTask(traceId, resultData, conn);\n if (!updated) {\n throw new Error(`Failed to resolve task: ${traceId}`);\n }\n\n // Check if the agent has other active tasks; if not, mark IDLE\n const activeTasks = listTasksByAssignee(task.assignee_id, conn).filter(\n t => t.status === 'DISPATCHED' || t.status === 'PENDING'\n );\n if (activeTasks.length === 0) {\n updateAgentStatus(task.assignee_id, 'IDLE', conn);\n }\n\n // Check if the whole job is now complete\n const jobComplete = isJobComplete(task.job_id, conn);\n const job = jobComplete ? getJobWithTasks(task.job_id, conn) : undefined;\n\n const resolvedTask = getTask(traceId, conn)!;\n\n return { task: resolvedTask, jobComplete, job };\n}\n\nexport function rejectTask(\n traceId: string,\n newDeadline?: string,\n db?: Database.Database\n): HumanTask {\n const conn = db ?? getDb();\n\n const task = getTask(traceId, conn);\n if (!task) {\n throw new Error(`Task not found: ${traceId}`);\n }\n\n // Default: extend deadline by 24h from now\n const deadline =\n newDeadline ?? new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();\n\n resetTaskDeadline(traceId, deadline, conn);\n\n return getTask(traceId, conn)!;\n}\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { getAgent } from '../models/agent.js';\nimport { getTask } from '../models/task.js';\nimport { createLlmProvider } from '../llm/index.js';\nimport type { LlmProvider } from '../llm/types.js';\n\nexport interface SimulateResult {\n trace_id: string;\n simulated_delivery: string;\n}\n\nfunction buildSimulatePrompt(\n agentName: string,\n relationship: string,\n capabilities: string[],\n taskDescription: string,\n deadline: string\n): string {\n return `你现在扮演一个名叫「${agentName}」的人,你的技能标签是 [${capabilities.join(', ')}]。\n${relationship ? `你和布置任务的人的关系是:${relationship}。` : ''}\n\n你收到了一个任务:\n${taskDescription}\n\n截止时间:${new Date(deadline).toLocaleString('zh-CN')}\n\n请站在「${agentName}」的视角,用这个人物自然的语气和口吻,写一段任务交付汇报。\n要求:\n1. 内容要贴合任务描述,体现专业能力\n2. 语气符合人物身份${relationship ? '和与上级的关系' : ''}\n3. 汇报内容具体、有细节,包含做了什么、遇到了什么问题、最终结果如何\n4. 字数 200-400 字\n5. 直接输出汇报内容,不要加任何格式前缀或说明`;\n}\n\nexport async function simulateDelivery(\n traceId: string,\n provider?: LlmProvider,\n db?: Database.Database\n): Promise<SimulateResult> {\n const conn = db ?? getDb();\n\n const task = getTask(traceId, conn);\n if (!task) {\n throw new Error(`Task not found: ${traceId}`);\n }\n\n const agent = getAgent(task.assignee_id, conn);\n if (!agent) {\n throw new Error(`Agent not found: ${task.assignee_id}`);\n }\n\n const llm = provider ?? createLlmProvider();\n\n const response = await llm.complete({\n messages: [\n {\n role: 'system',\n content: '你是一个角色扮演专家,擅长模拟不同人物的语气和汇报风格。',\n },\n {\n role: 'user',\n content: buildSimulatePrompt(\n agent.name,\n agent.relationship,\n agent.capabilities,\n task.todo_description,\n task.deadline\n ),\n },\n ],\n temperature: 0.7,\n max_tokens: 1024,\n });\n\n return {\n trace_id: traceId,\n simulated_delivery: response.content,\n };\n}\n","import { Router } from 'express';\nimport { reviewJob } from '../services/reviewer.js';\n\nconst router = Router();\n\n// POST /api/v1/jobs/:job_id/review - AI review of all deliverables\nrouter.post('/:job_id/review', async (req, res) => {\n const { job_id } = req.params;\n\n try {\n const result = await reviewJob(job_id);\n res.json(result);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n const status = message.includes('API Key') || message.includes('API key') ? 503 : 400;\n res.status(status).json({ error: message });\n }\n});\n\nexport default router;\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { getJobWithTasks, isJobComplete } from '../models/job.js';\nimport { createLlmProvider } from '../llm/index.js';\nimport type { LlmProvider } from '../llm/types.js';\n\nexport interface ReviewResult {\n job_id: string;\n original_prompt: string;\n review: string;\n reviewed_at: string;\n}\n\nfunction buildReviewSystemPrompt(): string {\n return `你是 HumanClaw 交付审查员。你的工作是审查碳基节点提交的所有任务交付物,生成整体审查报告。\n\n审查要点:\n1. 检查每个交付物是否满足原始任务要求\n2. 如果交付物包含 GitHub URL(PR、commit、issue 等),分析其内容和质量\n3. 指出潜在问题或改进建议\n4. 给出整体完成度评分(1-10)和总结\n\n输出格式:\n- 使用 Markdown 格式\n- 先给总结评分,再逐个分析每个子任务的交付质量`;\n}\n\nfunction buildReviewUserPrompt(\n originalPrompt: string,\n tasks: Array<{\n assignee_id: string;\n todo_description: string;\n result_data: unknown;\n }>\n): string {\n let prompt = `原始需求: ${originalPrompt}\\n\\n`;\n\n for (let i = 0; i < tasks.length; i++) {\n const t = tasks[i];\n let resultText = '';\n if (t.result_data) {\n if (typeof t.result_data === 'string') {\n resultText = t.result_data;\n } else if (typeof t.result_data === 'object') {\n const rd = t.result_data as Record<string, unknown>;\n resultText = (rd.text as string) || JSON.stringify(rd, null, 2);\n } else {\n resultText = String(t.result_data);\n }\n }\n prompt += `--- 子任务 ${i + 1} ---\\n`;\n prompt += `执行者: ${t.assignee_id}\\n`;\n prompt += `任务: ${t.todo_description}\\n`;\n prompt += `交付物:\\n${resultText}\\n\\n`;\n }\n\n prompt += '请审查以上所有交付物,生成审查报告。';\n return prompt;\n}\n\nexport async function reviewJob(\n jobId: string,\n provider?: LlmProvider,\n db?: Database.Database\n): Promise<ReviewResult> {\n const conn = db ?? getDb();\n\n const job = getJobWithTasks(jobId, conn);\n if (!job) {\n throw new Error(`Job not found: ${jobId}`);\n }\n\n if (!isJobComplete(jobId, conn)) {\n const resolved = job.tasks.filter(t => t.status === 'RESOLVED').length;\n throw new Error(\n `Job 尚未全部完成: ${resolved}/${job.tasks.length} 个任务已交付`\n );\n }\n\n const llm = provider ?? createLlmProvider();\n\n const response = await llm.complete({\n messages: [\n { role: 'system', content: buildReviewSystemPrompt() },\n {\n role: 'user',\n content: buildReviewUserPrompt(\n job.original_prompt,\n job.tasks.map(t => ({\n assignee_id: t.assignee_id,\n todo_description: t.todo_description,\n result_data: t.result_data,\n }))\n ),\n },\n ],\n temperature: 0.3,\n max_tokens: 4096,\n });\n\n return {\n job_id: jobId,\n original_prompt: job.original_prompt,\n review: response.content,\n reviewed_at: new Date().toISOString(),\n };\n}\n","import { Router } from 'express';\nimport { getConfig, setConfig, deleteConfig } from '../models/config.js';\n\nconst router = Router();\n\n// GET /api/v1/config - Get LLM config (masks API key)\nrouter.get('/', (_req, res) => {\n const provider = getConfig('llm_provider') || process.env.HUMANCLAW_LLM_PROVIDER || 'claude';\n const apiKey = getConfig('llm_api_key') || process.env.HUMANCLAW_LLM_API_KEY || '';\n const model = getConfig('llm_model') || process.env.HUMANCLAW_LLM_MODEL || '';\n const baseUrl = getConfig('llm_base_url') || process.env.HUMANCLAW_LLM_BASE_URL || '';\n\n const keySource = getConfig('llm_api_key') ? 'dashboard' : (process.env.HUMANCLAW_LLM_API_KEY ? 'env' : 'none');\n\n res.json({\n provider,\n api_key_set: apiKey.length > 0,\n api_key_masked: apiKey ? apiKey.slice(0, 8) + '...' + apiKey.slice(-4) : '',\n api_key_source: keySource,\n model,\n base_url: baseUrl,\n });\n});\n\n// PUT /api/v1/config - Update LLM config\nrouter.put('/', (req, res) => {\n const { provider, api_key, model, base_url } = req.body as {\n provider?: string;\n api_key?: string;\n model?: string;\n base_url?: string;\n };\n\n if (provider !== undefined) {\n if (provider !== 'claude' && provider !== 'openai') {\n res.status(400).json({ error: '不支持的提供商,支持: claude, openai' });\n return;\n }\n setConfig('llm_provider', provider);\n }\n\n if (api_key !== undefined) {\n if (api_key === '') {\n deleteConfig('llm_api_key');\n } else {\n setConfig('llm_api_key', api_key);\n }\n }\n\n if (model !== undefined) {\n if (model === '') {\n deleteConfig('llm_model');\n } else {\n setConfig('llm_model', model);\n }\n }\n\n if (base_url !== undefined) {\n if (base_url === '') {\n deleteConfig('llm_base_url');\n } else {\n setConfig('llm_base_url', base_url);\n }\n }\n\n res.json({ ok: true });\n});\n\nexport default router;\n","export function getDashboardHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\"/>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"/>\n<title>HumanClaw - 碳基节点编排</title>\n<style>\n:root{--bg:#0f1117;--surface:#1a1d27;--surface-hover:#242836;--border:#2e3346;--text:#e2e4ed;--text-dim:#7b8196;--accent:#00d4ff;--accent-dim:rgba(0,212,255,.12);--green:#22c55e;--yellow:#eab308;--red:#ef4444;--purple:#a855f7;--font-mono:'SF Mono','JetBrains Mono','Fira Code',monospace;--font-sans:-apple-system,'Inter','Segoe UI',sans-serif;--radius:10px}\n*{margin:0;padding:0;box-sizing:border-box}\nbody{background:var(--bg);color:var(--text);font-family:var(--font-sans);min-height:100vh;line-height:1.5}\nheader{padding:20px 32px 12px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:14px}\nheader h1{font-family:var(--font-mono);font-size:22px;color:var(--accent);letter-spacing:1.5px}\n.subtitle{color:var(--text-dim);font-size:12px;border-left:1px solid var(--border);padding-left:14px}\n.hdr-spacer{flex:1}\n.gear-btn{background:none;border:1px solid var(--border);color:var(--text-dim);width:34px;height:34px;border-radius:8px;cursor:pointer;font-size:18px;display:flex;align-items:center;justify-content:center;transition:all .15s}\n.gear-btn:hover{color:var(--accent);border-color:var(--accent);background:var(--accent-dim)}\nnav{display:flex;border-bottom:1px solid var(--border);padding:0 32px;gap:4px}\n.tab{background:none;border:none;color:var(--text-dim);padding:12px 18px;font-size:13px;cursor:pointer;border-bottom:2px solid transparent;transition:all .15s;font-family:var(--font-sans);font-weight:500}\n.tab:hover{color:var(--text);background:var(--surface)}\n.tab.active{color:var(--accent);border-bottom-color:var(--accent)}\nmain{padding:24px 32px;max-width:1200px}\n.panel{display:none}.panel.active{display:block}\n/* Cards */\n.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;transition:border-color .15s}\n.card:hover{border-color:color-mix(in srgb,var(--accent) 40%,transparent)}\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px;margin-top:16px}\n/* Agent Card */\n.agent-header{display:flex;align-items:center;gap:10px;margin-bottom:10px}\n.dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}\n.dot.IDLE{background:var(--green);box-shadow:0 0 8px var(--green)}.dot.BUSY{background:var(--yellow);box-shadow:0 0 8px var(--yellow)}.dot.OFFLINE{background:var(--red);box-shadow:0 0 6px var(--red)}.dot.OOM{background:var(--purple);box-shadow:0 0 8px var(--purple)}\n.agent-name{font-weight:600;font-size:15px}\n.agent-id{color:var(--text-dim);font-family:var(--font-mono);font-size:11px;margin-bottom:8px}\n.caps{display:flex;flex-wrap:wrap;gap:5px;margin-top:6px}\n.cap{background:var(--accent-dim);border:1px solid color-mix(in srgb,var(--accent) 25%,transparent);border-radius:4px;padding:1px 8px;font-size:11px;color:var(--accent)}\n.agent-meta{margin-top:10px;font-size:11px;color:var(--text-dim);display:flex;gap:12px}\n.agent-actions{margin-top:12px;display:flex;gap:6px}\n.agent-actions select{background:var(--surface-hover);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:11px;cursor:pointer;outline:none}\n.agent-actions button{background:var(--red);color:#fff;border:none;border-radius:6px;padding:4px 10px;font-size:11px;cursor:pointer}\n.agent-actions button:hover{opacity:.8}\n/* Stats Bar */\n.stats{display:flex;gap:12px;flex-wrap:wrap}\n.stat{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px 22px;text-align:center;min-width:90px}\n.stat-val{font-size:28px;font-weight:700;font-family:var(--font-mono)}\n.stat-lbl{font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:1px;margin-top:2px}\n/* Forms */\n.form-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:24px;margin-top:16px;max-width:620px}\n.form-card h3{font-size:15px;color:var(--accent);font-family:var(--font-mono);margin-bottom:16px}\n.fg{margin-bottom:14px}\n.fg label{display:block;font-size:12px;color:var(--text-dim);margin-bottom:5px;font-weight:500}\n.fg input,.fg textarea,.fg select{width:100%;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:10px 12px;color:var(--text);font-size:13px;font-family:var(--font-sans);outline:none;transition:border-color .15s}\n.fg input:focus,.fg textarea:focus,.fg select:focus{border-color:var(--accent)}\n.fg textarea{min-height:80px;resize:vertical;font-family:var(--font-mono);font-size:12px}\n.fg .hint{font-size:11px;color:var(--text-dim);margin-top:4px}\n.btn{padding:10px 22px;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;transition:all .15s;display:inline-flex;align-items:center;gap:6px}\n.btn:hover{opacity:.85;transform:translateY(-1px)}\n.btn:active{transform:translateY(0)}\n.btn:disabled{opacity:.5;cursor:not-allowed;transform:none}\n.btn-primary{background:var(--accent);color:var(--bg)}\n.btn-green{background:var(--green);color:#fff}\n.btn-danger{background:var(--red);color:#fff}\n.btn-ghost{background:transparent;color:var(--accent);border:1px solid var(--border)}\n.btn-ghost:hover{background:var(--accent-dim)}\n.btn-sm{padding:6px 14px;font-size:12px}\n.btn-group{display:flex;gap:10px;margin-top:16px;flex-wrap:wrap}\n/* Section header */\n.section-hd{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}\n.section-hd h2{font-size:16px;font-weight:600}\n/* Job card */\n.job-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;margin-top:14px}\n.job-hd{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}\n.job-title{font-weight:600;font-size:14px}\n.job-id{font-family:var(--font-mono);font-size:11px;color:var(--text-dim)}\n.pbar-wrap{margin:6px 0 14px}\n.pbar-label{font-size:11px;color:var(--text-dim);margin-bottom:4px}\n.pbar{background:var(--surface-hover);border-radius:4px;height:6px;overflow:hidden}\n.pbar-fill{background:var(--accent);height:100%;border-radius:4px;transition:width .3s}\n.kanban{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px}\n.lane{min-height:40px}\n.lane-hd{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px;padding-bottom:4px;border-bottom:2px solid var(--border)}\n.lane-hd.y{color:var(--yellow);border-color:var(--yellow)}.lane-hd.r{color:var(--red);border-color:var(--red)}.lane-hd.g{color:var(--green);border-color:var(--green)}\n.tcard{background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:8px 10px;margin-bottom:6px;font-size:12px;cursor:pointer;transition:all .15s}\n.tcard:hover{border-color:var(--accent);background:var(--surface-hover)}\n.tcard-trace{font-family:var(--font-mono);font-size:10px;color:var(--accent);margin-bottom:3px}\n.tcard-meta{font-size:10px;color:var(--text-dim);margin-top:3px}\n/* Task detail modal */\n.task-detail-hd{display:flex;align-items:center;gap:10px;margin-bottom:16px}\n.task-detail-hd .dot{width:12px;height:12px}\n.task-detail-status{font-size:11px;padding:3px 10px;border-radius:12px;font-weight:600;text-transform:uppercase}\n.task-detail-status.DISPATCHED,.task-detail-status.PENDING{background:rgba(234,179,8,.15);color:var(--yellow)}\n.task-detail-status.OVERDUE{background:rgba(239,68,68,.15);color:var(--red)}\n.task-detail-status.RESOLVED{background:rgba(34,197,94,.15);color:var(--green)}\n.trace-copy-row{display:flex;align-items:center;gap:8px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 12px;margin-bottom:14px;font-family:var(--font-mono);font-size:13px;color:var(--accent)}\n.trace-copy-row button{background:var(--surface-hover);border:1px solid var(--border);color:var(--text-dim);border-radius:4px;padding:3px 10px;font-size:11px;cursor:pointer;margin-left:auto;flex-shrink:0}\n.trace-copy-row button:hover{color:var(--accent);border-color:var(--accent)}\n.detail-row{margin-bottom:12px}\n.detail-label{font-size:11px;color:var(--text-dim);margin-bottom:3px;font-weight:500}\n.detail-value{font-size:13px}\n.result-display{background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:12px;font-family:var(--font-mono);font-size:12px;white-space:pre-wrap;max-height:200px;overflow-y:auto}\n/* Demo cards */\n.demo-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px;margin-top:16px}\n.demo-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;cursor:pointer;transition:all .2s;position:relative;overflow:hidden}\n.demo-card:hover{border-color:var(--accent);transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,212,255,.08)}\n.demo-card-emoji{font-size:36px;margin-bottom:10px}\n.demo-card-title{font-weight:700;font-size:15px;margin-bottom:4px}\n.demo-card-role{font-size:12px;color:var(--accent);margin-bottom:8px;font-family:var(--font-mono)}\n.demo-card-desc{font-size:12px;color:var(--text-dim);line-height:1.6;margin-bottom:10px}\n.demo-card-agents{display:flex;flex-wrap:wrap;gap:4px}\n.demo-card-agents span{background:var(--accent-dim);border:1px solid color-mix(in srgb,var(--accent) 20%,transparent);border-radius:4px;padding:1px 6px;font-size:10px;color:var(--accent)}\n.demo-card-prompt{margin-top:10px;padding-top:10px;border-top:1px solid var(--border);font-size:11px;color:var(--text-dim);font-style:italic}\n.demo-loading{position:absolute;inset:0;background:rgba(15,17,23,.85);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:8px;z-index:2}\n/* Toast */\n.toast{position:fixed;bottom:24px;right:24px;padding:12px 20px;border-radius:8px;font-size:13px;z-index:9999;animation:slide-up .25s ease;font-weight:500;box-shadow:0 8px 24px rgba(0,0,0,.4)}\n.toast.ok{background:var(--green);color:#fff}.toast.err{background:var(--red);color:#fff}\n@keyframes slide-up{from{transform:translateY(16px);opacity:0}to{transform:translateY(0);opacity:1}}\n/* Empty state */\n.empty-state{text-align:center;padding:48px 20px;color:var(--text-dim)}\n.empty-state .icon{font-size:48px;margin-bottom:12px;opacity:.6}\n.empty-state p{font-size:14px;margin-bottom:16px;max-width:360px;margin-left:auto;margin-right:auto;line-height:1.6}\n/* Modal/Overlay */\n.overlay{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:100;display:flex;align-items:flex-start;justify-content:center;padding-top:60px;animation:fade-in .15s ease}\n.overlay .form-card{max-width:620px;width:100%;max-height:85vh;overflow-y:auto;margin:0}\n@keyframes fade-in{from{opacity:0}to{opacity:1}}\n/* Agent chips for AI planning */\n.chip-bar{display:flex;gap:6px;margin-bottom:10px;flex-wrap:wrap}\n.chip-filter{background:var(--surface-hover);color:var(--text-dim);border:1px solid var(--border);border-radius:14px;padding:3px 12px;font-size:11px;cursor:pointer;transition:all .15s}\n.chip-filter.active{background:var(--accent-dim);color:var(--accent);border-color:var(--accent)}\n.agent-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}\n.achip{background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:6px 12px;font-size:12px;cursor:pointer;transition:all .15s;display:flex;align-items:center;gap:6px;user-select:none}\n.achip:hover{border-color:var(--text-dim)}\n.achip.selected{border-color:var(--accent);background:var(--accent-dim)}\n.achip .adot{width:8px;height:8px;border-radius:50%;flex-shrink:0}\n.achip .adot.IDLE{background:var(--green)}.achip .adot.BUSY{background:var(--yellow)}.achip .adot.OFFLINE{background:var(--red)}.achip .adot.OOM{background:var(--purple)}\n/* Plan preview cards */\n.plan-card{background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:16px;margin-bottom:12px}\n.plan-card-hd{display:flex;align-items:center;gap:8px;margin-bottom:8px}\n.plan-card-hd .adot{width:8px;height:8px;border-radius:50%}\n.plan-card-agent{font-weight:600;font-size:13px}\n.plan-card-agentid{font-family:var(--font-mono);font-size:10px;color:var(--text-dim)}\n.plan-card-desc{font-size:13px;margin-bottom:10px;font-weight:500}\n.briefing-box{background:var(--surface);border-left:3px solid var(--accent);border-radius:0 6px 6px 0;padding:10px 14px;font-size:12px;line-height:1.6;color:var(--text);margin-bottom:8px;position:relative}\n.briefing-label{font-size:10px;color:var(--accent);font-weight:600;margin-bottom:4px;font-family:var(--font-mono)}\n.briefing-copy{position:absolute;top:8px;right:8px;background:var(--surface-hover);border:1px solid var(--border);color:var(--text-dim);border-radius:4px;padding:2px 8px;font-size:10px;cursor:pointer}\n.briefing-copy:hover{color:var(--accent);border-color:var(--accent)}\n.plan-card-dl{font-size:11px;color:var(--text-dim);font-family:var(--font-mono)}\n/* Spinner */\n.spinner-wrap{text-align:center;padding:40px 20px}\n.spinner{display:inline-block;width:28px;height:28px;border:3px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin .6s linear infinite}\n@keyframes spin{to{transform:rotate(360deg)}}\n.spinner-text{color:var(--text-dim);font-size:13px;margin-top:12px}\n/* Manual task row */\n.task-row{background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:14px;margin-bottom:10px;position:relative}\n.task-row .remove-task{position:absolute;top:8px;right:10px;background:none;border:none;color:var(--red);cursor:pointer;font-size:16px;line-height:1}\n.task-row .fg{margin-bottom:10px}\n.task-row .fg:last-child{margin-bottom:0}\n</style>\n</head>\n<body>\n<header>\n <h1>HumanClaw</h1>\n <span class=\"subtitle\">碳基节点编排框架</span>\n <span class=\"hdr-spacer\"></span>\n <button class=\"gear-btn\" onclick=\"showSettings()\" title=\"设置\">&#9881;</button>\n</header>\n<nav>\n <button class=\"tab active\" data-panel=\"fleet\">碳基算力池</button>\n <button class=\"tab\" data-panel=\"pipeline\">编排大盘</button>\n <button class=\"tab\" data-panel=\"terminal\">I/O 终端</button>\n</nav>\n<main>\n <section id=\"fleet\" class=\"panel active\"></section>\n <section id=\"pipeline\" class=\"panel\"></section>\n <section id=\"terminal\" class=\"panel\"></section>\n</main>\n\n<script>\nconst API='/api/v1';\nfunction esc(s){const d=document.createElement('div');d.textContent=s;return d.innerHTML}\nfunction toast(msg,ok){\n document.querySelectorAll('.toast').forEach(t=>t.remove());\n const t=document.createElement('div');t.className='toast '+(ok?'ok':'err');t.textContent=msg;document.body.appendChild(t);\n setTimeout(()=>t.remove(),3500);\n}\nlet cachedAgents=[];\nlet currentPlan=null;\nlet selectedAgentIds=new Set();\n\n// ═══════════════════════════════════════════════\n// DEMO SCENARIOS\n// ═══════════════════════════════════════════════\nconst DEMOS={\n sanguo:{\n emoji:'&#128009;',title:'三国蜀汉',role:'你是刘备',\n desc:'桃园结义,三顾茅庐。作为蜀汉之主,统领五虎上将和卧龙凤雏,逐鹿中原。',\n prompt:'北伐中原,兵分三路,需要攻城、断粮和外交三管齐下',\n agents:[\n {name:'关羽',capabilities:['武艺','统兵','镇守要地','水军指挥'],relationship:'义弟,桃园结义二弟,最信任的兄弟和大将'},\n {name:'张飞',capabilities:['武艺','先锋突击','骑兵指挥','威慑敌军'],relationship:'义弟,桃园结义三弟,性如烈火但忠心耿耿'},\n {name:'赵云',capabilities:['武艺','护卫','骑兵突击','侦察敏捷'],relationship:'四弟级别的心腹爱将,长坂坡救阿斗'},\n {name:'诸葛亮',capabilities:['战略规划','内政治理','外交','发明创造','阵法'],relationship:'三顾茅庐请来的军师,如鱼得水的关系'},\n {name:'庞统',capabilities:['战略规划','奇谋','攻城战术','地形分析'],relationship:'凤雏,与诸葛亮齐名的军师,副军师中郎将'},\n {name:'黄忠',capabilities:['武艺','弓箭','老当益壮','攻城战'],relationship:'老将军,定军山斩夏侯渊,五虎上将之一'},\n {name:'马超',capabilities:['武艺','骑兵','西凉作战','威慑羌族'],relationship:'归降的西凉猛将,五虎上将之一'}\n ]\n },\n tech:{\n emoji:'&#128187;',title:'互联网大厂',role:'你是技术总监',\n desc:'带领一支全栈团队,从前端到运维一应俱全。应对高并发、搞 AI、上线新系统。',\n prompt:'上线一个 AI 智能客服系统,包括前端界面、后端 API、推荐算法、压力测试和灰度发布方案',\n agents:[\n {name:'前端老李',capabilities:['React','TypeScript','Next.js','移动端适配','性能优化'],relationship:'P7 前端 TL,跟了你三年,技术过硬但最近有点倦怠'},\n {name:'后端大王',capabilities:['Java','Go','微服务','数据库设计','高并发架构'],relationship:'P8 后端架构师,技术大拿,说话直来直去'},\n {name:'算法小陈',capabilities:['机器学习','推荐系统','NLP','Python','数据分析'],relationship:'P6 算法工程师,刚从学校毕业一年,潜力很大但经验不足'},\n {name:'产品经理 Amy',capabilities:['需求分析','用户调研','PRD撰写','项目管理','数据驱动'],relationship:'P7 产品经理,业务感觉很好,跨部门沟通能力强'},\n {name:'设计师小林',capabilities:['UI设计','交互设计','Figma','设计系统','用户体验'],relationship:'P6 资深设计师,审美在线,偶尔和产品经理吵架'},\n {name:'测试负责人老赵',capabilities:['自动化测试','性能测试','安全测试','测试用例设计','CI集成'],relationship:'P7 测试负责人,入职五年老员工,对质量要求极高'},\n {name:'运维 DevOps 阿杰',capabilities:['Kubernetes','Docker','CI/CD','监控告警','云原生架构'],relationship:'P7 SRE,半夜被 oncall 叫起来过无数次,求稳派'}\n ]\n },\n gov:{\n emoji:'&#127482;&#127480;',title:'美国政府',role:'你是特朗普 (POTUS)',\n desc:'Make the executive branch great again! 管理你的核心内阁成员,推行政策议程。',\n prompt:'制定一个让美国制造业回流的综合计划,需要关税政策、减税方案、能源保障、边境安全配合和政府效率优化',\n agents:[\n {name:'Elon Musk',capabilities:['政府效率','成本削减','科技创新','SpaceX','Tesla','社交媒体'],relationship:'DOGE 负责人,世界首富,Twitter/X 老板,最具影响力的盟友'},\n {name:'Marco Rubio',capabilities:['外交政策','拉美事务','国际谈判','制裁政策','国家安全'],relationship:'国务卿,佛罗里达参议员,曾经的竞选对手变忠实支持者'},\n {name:'Pete Hegseth',capabilities:['国防战略','军事改革','退伍军人事务','军费预算','作战指挥'],relationship:'国防部长,前 Fox News 主持人,坚定的 MAGA 支持者'},\n {name:'Scott Bessent',capabilities:['经济政策','金融市场','税收改革','债务管理','贸易政策'],relationship:'财政部长,华尔街老将,关键经济顾问'},\n {name:'Kristi Noem',capabilities:['国土安全','边境管控','移民执法','反恐','网络安全'],relationship:'国土安全部长,前南达科他州长,边境强硬派'},\n {name:'Tulsi Gabbard',capabilities:['国家情报','情报分析','反间谍','网络战','安全评估'],relationship:'国家情报总监,前民主党国会议员,转投共和党的盟友'},\n {name:'Robert F. Kennedy Jr.',capabilities:['公共卫生','疫苗政策','食品安全','药品监管','医疗改革'],relationship:'卫生与公众服务部长,反建制派,疫苗怀疑论者'}\n ]\n }\n};\n\nfunction renderDemoCards(){\n let h='<div style=\"margin-top:24px\"><div style=\"font-size:14px;font-weight:600;margin-bottom:4px\">&#127918; 快速体验 Demo</div><div style=\"font-size:12px;color:var(--text-dim);margin-bottom:12px\">选择一个场景,一键加载碳基节点,立即开始编排</div></div>';\n h+='<div class=\"demo-grid\">';\n for(const[key,d]of Object.entries(DEMOS)){\n h+='<div class=\"demo-card\" id=\"demo-card-'+key+'\" onclick=\"loadDemo(\\\\''+key+'\\\\')\">';\n h+='<div class=\"demo-card-emoji\">'+d.emoji+'</div>';\n h+='<div class=\"demo-card-title\">'+d.title+'</div>';\n h+='<div class=\"demo-card-role\">'+d.role+'</div>';\n h+='<div class=\"demo-card-desc\">'+esc(d.desc)+'</div>';\n h+='<div class=\"demo-card-agents\">';\n for(const a of d.agents)h+='<span>'+esc(a.name)+'</span>';\n h+='</div>';\n h+='<div class=\"demo-card-prompt\">&#128161; &quot;'+esc(d.prompt)+'&quot;</div>';\n h+='</div>';\n }\n h+='</div>';\n return h;\n}\n\nwindow.loadDemo=async function(key){\n const demo=DEMOS[key];\n if(!demo)return;\n const card=document.getElementById('demo-card-'+key);\n if(card){\n const ld=document.createElement('div');ld.className='demo-loading';\n ld.innerHTML='<div class=\"spinner\"></div><div style=\"font-size:12px;color:var(--text-dim)\">加载 '+demo.title+' 场景中...</div>';\n card.appendChild(ld);\n }\n let ok=0;\n const demoAgentIds=[];\n for(const a of demo.agents){\n try{\n const r=await fetch(API+'/nodes',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:a.name,capabilities:a.capabilities,relationship:a.relationship})});\n if(r.ok){ok++;const d=await r.json();demoAgentIds.push(d.agent_id);}\n }catch{}\n }\n toast(demo.title+' 场景已加载!'+ok+'/'+demo.agents.length+' 个节点注册成功',true);\n // Switch to pipeline and open AI planning with suggested prompt\n load('fleet');\n setTimeout(()=>{\n // Switch tab to pipeline\n const tabs=document.querySelectorAll('.tab');\n const panels=document.querySelectorAll('.panel');\n tabs.forEach(x=>x.classList.remove('active'));\n panels.forEach(x=>x.classList.remove('active'));\n tabs[1].classList.add('active');\n document.getElementById('pipeline').classList.add('active');\n load('pipeline');\n // Wait for pipeline to render, then open create job with demo prompt\n setTimeout(()=>showCreateJob(demo.prompt,demoAgentIds),300);\n },200);\n};\n\nwindow.showDemoSelector=function(){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n let h='<div class=\"form-card\"><h3>&#127918; 选择 Demo 场景</h3>';\n h+='<div style=\"font-size:12px;color:var(--text-dim);margin-bottom:16px\">一键加载碳基节点,立即开始编排体验</div>';\n for(const[key,d]of Object.entries(DEMOS)){\n h+='<div class=\"demo-card\" id=\"demo-card-'+key+'\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove();loadDemo(\\\\''+key+'\\\\')\" style=\"margin-bottom:10px\">';\n h+='<div style=\"display:flex;align-items:center;gap:10px;margin-bottom:8px\"><span style=\"font-size:28px\">'+d.emoji+'</span><div><div class=\"demo-card-title\">'+d.title+'</div><div class=\"demo-card-role\">'+d.role+'</div></div></div>';\n h+='<div class=\"demo-card-desc\">'+esc(d.desc)+'</div>';\n h+='<div class=\"demo-card-agents\">';\n for(const a of d.agents)h+='<span>'+esc(a.name)+'</span>';\n h+='</div></div>';\n }\n h+='<div class=\"btn-group\"><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button></div></div>';\n ov.innerHTML=h;\n document.body.appendChild(ov);\n};\n\n// ═══════════════════════════════════════════════\n// FLEET\n// ═══════════════════════════════════════════════\nasync function loadFleet(el){\n el.innerHTML='<div class=\"empty-state\"><p>加载中...</p></div>';\n try{\n const r=await fetch(API+'/nodes/status');\n const d=await r.json();\n cachedAgents=d.agents||[];\n let h='<div class=\"section-hd\"><h2>碳基算力池</h2><div style=\"display:flex;gap:6px\"><button class=\"btn btn-ghost btn-sm\" onclick=\"showDemoSelector()\">&#127918; Demo</button><button class=\"btn btn-primary btn-sm\" onclick=\"showAddAgent()\">+ 添加节点</button></div></div>';\n h+='<div class=\"stats\">';\n h+='<div class=\"stat\"><div class=\"stat-val\">'+d.total+'</div><div class=\"stat-lbl\">总计</div></div>';\n h+='<div class=\"stat\"><div class=\"stat-val\" style=\"color:var(--green)\">'+d.idle+'</div><div class=\"stat-lbl\">空闲</div></div>';\n h+='<div class=\"stat\"><div class=\"stat-val\" style=\"color:var(--yellow)\">'+d.busy+'</div><div class=\"stat-lbl\">忙碌</div></div>';\n h+='<div class=\"stat\"><div class=\"stat-val\" style=\"color:var(--red)\">'+d.offline+'</div><div class=\"stat-lbl\">离线</div></div>';\n h+='<div class=\"stat\"><div class=\"stat-val\" style=\"color:var(--purple)\">'+d.oom+'</div><div class=\"stat-lbl\">崩溃</div></div>';\n h+='</div>';\n if(!d.agents.length){\n h+='<div class=\"empty-state\" style=\"margin-top:32px\"><div class=\"icon\">&#128100;</div><p>还没有碳基节点。点击上方「+ 添加节点」手动注册,或选择一个 Demo 场景快速体验。</p></div>';\n h+=renderDemoCards();\n el.innerHTML=h;return;\n }\n h+='<div class=\"grid\">';\n for(const a of d.agents){\n h+='<div class=\"card\"><div class=\"agent-header\"><span class=\"dot '+a.status+'\"></span><span class=\"agent-name\">'+esc(a.name)+'</span></div>';\n h+='<div class=\"agent-id\">'+a.agent_id+'</div>';\n if(a.relationship)h+='<div style=\"font-size:11px;color:var(--purple);margin-bottom:4px\">&#128101; '+esc(a.relationship)+'</div>';\n h+='<div class=\"caps\">';for(const c of a.capabilities)h+='<span class=\"cap\">'+esc(c)+'</span>';h+='</div>';\n h+='<div class=\"agent-meta\"><span>任务: '+a.active_task_count+'</span>';\n if(a.avg_delivery_hours!==null)h+='<span>平均交付: '+a.avg_delivery_hours+'h</span>';\n h+='</div>';\n h+='<div class=\"agent-actions\">';\n h+='<select onchange=\"changeStatus(\\\\''+a.agent_id+'\\\\',this.value)\">';\n for(const s of ['IDLE','BUSY','OFFLINE','OOM'])h+='<option'+(s===a.status?' selected':'')+'>'+s+'</option>';\n h+='</select>';\n h+='<button onclick=\"deleteAgent(\\\\''+a.agent_id+'\\\\')\">删除</button>';\n h+='</div></div>';\n }\n h+='</div>';\n el.innerHTML=h;\n }catch(e){el.innerHTML='<div class=\"empty-state\"><p>加载失败: '+e.message+'</p></div>'}\n}\nwindow.showAddAgent=function(){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n ov.innerHTML='<div class=\"form-card\"><h3>+ 添加碳基节点</h3>'\n +'<div class=\"fg\"><label>节点名称</label><input id=\"aa-name\" placeholder=\"例: 前端老李\"/></div>'\n +'<div class=\"fg\"><label>技能标签</label><input id=\"aa-caps\" placeholder=\"例: UI/UX, 前端开发, 抗压能力强\"/><div class=\"hint\">多个标签用逗号分隔</div></div>'\n +'<div class=\"fg\"><label>与你的关系 <span style=\"color:var(--text-dim);font-weight:400\">(可选)</span></label><input id=\"aa-rel\" placeholder=\"例: 直属下属 / 实习生 / 外包同事 / 义弟\"/><div class=\"hint\">描述此人与你的关系,AI 规划和模拟交付时会参考</div></div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-primary\" onclick=\"submitAgent()\">注册节点</button><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button></div></div>';\n document.body.appendChild(ov);\n document.getElementById('aa-name').focus();\n};\nwindow.submitAgent=async function(){\n const name=document.getElementById('aa-name').value.trim();\n const caps=document.getElementById('aa-caps').value.trim();\n const rel=document.getElementById('aa-rel').value.trim();\n if(!name){toast('请输入节点名称',false);return}\n if(!caps){toast('请输入至少一个技能标签',false);return}\n try{\n const r=await fetch(API+'/nodes',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name,capabilities:caps.split(',').map(s=>s.trim()).filter(Boolean),relationship:rel})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'注册失败',false);return}\n toast('节点 '+d.agent_id+' 注册成功!',true);\n document.getElementById('overlay').remove();\n load('fleet');\n }catch{toast('网络错误',false)}\n};\nwindow.changeStatus=async function(id,status){\n try{\n const r=await fetch(API+'/nodes/'+id+'/status',{method:'PATCH',headers:{'Content-Type':'application/json'},body:JSON.stringify({status})});\n if(!r.ok){const d=await r.json();toast(d.error||'更新失败',false);return}\n toast('状态已更新',true);\n }catch{toast('网络错误',false)}\n};\nwindow.deleteAgent=async function(id){\n if(!confirm('确定删除节点 '+id+'?'))return;\n try{\n const r=await fetch(API+'/nodes/'+id,{method:'DELETE'});\n if(!r.ok){const d=await r.json();toast(d.error||'删除失败',false);return}\n toast('节点已删除',true);load('fleet');\n }catch{toast('网络错误',false)}\n};\n\n// ═══════════════════════════════════════════════\n// PIPELINE\n// ═══════════════════════════════════════════════\nlet allTasks=[];\nfunction tcard(t){\n return '<div class=\"tcard\" onclick=\"showTaskDetail(\\\\''+t.trace_id+'\\\\')\"><div class=\"tcard-trace\">'+t.trace_id+'</div><div>'+esc(t.todo_description)+'</div><div class=\"tcard-meta\">'+t.assignee_id+' | '+new Date(t.deadline).toLocaleString()+'</div></div>';\n}\nasync function loadPipeline(el){\n el.innerHTML='<div class=\"empty-state\"><p>加载中...</p></div>';\n allTasks=[];\n try{\n try{const ar=await fetch(API+'/nodes/status');const ad=await ar.json();cachedAgents=ad.agents||[]}catch{}\n const r=await fetch(API+'/jobs/active');\n const d=await r.json();\n let h='<div class=\"section-hd\"><h2>碳基编排大盘</h2><button class=\"btn btn-primary btn-sm\" onclick=\"showCreateJob()\">+ 创建任务</button></div>';\n if(!d.jobs.length){\n h+='<div class=\"empty-state\" style=\"margin-top:32px\"><div class=\"icon\">📋</div><p>暂无进行中的任务。点击上方「+ 创建任务」,输入需求后 AI 自动规划分发。';\n if(!cachedAgents.length)h+='<br/><br/>⚠️ 需要先在「碳基算力池」中添加碳基节点。';\n h+='</p></div>';\n el.innerHTML=h;return;\n }\n for(const j of d.jobs){\n const res=j.tasks.filter(t=>t.status==='RESOLVED').length;\n const tot=j.tasks.length;\n const pct=tot?Math.round(res/tot*100):0;\n const dispatched=j.tasks.filter(t=>t.status==='DISPATCHED'||t.status==='PENDING');\n const overdue=j.tasks.filter(t=>t.status==='OVERDUE');\n const done=j.tasks.filter(t=>t.status==='RESOLVED');\n allTasks.push(...j.tasks);\n h+='<div class=\"job-card\"><div class=\"job-hd\"><span class=\"job-title\">'+esc(j.original_prompt)+'</span><span class=\"job-id\">'+j.job_id+'</span></div>';\n h+='<div class=\"pbar-wrap\"><div class=\"pbar-label\">'+res+'/'+tot+' 已完成 ('+pct+'%)</div><div class=\"pbar\"><div class=\"pbar-fill\" style=\"width:'+pct+'%\"></div></div></div>';\n if(pct===100)h+='<div style=\"margin-bottom:12px\"><button class=\"btn btn-green btn-sm\" onclick=\"reviewJob(\\\\''+j.job_id+'\\\\')\">AI 聚合审查</button></div>';\n h+='<div class=\"kanban\"><div class=\"lane\"><div class=\"lane-hd y\">已分发 ('+dispatched.length+')</div>'+dispatched.map(tcard).join('')+'</div>';\n h+='<div class=\"lane\"><div class=\"lane-hd r\">已超时 ('+overdue.length+')</div>'+overdue.map(tcard).join('')+'</div>';\n h+='<div class=\"lane\"><div class=\"lane-hd g\">已交付 ('+done.length+')</div>'+done.map(tcard).join('')+'</div></div></div>';\n }\n el.innerHTML=h;\n }catch(e){el.innerHTML='<div class=\"empty-state\"><p>加载失败: '+e.message+'</p></div>'}\n}\n\n// ─── AI Planning Flow ────────────────────────\nlet pendingDemoPrompt='';\nwindow.showCreateJob=function(demoPrompt,demoAgentIds){\n if(!cachedAgents.length){toast('请先在「碳基算力池」中添加至少一个碳基节点',false);return}\n pendingDemoPrompt=demoPrompt||'';\n currentPlan=null;\n // Pre-select: demo agents if provided, otherwise all IDLE agents\n if(demoAgentIds&&demoAgentIds.length){\n selectedAgentIds=new Set(demoAgentIds);\n }else{\n selectedAgentIds=new Set(cachedAgents.filter(a=>a.status==='IDLE').map(a=>a.agent_id));\n }\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n renderPlanStep1(ov);\n document.body.appendChild(ov);\n};\n\nfunction renderPlanStep1(ov){\n let chipFilter='all';\n function renderChips(filter){\n chipFilter=filter;\n const filtered=filter==='idle'?cachedAgents.filter(a=>a.status==='IDLE'):cachedAgents;\n let ch='<div class=\"chip-bar\">';\n ch+='<span class=\"chip-filter'+(filter==='all'?' active':'')+'\" onclick=\"window._filterChips(\\\\'all\\\\')\">全部</span>';\n ch+='<span class=\"chip-filter'+(filter==='idle'?' active':'')+'\" onclick=\"window._filterChips(\\\\'idle\\\\')\">仅空闲</span>';\n ch+='</div>';\n ch+='<div class=\"agent-chips\">';\n for(const a of filtered){\n const sel=selectedAgentIds.has(a.agent_id);\n ch+='<div class=\"achip'+(sel?' selected':'')+'\" onclick=\"window._toggleChip(\\\\''+a.agent_id+'\\\\')\">';\n ch+='<span class=\"adot '+a.status+'\"></span>';\n ch+=esc(a.name);\n ch+='</div>';\n }\n ch+='</div>';\n return ch;\n }\n window._filterChips=function(f){\n chipFilter=f;\n const el=document.getElementById('agent-chip-area');\n if(el)el.innerHTML=renderChips(f);\n };\n window._toggleChip=function(id){\n if(selectedAgentIds.has(id))selectedAgentIds.delete(id);\n else selectedAgentIds.add(id);\n const el=document.getElementById('agent-chip-area');\n if(el)el.innerHTML=renderChips(chipFilter);\n };\n\n ov.innerHTML='<div class=\"form-card\"><h3>AI 智能规划</h3>'\n +'<div class=\"fg\"><label>输入你的需求</label><textarea id=\"plan-prompt\" rows=\"3\" placeholder=\"例: 完成首页重构,包括导航栏、内容区和页脚的响应式改版\" style=\"font-family:var(--font-sans);font-size:13px\">'+esc(pendingDemoPrompt)+'</textarea></div>'\n +'<div class=\"fg\"><label>选择参与的碳基节点 <span style=\"color:var(--text-dim);font-weight:400\">(默认选中空闲节点)</span></label>'\n +'<div id=\"agent-chip-area\">'+renderChips('all')+'</div></div>'\n +'<div class=\"btn-group\">'\n +'<button class=\"btn btn-primary\" onclick=\"planWithAI()\">AI 规划</button>'\n +'<button class=\"btn btn-ghost\" onclick=\"showManualCreate()\">手动创建</button>'\n +'<button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button>'\n +'</div></div>';\n setTimeout(()=>{const ta=document.getElementById('plan-prompt');if(ta)ta.focus()},50);\n};\n\nwindow.planWithAI=async function(){\n const prompt=document.getElementById('plan-prompt').value.trim();\n if(!prompt){toast('请输入需求描述',false);return}\n if(selectedAgentIds.size===0){toast('请至少选择一个碳基节点',false);return}\n\n const ov=document.getElementById('overlay');\n const fc=ov.querySelector('.form-card');\n fc.innerHTML='<h3>AI 智能规划</h3><div class=\"spinner-wrap\"><div class=\"spinner\"></div><div class=\"spinner-text\">AI 正在分析需求、匹配节点、生成话术...</div></div>';\n\n try{\n const r=await fetch(API+'/jobs/plan',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({prompt,agent_ids:Array.from(selectedAgentIds)})});\n const d=await r.json();\n if(!r.ok){\n toast(d.error||'规划失败',false);\n renderPlanStep1(ov);\n return;\n }\n currentPlan={...d};\n renderPlanStep2(ov);\n }catch(e){\n toast('网络错误: '+e.message,false);\n renderPlanStep1(ov);\n }\n};\n\nfunction renderPlanStep2(ov){\n const p=currentPlan;\n let h='<h3>规划预览</h3>';\n h+='<div style=\"font-size:12px;color:var(--text-dim);margin-bottom:14px\">需求: '+esc(p.original_prompt)+'</div>';\n for(let i=0;i<p.planned_tasks.length;i++){\n const t=p.planned_tasks[i];\n const agent=cachedAgents.find(a=>a.agent_id===t.assignee_id);\n const status=agent?agent.status:'IDLE';\n h+='<div class=\"plan-card\">';\n h+='<div class=\"plan-card-hd\"><span class=\"adot '+status+'\"></span><span class=\"plan-card-agent\">'+esc(t.assignee_name)+'</span><span class=\"plan-card-agentid\">'+t.assignee_id+'</span></div>';\n h+='<div class=\"plan-card-desc\">'+esc(t.todo_description)+'</div>';\n h+='<div class=\"briefing-box\"><div class=\"briefing-label\">话术 (可直接发给 TA)</div><button class=\"briefing-copy\" onclick=\"window._copyBriefing('+i+')\">复制</button>'+esc(t.briefing)+'</div>';\n const dlVal=t.deadline.slice(0,16);\n h+='<div class=\"plan-card-dl\"><label style=\"font-size:10px;color:var(--text-dim);margin-right:6px\">截止时间</label><input type=\"datetime-local\" class=\"plan-dl-input\" data-idx=\"'+i+'\" value=\"'+dlVal+'\" style=\"background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 8px;color:var(--text);font-family:var(--font-mono);font-size:12px;outline:none\"/></div>';\n h+='</div>';\n }\n h+='<div class=\"btn-group\">';\n h+='<button class=\"btn btn-green\" onclick=\"dispatchPlan()\">确认分发</button>';\n h+='<button class=\"btn btn-ghost\" onclick=\"renderPlanStep1(document.getElementById(\\\\'overlay\\\\'))\">重新规划</button>';\n h+='<button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button>';\n h+='</div>';\n ov.querySelector('.form-card').innerHTML=h;\n}\nwindow.renderPlanStep1=renderPlanStep1;\n\nwindow._copyBriefing=function(i){\n const text=currentPlan.planned_tasks[i].briefing;\n navigator.clipboard.writeText(text).then(()=>toast('话术已复制',true)).catch(()=>toast('复制失败',false));\n};\n\nwindow.dispatchPlan=async function(){\n const p=currentPlan;\n // Read adjusted deadlines from inputs\n const dlInputs=document.querySelectorAll('.plan-dl-input');\n const tasks=p.planned_tasks.map((t,i)=>{\n const input=dlInputs[i];\n const dl=input?new Date(input.value).toISOString():t.deadline;\n return {assignee_id:t.assignee_id,todo_description:t.todo_description,deadline:dl};\n });\n try{\n const r=await fetch(API+'/jobs/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({original_prompt:p.original_prompt,tasks})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'分发失败',false);return}\n toast('任务已分发!Job: '+d.job_id,true);\n document.getElementById('overlay').remove();\n load('pipeline');\n }catch{toast('网络错误',false)}\n};\n\n// ─── Manual creation (fallback) ──────────────\nwindow.showManualCreate=function(){\n const ov=document.getElementById('overlay');\n const optionsHtml=cachedAgents.map(a=>'<option value=\"'+a.agent_id+'\">'+esc(a.name)+' ('+a.agent_id+')</option>').join('');\n const tomorrow=new Date(Date.now()+86400000).toISOString().slice(0,16);\n ov.querySelector('.form-card').innerHTML='<h3>手动创建任务</h3>'\n +'<div class=\"fg\"><label>任务描述 (原始需求)</label><input id=\"cj-prompt\" placeholder=\"例: 完成首页改版\"/></div>'\n +'<div style=\"margin-bottom:10px;display:flex;justify-content:space-between;align-items:center\"><label style=\"font-size:13px;font-weight:600;color:var(--text)\">子任务列表</label><button class=\"btn btn-ghost btn-sm\" onclick=\"addTaskRow()\">+ 添加子任务</button></div>'\n +'<div id=\"task-rows\"><div class=\"task-row\"><div class=\"fg\"><label>指派节点</label><select class=\"tr-agent\">'+optionsHtml+'</select></div><div class=\"fg\"><label>任务描述</label><input class=\"tr-desc\" placeholder=\"具体要做什么...\"/></div><div class=\"fg\"><label>截止时间</label><input class=\"tr-deadline\" type=\"datetime-local\" value=\"'+tomorrow+'\"/></div></div></div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-primary\" onclick=\"submitJob()\">创建并分发</button><button class=\"btn btn-ghost\" onclick=\"renderPlanStep1(document.getElementById(\\\\'overlay\\\\'))\">返回 AI 规划</button><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button></div>';\n};\nwindow.addTaskRow=function(){\n const optionsHtml=cachedAgents.map(a=>'<option value=\"'+a.agent_id+'\">'+esc(a.name)+' ('+a.agent_id+')</option>').join('');\n const tomorrow=new Date(Date.now()+86400000).toISOString().slice(0,16);\n const row=document.createElement('div');row.className='task-row';\n row.innerHTML='<button class=\"remove-task\" onclick=\"this.parentElement.remove()\">&times;</button>'\n +'<div class=\"fg\"><label>指派节点</label><select class=\"tr-agent\">'+optionsHtml+'</select></div>'\n +'<div class=\"fg\"><label>任务描述</label><input class=\"tr-desc\" placeholder=\"具体要做什么...\"/></div>'\n +'<div class=\"fg\"><label>截止时间</label><input class=\"tr-deadline\" type=\"datetime-local\" value=\"'+tomorrow+'\"/></div>';\n document.getElementById('task-rows').appendChild(row);\n};\nwindow.submitJob=async function(){\n const prompt=document.getElementById('cj-prompt').value.trim();\n if(!prompt){toast('请输入任务描述',false);return}\n const rows=document.querySelectorAll('.task-row');\n const tasks=[];\n for(const row of rows){\n const aid=row.querySelector('.tr-agent').value;\n const desc=row.querySelector('.tr-desc').value.trim();\n const dl=row.querySelector('.tr-deadline').value;\n if(!desc){toast('每个子任务都需要填写描述',false);return}\n tasks.push({assignee_id:aid,todo_description:desc,deadline:new Date(dl).toISOString()});\n }\n if(!tasks.length){toast('至少添加一个子任务',false);return}\n try{\n const r=await fetch(API+'/jobs/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({original_prompt:prompt,tasks})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'创建失败',false);return}\n toast('任务已创建并分发!Job: '+d.job_id,true);\n document.getElementById('overlay').remove();\n load('pipeline');\n }catch{toast('网络错误',false)}\n};\nwindow.reviewJob=async function(jobId){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n ov.innerHTML='<div class=\"form-card\"><h3>AI 聚合审查</h3><div class=\"spinner-wrap\"><div class=\"spinner\"></div><div class=\"spinner-text\">AI 正在审查所有交付物...</div></div></div>';\n document.body.appendChild(ov);\n try{\n const r=await fetch(API+'/jobs/'+jobId+'/review',{method:'POST'});\n const d=await r.json();\n if(!r.ok){toast(d.error||'审查失败',false);ov.remove();return}\n const fc=ov.querySelector('.form-card');\n fc.innerHTML='<h3>AI 聚合审查</h3>'\n +'<div style=\"font-size:12px;color:var(--text-dim);margin-bottom:12px\">需求: '+esc(d.original_prompt)+'</div>'\n +'<div class=\"result-display\" style=\"max-height:400px\">'+esc(d.review).replace(/\\\\n/g,'<br>')+'</div>'\n +'<div style=\"font-size:10px;color:var(--text-dim);margin-top:8px;font-family:var(--font-mono)\">审查时间: '+new Date(d.reviewed_at).toLocaleString()+'</div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">关闭</button></div>';\n }catch(e){toast('网络错误: '+e.message,false);ov.remove()}\n};\n\n// ─── Task Detail Modal ──────────────────────\nwindow.showTaskDetail=function(traceId){\n const t=allTasks.find(x=>x.trace_id===traceId);\n if(!t)return;\n const agent=cachedAgents.find(a=>a.agent_id===t.assignee_id);\n const agentName=agent?agent.name:t.assignee_id;\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n\n let h='<div class=\"form-card\"><div class=\"task-detail-hd\"><span class=\"task-detail-status '+t.status+'\">'+t.status+'</span><span style=\"font-size:15px;font-weight:600\">任务详情</span></div>';\n\n // Trace ID with copy\n h+='<div class=\"trace-copy-row\"><span>'+t.trace_id+'</span><button onclick=\"navigator.clipboard.writeText(\\\\''+t.trace_id+'\\\\').then(()=>toast(\\\\'已复制\\\\',true))\">复制</button></div>';\n\n // Info rows\n h+='<div class=\"detail-row\"><div class=\"detail-label\">指派节点</div><div class=\"detail-value\">'+esc(agentName)+' <span style=\"color:var(--text-dim);font-family:var(--font-mono);font-size:11px\">'+t.assignee_id+'</span></div></div>';\n h+='<div class=\"detail-row\"><div class=\"detail-label\">任务描述</div><div class=\"detail-value\">'+esc(t.todo_description)+'</div></div>';\n h+='<div class=\"detail-row\"><div class=\"detail-label\">截止时间</div><div class=\"detail-value\" style=\"font-family:var(--font-mono)\">'+new Date(t.deadline).toLocaleString()+'</div></div>';\n\n if(t.status==='RESOLVED'&&t.result_data){\n // Show result\n let resultText='';\n if(typeof t.result_data==='object'&&t.result_data){resultText=t.result_data.text||JSON.stringify(t.result_data,null,2)}else{resultText=String(t.result_data||'')}\n h+='<div class=\"detail-row\"><div class=\"detail-label\">交付结果</div><div class=\"result-display\">'+esc(resultText)+'</div></div>';\n h+='<div class=\"btn-group\"><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">关闭</button></div>';\n }else{\n // Input for resume/reject\n h+='<div class=\"fg\" style=\"margin-top:16px\"><label>提交 Human 交付物</label><textarea id=\"td-payload\" rows=\"4\" placeholder=\"粘贴交付物内容、GitHub PR/Commit URL、工作汇报等...\"></textarea><div class=\"hint\">支持贴 GitHub URL(PR、Commit、Issue),AI 审查时会分析</div></div>';\n h+='<div class=\"btn-group\">';\n h+='<button class=\"btn btn-primary btn-sm\" onclick=\"simulateDelivery(\\\\''+t.trace_id+'\\\\')\">&#129302; 模拟交付</button>';\n h+='<button class=\"btn btn-green\" onclick=\"taskResume(\\\\''+t.trace_id+'\\\\')\">提交交付 (Resume)</button>';\n h+='<button class=\"btn btn-danger\" onclick=\"taskReject(\\\\''+t.trace_id+'\\\\')\">打回重做 (Reject)</button>';\n h+='<button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button>';\n h+='</div>';\n }\n h+='</div>';\n ov.innerHTML=h;\n document.body.appendChild(ov);\n};\n\nwindow.taskResume=async function(traceId){\n const payload=document.getElementById('td-payload').value.trim();\n if(!payload){toast('请输入交付内容',false);return}\n try{\n const r=await fetch(API+'/tasks/resume',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:traceId,result_data:{text:payload}})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'提交失败',false);return}\n toast(d.job_complete?'任务已交付!Job 已全部完成,可以聚合同步。':'任务 '+traceId+' 已交付。',true);\n document.getElementById('overlay').remove();\n load('pipeline');\n }catch{toast('网络错误',false)}\n};\n\nwindow.taskReject=async function(traceId){\n if(!confirm('确定打回任务 '+traceId+'?截止时间将延长 24 小时。'))return;\n try{\n const r=await fetch(API+'/tasks/reject',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:traceId})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'操作失败',false);return}\n toast('任务 '+traceId+' 已打回,截止时间已延长 24 小时。',true);\n document.getElementById('overlay').remove();\n load('pipeline');\n }catch{toast('网络错误',false)}\n};\n\nwindow.simulateDelivery=async function(traceId){\n const btn=event.target;\n const origText=btn.innerHTML;\n btn.disabled=true;btn.innerHTML='&#8987; AI 生成中...';\n try{\n const r=await fetch(API+'/tasks/simulate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:traceId})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'模拟失败',false);btn.disabled=false;btn.innerHTML=origText;return}\n const ta=document.getElementById('td-payload');\n if(ta)ta.value=d.simulated_delivery;\n toast('已生成模拟交付内容',true);\n btn.disabled=false;btn.innerHTML=origText;\n }catch(e){toast('网络错误: '+e.message,false);btn.disabled=false;btn.innerHTML=origText}\n};\n\n// ═══════════════════════════════════════════════\n// TERMINAL\n// ═══════════════════════════════════════════════\nfunction loadTerminal(el){\n el.innerHTML='<div class=\"section-hd\"><h2>I/O 交付终端</h2></div>'\n +'<div class=\"form-card\" style=\"margin-top:12px\"><h3>> 提交碳基节点产出</h3>'\n +'<div class=\"fg\"><label>Trace ID (追踪码)</label><input id=\"t-tid\" placeholder=\"例: TK-9527\"/></div>'\n +'<div class=\"fg\"><label>交付载荷</label><textarea id=\"t-payload\" placeholder=\"粘贴交付物内容、GitHub PR/Commit URL、工作汇报等...\"></textarea><div class=\"hint\">支持贴 GitHub URL(PR、Commit、Issue),AI 审查时会分析</div></div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-primary\" onclick=\"doResume()\">提交并恢复 (Resume)</button><button class=\"btn btn-danger\" onclick=\"doReject()\">打回重做 (Reject)</button></div></div>';\n}\nwindow.doResume=async function(){\n const tid=document.getElementById('t-tid').value.trim();\n const payload=document.getElementById('t-payload').value.trim();\n if(!tid){toast('请输入 Trace ID',false);return}\n if(!payload){toast('请输入交付载荷',false);return}\n try{\n const r=await fetch(API+'/tasks/resume',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:tid,result_data:{text:payload}})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'提交失败',false);return}\n toast(d.job_complete?'任务已交付!Job 已全部完成,可以聚合同步。':'任务 '+tid+' 已交付。',true);\n document.getElementById('t-tid').value='';document.getElementById('t-payload').value='';\n }catch{toast('网络错误',false)}\n};\nwindow.doReject=async function(){\n const tid=document.getElementById('t-tid').value.trim();\n if(!tid){toast('请输入 Trace ID',false);return}\n try{\n const r=await fetch(API+'/tasks/reject',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:tid})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'操作失败',false);return}\n toast('任务 '+tid+' 已打回,截止时间已延长 24 小时。',true);\n document.getElementById('t-tid').value='';document.getElementById('t-payload').value='';\n }catch{toast('网络错误',false)}\n};\n\n// ═══════════════════════════════════════════════\n// SETTINGS\n// ═══════════════════════════════════════════════\nwindow.showSettings=async function(){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n ov.innerHTML='<div class=\"form-card\"><h3>LLM 设置</h3><div class=\"spinner-wrap\"><div class=\"spinner\"></div></div></div>';\n document.body.appendChild(ov);\n\n try{\n const r=await fetch(API+'/config');\n const cfg=await r.json();\n const fc=ov.querySelector('.form-card');\n let statusHtml='';\n if(cfg.api_key_set){\n const src=cfg.api_key_source==='dashboard'?'Dashboard 配置':'环境变量';\n statusHtml='<div style=\"background:var(--accent-dim);border:1px solid color-mix(in srgb,var(--accent) 25%,transparent);border-radius:8px;padding:10px 14px;margin-bottom:16px;font-size:12px\"><span style=\"color:var(--green)\">&#10003;</span> API Key 已配置 <span style=\"color:var(--text-dim)\">('+esc(cfg.api_key_masked)+' | 来源: '+src+')</span></div>';\n }else{\n statusHtml='<div style=\"background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.25);border-radius:8px;padding:10px 14px;margin-bottom:16px;font-size:12px;color:var(--red)\">&#9888; 未配置 API Key,AI 规划功能不可用</div>';\n }\n fc.innerHTML='<h3>LLM 设置</h3>'+statusHtml\n +'<div class=\"fg\"><label>提供商</label><select id=\"cfg-provider\"><option value=\"claude\"'+(cfg.provider==='claude'?' selected':'')+'>Claude (Anthropic)</option><option value=\"openai\"'+(cfg.provider==='openai'?' selected':'')+'>OpenAI</option></select></div>'\n +'<div class=\"fg\"><label>API Key</label><input id=\"cfg-key\" type=\"password\" placeholder=\"'+(cfg.api_key_set?'已配置,留空则不修改':'输入你的 API Key...')+'\"/><div class=\"hint\">Claude: sk-ant-... | OpenAI: sk-...</div></div>'\n +'<div class=\"fg\"><label>模型 <span style=\"color:var(--text-dim);font-weight:400\">(可选,留空用默认)</span></label><input id=\"cfg-model\" value=\"'+esc(cfg.model||'')+'\" placeholder=\"例: claude-sonnet-4-20250514 / gpt-4o\"/></div>'\n +'<div class=\"fg\"><label>API Base URL <span style=\"color:var(--text-dim);font-weight:400\">(可选,留空用官方地址)</span></label><input id=\"cfg-baseurl\" value=\"'+esc(cfg.base_url||'')+'\" placeholder=\"例: https://your-proxy.com\"/><div class=\"hint\">私有部署: 填写你的模型服务地址,如 vLLM / Ollama / Azure 等</div></div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-primary\" onclick=\"saveSettings()\">保存</button><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button></div>';\n }catch{\n ov.querySelector('.form-card').innerHTML='<h3>LLM 设置</h3><p style=\"color:var(--red)\">加载配置失败</p>';\n }\n};\nwindow.saveSettings=async function(){\n const provider=document.getElementById('cfg-provider').value;\n const apiKey=document.getElementById('cfg-key').value.trim();\n const model=document.getElementById('cfg-model').value.trim();\n const baseUrl=document.getElementById('cfg-baseurl').value.trim();\n const body={provider};\n if(apiKey)body.api_key=apiKey;\n body.model=model;\n body.base_url=baseUrl;\n try{\n const r=await fetch(API+'/config',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});\n if(!r.ok){const d=await r.json();toast(d.error||'保存失败',false);return}\n toast('设置已保存',true);\n document.getElementById('overlay').remove();\n }catch{toast('网络错误',false)}\n};\n\n// ═══════════════════════════════════════════════\n// TABS & INIT\n// ═══════════════════════════════════════════════\nconst tabs=document.querySelectorAll('.tab');\nconst panels=document.querySelectorAll('.panel');\nfunction load(id){\n const el=document.getElementById(id);\n if(id==='fleet')loadFleet(el);\n else if(id==='pipeline')loadPipeline(el);\n else if(id==='terminal')loadTerminal(el);\n}\ntabs.forEach(t=>t.addEventListener('click',()=>{\n tabs.forEach(x=>x.classList.remove('active'));\n panels.forEach(x=>x.classList.remove('active'));\n t.classList.add('active');\n const id=t.dataset.panel;\n document.getElementById(id).classList.add('active');\n load(id);\n}));\nload('fleet');\nsetInterval(()=>{\n const a=document.querySelector('.tab.active');\n if(a&&a.dataset.panel!=='terminal')load(a.dataset.panel);\n},15000);\n</script>\n</body>\n</html>`;\n}\n"],"mappings":"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A;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAA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","names":["db","db","Router","db","db","db","db","db","DEFAULT_BASE_URL","db","db","router","Router","Router","db","db","router","Router","Router","db","router","Router","Router","router","Router","db","db","confirm"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/db/connection.ts","../src/db/schema.ts","../src/routes/nodes.ts","../src/models/agent.ts","../src/utils/trace-id.ts","../src/routes/jobs.ts","../src/models/task.ts","../src/models/job.ts","../src/services/dispatch.ts","../src/models/team.ts","../src/llm/claude.ts","../src/llm/openai.ts","../src/llm/responses.ts","../src/models/config.ts","../src/llm/index.ts","../src/services/planner.ts","../src/routes/tasks.ts","../src/services/resume.ts","../src/services/simulator.ts","../src/routes/sync.ts","../src/models/evaluation.ts","../src/services/reviewer.ts","../src/routes/config.ts","../src/routes/teams.ts","../src/routes/evaluations.ts","../src/services/evaluator.ts","../src/dashboard.ts"],"sourcesContent":["import { Command } from 'commander';\nimport * as p from '@clack/prompts';\nimport chalk from 'chalk';\nimport { startServer } from './server.js';\nimport { getDb } from './db/connection.js';\nimport { initSchema } from './db/schema.js';\nimport { createAgent, listAgents } from './models/agent.js';\nimport { listActiveJobs } from './models/job.js';\nimport { markOverdueTasks } from './models/task.js';\nimport { dispatchJob } from './services/dispatch.js';\nimport { planJob } from './services/planner.js';\nimport { generateId } from './utils/trace-id.js';\nimport type { AgentStatus } from './models/types.js';\n\nconst program = new Command();\n\nprogram\n .name('humanclaw')\n .description(\n 'Carbon-based node orchestration framework - treating humans as distributed worker nodes'\n )\n .version('1.0.0');\n\n// ─── serve ───────────────────────────────────────────────────────────────────\n\nprogram\n .command('serve')\n .description('Start the HumanClaw server')\n .option('-p, --port <port>', 'Server port', '2026')\n .action(opts => {\n const port = parseInt(opts.port, 10);\n startServer(port);\n });\n\n// ─── agent ───────────────────────────────────────────────────────────────────\n\nconst agentCmd = program\n .command('agent')\n .description('Manage carbon-based nodes (HumanAgent)');\n\nagentCmd\n .command('add')\n .description('Register a new carbon-based node')\n .action(async () => {\n const db = getDb();\n initSchema(db);\n\n p.intro(chalk.bgCyan(' Register New Carbon-Based Node '));\n\n const name = await p.text({\n message: 'Node alias (name):',\n placeholder: 'e.g. Frontend Lao Li',\n validate: v => (!v ? 'Name is required' : undefined),\n });\n\n if (p.isCancel(name)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n\n const capInput = await p.text({\n message: 'Capabilities (comma-separated):',\n placeholder: 'e.g. UI/UX, Frontend Dev, Stress Resistant',\n validate: v => (!v ? 'At least one capability required' : undefined),\n });\n\n if (p.isCancel(capInput)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n\n const capabilities = (capInput as string)\n .split(',')\n .map(s => s.trim())\n .filter(Boolean);\n\n const relInput = await p.text({\n message: 'Relationship with you (optional):',\n placeholder: 'e.g. direct report / intern / contractor',\n defaultValue: '',\n });\n\n if (p.isCancel(relInput)) {\n p.cancel('Cancelled.');\n process.exit(0);\n }\n\n const agent = createAgent({\n agent_id: generateId('emp'),\n name: name as string,\n capabilities,\n relationship: (relInput as string) || '',\n status: 'IDLE',\n });\n\n p.outro(\n `${chalk.green('Node registered!')} ID: ${chalk.bold(agent.agent_id)}`\n );\n });\n\nagentCmd\n .command('list')\n .description('Show fleet status')\n .action(() => {\n const db = getDb();\n initSchema(db);\n\n const agents = listAgents();\n if (agents.length === 0) {\n console.log(chalk.dim(' No carbon-based nodes registered.'));\n return;\n }\n\n const statusIcon: Record<AgentStatus, string> = {\n IDLE: '🟢',\n BUSY: '🟡',\n OFFLINE: '🔴',\n OOM: '🟣',\n };\n\n console.log(chalk.bold('\\n Carbon Compute Pool\\n'));\n\n for (const agent of agents) {\n const icon = statusIcon[agent.status];\n const caps = agent.capabilities.join(', ');\n console.log(\n ` ${icon} ${chalk.bold(agent.name)} (${chalk.dim(agent.agent_id)})`\n );\n console.log(` Capabilities: ${chalk.cyan(caps)}`);\n console.log(` Status: ${agent.status}\\n`);\n }\n });\n\n// ─── status ──────────────────────────────────────────────────────────────────\n\nprogram\n .command('status')\n .description('Show active jobs overview')\n .action(() => {\n const db = getDb();\n initSchema(db);\n\n markOverdueTasks();\n const jobs = listActiveJobs();\n\n if (jobs.length === 0) {\n console.log(chalk.dim('\\n No active jobs.\\n'));\n return;\n }\n\n console.log(chalk.bold('\\n Async Orchestration Dashboard\\n'));\n\n for (const job of jobs) {\n const resolved = job.tasks.filter(t => t.status === 'RESOLVED').length;\n const total = job.tasks.length;\n const pct = Math.round((resolved / total) * 100);\n const bar = '█'.repeat(Math.round(pct / 5)) + '░'.repeat(20 - Math.round(pct / 5));\n\n console.log(` ${chalk.bold(job.job_id)} - ${job.original_prompt}`);\n console.log(` Progress: [${bar}] ${resolved}/${total} (${pct}%)`);\n\n for (const task of job.tasks) {\n const statusColor =\n task.status === 'RESOLVED'\n ? chalk.green\n : task.status === 'OVERDUE'\n ? chalk.red\n : chalk.yellow;\n console.log(\n ` ${statusColor(task.status.padEnd(10))} ${task.trace_id} -> ${chalk.dim(task.assignee_id)}`\n );\n console.log(` ${task.todo_description}`);\n }\n console.log();\n }\n });\n\n// ─── plan ───────────────────────────────────────────────────────────────────\n\nprogram\n .command('plan')\n .description('AI-powered task planning from natural language')\n .argument('[prompt]', 'What you want to get done')\n .option('--agents <ids>', 'Comma-separated agent IDs (default: all IDLE)')\n .option('--dispatch', 'Automatically dispatch after planning')\n .action(async (promptArg?: string, opts?: { agents?: string; dispatch?: boolean }) => {\n const db = getDb();\n initSchema(db);\n\n p.intro(chalk.bgCyan(' AI 智能规划 '));\n\n let prompt = promptArg;\n if (!prompt) {\n const input = await p.text({\n message: '输入你的需求:',\n placeholder: '例: 完成首页重构,包括导航栏和内容区的响应式改版',\n validate: v => (!v ? '需求描述不能为空' : undefined),\n });\n if (p.isCancel(input)) {\n p.cancel('已取消');\n process.exit(0);\n }\n prompt = input as string;\n }\n\n const agentIds = opts?.agents?.split(',').map(s => s.trim()).filter(Boolean);\n\n const spin = p.spinner();\n spin.start('AI 正在分析需求、匹配节点、生成话术...');\n\n try {\n const plan = await planJob({ prompt, agent_ids: agentIds }, undefined, db);\n spin.stop('规划完成!');\n\n console.log(chalk.bold(`\\n 需求: ${chalk.white(plan.original_prompt)}\\n`));\n\n for (const task of plan.planned_tasks) {\n console.log(chalk.cyan(` ┌─ ${chalk.bold(task.assignee_name)} (${chalk.dim(task.assignee_id)})`));\n console.log(chalk.cyan(' │') + ` 任务: ${task.todo_description}`);\n console.log(chalk.cyan(' │') + ` 话术: ${chalk.italic(task.briefing)}`);\n console.log(chalk.cyan(' │') + ` 截止: ${new Date(task.deadline).toLocaleString()}`);\n console.log(chalk.cyan(' └─'));\n console.log();\n }\n\n if (opts?.dispatch) {\n const tasks = plan.planned_tasks.map(t => ({\n assignee_id: t.assignee_id,\n todo_description: t.todo_description,\n deadline: t.deadline,\n }));\n const job = dispatchJob({\n original_prompt: plan.original_prompt,\n tasks,\n }, db);\n p.outro(`${chalk.green('已分发!')} Job: ${chalk.bold(job.job_id)}`);\n } else {\n const confirm = await p.confirm({\n message: '确认分发这些任务?',\n });\n if (p.isCancel(confirm) || !confirm) {\n p.outro(chalk.dim('已取消分发'));\n process.exit(0);\n }\n const tasks = plan.planned_tasks.map(t => ({\n assignee_id: t.assignee_id,\n todo_description: t.todo_description,\n deadline: t.deadline,\n }));\n const job = dispatchJob({\n original_prompt: plan.original_prompt,\n tasks,\n }, db);\n p.outro(`${chalk.green('已分发!')} Job: ${chalk.bold(job.job_id)}`);\n }\n } catch (error) {\n spin.stop('规划失败');\n const message = error instanceof Error ? error.message : 'Unknown error';\n p.outro(chalk.red(message));\n process.exit(1);\n }\n });\n\nprogram.parse();\n","import express from 'express';\nimport cors from 'cors';\nimport { getDb } from './db/connection.js';\nimport { initSchema } from './db/schema.js';\nimport nodesRouter from './routes/nodes.js';\nimport jobsRouter from './routes/jobs.js';\nimport tasksRouter from './routes/tasks.js';\nimport syncRouter from './routes/sync.js';\nimport configRouter from './routes/config.js';\nimport teamsRouter from './routes/teams.js';\nimport evaluationsRouter from './routes/evaluations.js';\nimport { getDashboardHtml } from './dashboard.js';\n\nexport function createServer(port = 2026) {\n // Initialize database\n const db = getDb();\n initSchema(db);\n\n const app = express();\n\n // Middleware\n app.use(cors());\n app.use(express.json({ limit: '10mb' }));\n\n // API routes\n app.use('/api/v1/nodes', nodesRouter);\n app.use('/api/v1/jobs', jobsRouter);\n app.use('/api/v1/tasks', tasksRouter);\n app.use('/api/v1/jobs', syncRouter);\n app.use('/api/v1/config', configRouter);\n app.use('/api/v1/teams', teamsRouter);\n app.use('/api/v1/evaluations', evaluationsRouter);\n\n // Serve dashboard as inline HTML (no build step needed)\n const dashboardHtml = getDashboardHtml();\n app.get('/', (_req, res) => {\n res.type('html').send(dashboardHtml);\n });\n\n // Error handler\n app.use(\n (\n err: Error,\n _req: express.Request,\n res: express.Response,\n _next: express.NextFunction\n ) => {\n console.error('Server error:', err.message);\n res.status(500).json({ error: 'Internal server error' });\n }\n );\n\n return { app, port };\n}\n\nexport function startServer(port = 2026) {\n const { app } = createServer(port);\n\n app.listen(port, () => {\n console.log(`\\n HumanClaw server running at http://localhost:${port}`);\n console.log(` Dashboard: http://localhost:${port}`);\n console.log(` API base: http://localhost:${port}/api/v1\\n`);\n });\n}\n","import Database from 'better-sqlite3';\nimport path from 'node:path';\nimport fs from 'node:fs';\n\nlet db: Database.Database | null = null;\n\nconst DEFAULT_DB_PATH = path.join(\n process.env.HUMANCLAW_DATA_DIR ?? process.cwd(),\n 'humanclaw.db'\n);\n\nexport function getDb(dbPath?: string): Database.Database {\n if (db) return db;\n\n const resolvedPath = dbPath ?? DEFAULT_DB_PATH;\n const dir = path.dirname(resolvedPath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n db = new Database(resolvedPath);\n db.pragma('journal_mode = WAL');\n db.pragma('foreign_keys = ON');\n\n return db;\n}\n\nexport function createInMemoryDb(): Database.Database {\n const memDb = new Database(':memory:');\n memDb.pragma('foreign_keys = ON');\n return memDb;\n}\n\nexport function closeDb(): void {\n if (db) {\n db.close();\n db = null;\n }\n}\n\nexport function setDb(newDb: Database.Database): void {\n db = newDb;\n}\n","import type Database from 'better-sqlite3';\n\nexport function initSchema(db: Database.Database): void {\n db.exec(`\n CREATE TABLE IF NOT EXISTS agents (\n agent_id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n capabilities TEXT NOT NULL DEFAULT '[]',\n relationship TEXT NOT NULL DEFAULT '',\n status TEXT NOT NULL DEFAULT 'IDLE'\n CHECK (status IN ('IDLE', 'BUSY', 'OFFLINE', 'OOM')),\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE TABLE IF NOT EXISTS jobs (\n job_id TEXT PRIMARY KEY,\n original_prompt TEXT NOT NULL,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE TABLE IF NOT EXISTS tasks (\n trace_id TEXT PRIMARY KEY,\n job_id TEXT NOT NULL REFERENCES jobs(job_id) ON DELETE CASCADE,\n assignee_id TEXT NOT NULL REFERENCES agents(agent_id),\n todo_description TEXT NOT NULL,\n deadline TEXT NOT NULL,\n payload TEXT NOT NULL DEFAULT '{}',\n status TEXT NOT NULL DEFAULT 'PENDING'\n CHECK (status IN ('PENDING', 'DISPATCHED', 'RESOLVED', 'OVERDUE')),\n result_data TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE INDEX IF NOT EXISTS idx_tasks_job_id ON tasks(job_id);\n CREATE INDEX IF NOT EXISTS idx_tasks_assignee_id ON tasks(assignee_id);\n CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);\n\n CREATE TABLE IF NOT EXISTS config (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n\n CREATE TABLE IF NOT EXISTS teams (\n team_id TEXT PRIMARY KEY,\n name TEXT NOT NULL,\n description TEXT NOT NULL DEFAULT '',\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE TABLE IF NOT EXISTS team_members (\n team_id TEXT NOT NULL REFERENCES teams(team_id) ON DELETE CASCADE,\n agent_id TEXT NOT NULL REFERENCES agents(agent_id) ON DELETE CASCADE,\n relationship TEXT NOT NULL DEFAULT '',\n PRIMARY KEY (team_id, agent_id)\n );\n\n CREATE INDEX IF NOT EXISTS idx_team_members_agent ON team_members(agent_id);\n\n CREATE TABLE IF NOT EXISTS evaluations (\n eval_id TEXT PRIMARY KEY,\n job_id TEXT NOT NULL REFERENCES jobs(job_id) ON DELETE CASCADE,\n agent_id TEXT NOT NULL REFERENCES agents(agent_id) ON DELETE CASCADE,\n trace_id TEXT NOT NULL REFERENCES tasks(trace_id) ON DELETE CASCADE,\n rating_system TEXT NOT NULL CHECK (rating_system IN ('ali', 'letter', 'em')),\n rating TEXT NOT NULL,\n weight REAL NOT NULL DEFAULT 1.0,\n comment TEXT NOT NULL DEFAULT '',\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n );\n\n CREATE INDEX IF NOT EXISTS idx_evaluations_job ON evaluations(job_id);\n CREATE INDEX IF NOT EXISTS idx_evaluations_agent ON evaluations(agent_id);\n `);\n\n // Migration: add relationship column to existing agents table\n const cols = db.prepare(\"PRAGMA table_info(agents)\").all() as Array<{ name: string }>;\n if (!cols.some(c => c.name === 'relationship')) {\n db.exec(`ALTER TABLE agents ADD COLUMN relationship TEXT NOT NULL DEFAULT ''`);\n }\n}\n","import { Router } from 'express';\nimport {\n listAgentsWithMetrics,\n createAgent,\n updateAgentStatus,\n deleteAgent,\n getAgentWithTeams,\n} from '../models/agent.js';\nimport { generateId } from '../utils/trace-id.js';\nimport type { AgentStatus } from '../models/types.js';\n\nconst router = Router();\n\n// GET /api/v1/nodes/status - Fleet status with metrics\nrouter.get('/status', (_req, res) => {\n const agents = listAgentsWithMetrics();\n res.json({\n total: agents.length,\n idle: agents.filter(a => a.status === 'IDLE').length,\n busy: agents.filter(a => a.status === 'BUSY').length,\n offline: agents.filter(a => a.status === 'OFFLINE').length,\n oom: agents.filter(a => a.status === 'OOM').length,\n agents,\n });\n});\n\n// POST /api/v1/nodes - Register a new agent\nrouter.post('/', (req, res) => {\n const { name, capabilities, relationship, status } = req.body;\n\n if (!name || !Array.isArray(capabilities)) {\n res.status(400).json({ error: 'name and capabilities[] are required' });\n return;\n }\n\n const agent = createAgent({\n agent_id: generateId('emp'),\n name,\n capabilities,\n relationship: relationship || '',\n status: (status as AgentStatus) ?? 'IDLE',\n });\n\n res.status(201).json(agent);\n});\n\n// PATCH /api/v1/nodes/:agent_id/status - Update agent status\nrouter.patch('/:agent_id/status', (req, res) => {\n const { agent_id } = req.params;\n const { status } = req.body;\n\n const validStatuses: AgentStatus[] = ['IDLE', 'BUSY', 'OFFLINE', 'OOM'];\n if (!validStatuses.includes(status)) {\n res.status(400).json({\n error: `Invalid status. Must be one of: ${validStatuses.join(', ')}`,\n });\n return;\n }\n\n const updated = updateAgentStatus(agent_id, status);\n if (!updated) {\n res.status(404).json({ error: `Agent not found: ${agent_id}` });\n return;\n }\n\n res.json({ agent_id, status });\n});\n\n// GET /api/v1/nodes/:agent_id - Get agent with teams\nrouter.get('/:agent_id', (req, res) => {\n const agent = getAgentWithTeams(req.params.agent_id);\n if (!agent) {\n res.status(404).json({ error: `Agent not found: ${req.params.agent_id}` });\n return;\n }\n res.json(agent);\n});\n\n// DELETE /api/v1/nodes/:agent_id\nrouter.delete('/:agent_id', (req, res) => {\n const { agent_id } = req.params;\n const deleted = deleteAgent(agent_id);\n if (!deleted) {\n res.status(404).json({ error: `Agent not found: ${agent_id}` });\n return;\n }\n res.json({ deleted: agent_id });\n});\n\nexport default router;\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport type {\n HumanAgent,\n AgentRow,\n AgentStatus,\n AgentWithMetrics,\n} from './types.js';\n\nfunction rowToAgent(row: AgentRow): HumanAgent {\n return {\n ...row,\n capabilities: JSON.parse(row.capabilities) as string[],\n };\n}\n\nexport function createAgent(\n agent: Omit<HumanAgent, 'created_at'>,\n db?: Database.Database\n): HumanAgent {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n conn\n .prepare(\n `INSERT INTO agents (agent_id, name, capabilities, relationship, status, created_at)\n VALUES (?, ?, ?, ?, ?, ?)`\n )\n .run(\n agent.agent_id,\n agent.name,\n JSON.stringify(agent.capabilities),\n agent.relationship || '',\n agent.status,\n now\n );\n return { ...agent, created_at: now };\n}\n\nexport function getAgent(\n agentId: string,\n db?: Database.Database\n): HumanAgent | undefined {\n const conn = db ?? getDb();\n const row = conn\n .prepare('SELECT * FROM agents WHERE agent_id = ?')\n .get(agentId) as AgentRow | undefined;\n return row ? rowToAgent(row) : undefined;\n}\n\nexport function listAgents(db?: Database.Database): HumanAgent[] {\n const conn = db ?? getDb();\n const rows = conn.prepare('SELECT * FROM agents ORDER BY created_at').all() as AgentRow[];\n return rows.map(rowToAgent);\n}\n\nexport function updateAgentStatus(\n agentId: string,\n status: AgentStatus,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const result = conn\n .prepare('UPDATE agents SET status = ? WHERE agent_id = ?')\n .run(status, agentId);\n return result.changes > 0;\n}\n\nexport function deleteAgent(\n agentId: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const result = conn\n .prepare('DELETE FROM agents WHERE agent_id = ?')\n .run(agentId);\n return result.changes > 0;\n}\n\nexport function getAgentWithTeams(\n agentId: string,\n db?: Database.Database\n): (HumanAgent & { teams: Array<{ team_id: string; team_name: string; relationship: string }> }) | undefined {\n const conn = db ?? getDb();\n const agent = getAgent(agentId, conn);\n if (!agent) return undefined;\n\n const teams = conn\n .prepare(\n `SELECT t.team_id, t.name AS team_name, tm.relationship\n FROM team_members tm\n JOIN teams t ON tm.team_id = t.team_id\n WHERE tm.agent_id = ?\n ORDER BY t.created_at`\n )\n .all(agentId) as Array<{ team_id: string; team_name: string; relationship: string }>;\n\n return { ...agent, teams };\n}\n\nexport function listAgentsWithMetrics(\n db?: Database.Database\n): AgentWithMetrics[] {\n const conn = db ?? getDb();\n const rows = conn\n .prepare(\n `SELECT\n a.*,\n COUNT(CASE WHEN t.status IN ('DISPATCHED', 'PENDING') THEN 1 END) AS active_task_count,\n AVG(\n CASE WHEN t.status = 'RESOLVED'\n THEN (julianday(t.updated_at) - julianday(t.created_at)) * 24\n END\n ) AS avg_delivery_hours\n FROM agents a\n LEFT JOIN tasks t ON a.agent_id = t.assignee_id\n GROUP BY a.agent_id\n ORDER BY a.created_at`\n )\n .all() as (AgentRow & { active_task_count: number; avg_delivery_hours: number | null })[];\n\n return rows.map(row => ({\n ...rowToAgent(row),\n active_task_count: row.active_task_count,\n avg_delivery_hours: row.avg_delivery_hours\n ? Math.round(row.avg_delivery_hours * 100) / 100\n : null,\n }));\n}\n","import { nanoid } from 'nanoid';\n\nexport function generateTraceId(): string {\n const num = Math.floor(Math.random() * 10000)\n .toString()\n .padStart(4, '0');\n return `TK-${num}`;\n}\n\nexport function generateId(prefix: string): string {\n return `${prefix}_${nanoid(8)}`;\n}\n","import { Router } from 'express';\nimport { dispatchJob } from '../services/dispatch.js';\nimport { planJob } from '../services/planner.js';\nimport { listActiveJobs, getJobWithTasks } from '../models/job.js';\nimport { markOverdueTasks } from '../models/task.js';\nimport type { CreateJobRequest, PlanRequest } from '../models/types.js';\n\nconst router = Router();\n\n// POST /api/v1/jobs/create - Create and dispatch a new job\nrouter.post('/create', (req, res) => {\n const body = req.body as CreateJobRequest;\n\n if (!body.original_prompt || !Array.isArray(body.tasks) || body.tasks.length === 0) {\n res.status(400).json({\n error: 'original_prompt and non-empty tasks[] are required',\n });\n return;\n }\n\n for (const task of body.tasks) {\n if (!task.assignee_id || !task.todo_description || !task.deadline) {\n res.status(400).json({\n error: 'Each task requires assignee_id, todo_description, and deadline',\n });\n return;\n }\n }\n\n try {\n const job = dispatchJob(body);\n res.status(201).json(job);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n res.status(400).json({ error: message });\n }\n});\n\n// GET /api/v1/jobs/active - List active jobs for kanban\nrouter.get('/active', (_req, res) => {\n // Mark overdue tasks before returning\n markOverdueTasks();\n const jobs = listActiveJobs();\n res.json({ jobs });\n});\n\n// POST /api/v1/jobs/plan - AI-powered task planning (does NOT dispatch)\nrouter.post('/plan', async (req, res) => {\n const body = req.body as PlanRequest;\n\n if (!body.prompt || typeof body.prompt !== 'string') {\n res.status(400).json({ error: 'prompt is required' });\n return;\n }\n\n try {\n const plan = await planJob(body);\n res.json(plan);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n const status = message.includes('API Key') || message.includes('API key') ? 503 : 400;\n res.status(status).json({ error: message });\n }\n});\n\n// GET /api/v1/jobs/:job_id - Get a single job with tasks\nrouter.get('/:job_id', (req, res) => {\n const { job_id } = req.params;\n const job = getJobWithTasks(job_id);\n if (!job) {\n res.status(404).json({ error: `Job not found: ${job_id}` });\n return;\n }\n res.json(job);\n});\n\nexport default router;\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport type { HumanTask, TaskRow, TaskStatus } from './types.js';\n\nfunction rowToTask(row: TaskRow): HumanTask {\n return {\n ...row,\n payload: JSON.parse(row.payload) as Record<string, unknown>,\n result_data: row.result_data ? JSON.parse(row.result_data) : null,\n };\n}\n\nexport function createTask(\n task: Omit<HumanTask, 'created_at' | 'updated_at' | 'result_data'>,\n db?: Database.Database\n): HumanTask {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n conn\n .prepare(\n `INSERT INTO tasks (trace_id, job_id, assignee_id, todo_description, deadline, payload, status, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n task.trace_id,\n task.job_id,\n task.assignee_id,\n task.todo_description,\n task.deadline,\n JSON.stringify(task.payload),\n task.status,\n now,\n now\n );\n return { ...task, result_data: null, created_at: now, updated_at: now };\n}\n\nexport function getTask(\n traceId: string,\n db?: Database.Database\n): HumanTask | undefined {\n const conn = db ?? getDb();\n const row = conn\n .prepare('SELECT * FROM tasks WHERE trace_id = ?')\n .get(traceId) as TaskRow | undefined;\n return row ? rowToTask(row) : undefined;\n}\n\nexport function listTasksByJob(\n jobId: string,\n db?: Database.Database\n): HumanTask[] {\n const conn = db ?? getDb();\n const rows = conn\n .prepare('SELECT * FROM tasks WHERE job_id = ? ORDER BY created_at')\n .all(jobId) as TaskRow[];\n return rows.map(rowToTask);\n}\n\nexport function listTasksByAssignee(\n assigneeId: string,\n db?: Database.Database\n): HumanTask[] {\n const conn = db ?? getDb();\n const rows = conn\n .prepare('SELECT * FROM tasks WHERE assignee_id = ? ORDER BY created_at')\n .all(assigneeId) as TaskRow[];\n return rows.map(rowToTask);\n}\n\nexport function updateTaskStatus(\n traceId: string,\n status: TaskStatus,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n const result = conn\n .prepare('UPDATE tasks SET status = ?, updated_at = ? WHERE trace_id = ?')\n .run(status, now, traceId);\n return result.changes > 0;\n}\n\nexport function resolveTask(\n traceId: string,\n resultData: unknown,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n const result = conn\n .prepare(\n `UPDATE tasks\n SET status = 'RESOLVED', result_data = ?, updated_at = ?\n WHERE trace_id = ? AND status IN ('DISPATCHED', 'OVERDUE')`\n )\n .run(JSON.stringify(resultData), now, traceId);\n return result.changes > 0;\n}\n\nexport function markOverdueTasks(db?: Database.Database): number {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n const result = conn\n .prepare(\n `UPDATE tasks\n SET status = 'OVERDUE', updated_at = ?\n WHERE status = 'DISPATCHED' AND deadline < ?`\n )\n .run(now, now);\n return result.changes;\n}\n\nexport function resetTaskDeadline(\n traceId: string,\n newDeadline: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n const result = conn\n .prepare(\n `UPDATE tasks\n SET status = 'DISPATCHED', deadline = ?, result_data = NULL, updated_at = ?\n WHERE trace_id = ?`\n )\n .run(newDeadline, now, traceId);\n return result.changes > 0;\n}\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport type { OrchestrationJob, JobRow, JobWithTasks } from './types.js';\nimport { listTasksByJob } from './task.js';\n\nexport function createJob(\n job: OrchestrationJob,\n db?: Database.Database\n): OrchestrationJob {\n const conn = db ?? getDb();\n conn\n .prepare(\n `INSERT INTO jobs (job_id, original_prompt, created_at)\n VALUES (?, ?, ?)`\n )\n .run(job.job_id, job.original_prompt, job.created_at);\n return job;\n}\n\nexport function getJob(\n jobId: string,\n db?: Database.Database\n): OrchestrationJob | undefined {\n const conn = db ?? getDb();\n return conn\n .prepare('SELECT * FROM jobs WHERE job_id = ?')\n .get(jobId) as JobRow | undefined;\n}\n\nexport function getJobWithTasks(\n jobId: string,\n db?: Database.Database\n): JobWithTasks | undefined {\n const conn = db ?? getDb();\n const job = conn\n .prepare('SELECT * FROM jobs WHERE job_id = ?')\n .get(jobId) as JobRow | undefined;\n if (!job) return undefined;\n\n const tasks = listTasksByJob(jobId, conn);\n return { ...job, tasks };\n}\n\nexport function listActiveJobs(db?: Database.Database): JobWithTasks[] {\n const conn = db ?? getDb();\n const jobs = conn\n .prepare(\n `SELECT DISTINCT j.*\n FROM jobs j\n INNER JOIN tasks t ON j.job_id = t.job_id\n WHERE t.status != 'RESOLVED'\n ORDER BY j.created_at DESC`\n )\n .all() as JobRow[];\n\n // Also include jobs where all tasks are resolved but not yet synced\n const allJobs = conn\n .prepare('SELECT * FROM jobs ORDER BY created_at DESC')\n .all() as JobRow[];\n\n const activeJobIds = new Set(jobs.map(j => j.job_id));\n const result: JobWithTasks[] = [];\n\n for (const job of allJobs) {\n const tasks = listTasksByJob(job.job_id, conn);\n if (tasks.length > 0) {\n result.push({ ...job, tasks });\n }\n }\n\n return result;\n}\n\nexport function isJobComplete(\n jobId: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const row = conn\n .prepare(\n `SELECT COUNT(*) as total,\n SUM(CASE WHEN status = 'RESOLVED' THEN 1 ELSE 0 END) as resolved\n FROM tasks WHERE job_id = ?`\n )\n .get(jobId) as { total: number; resolved: number };\n return row.total > 0 && row.total === row.resolved;\n}\n\nexport function deleteJob(\n jobId: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const result = conn\n .prepare('DELETE FROM jobs WHERE job_id = ?')\n .run(jobId);\n return result.changes > 0;\n}\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { createJob } from '../models/job.js';\nimport { createTask } from '../models/task.js';\nimport { getAgent, updateAgentStatus } from '../models/agent.js';\nimport { generateTraceId, generateId } from '../utils/trace-id.js';\nimport type { CreateJobRequest, JobWithTasks } from '../models/types.js';\n\nexport function dispatchJob(\n request: CreateJobRequest,\n db?: Database.Database\n): JobWithTasks {\n const conn = db ?? getDb();\n const jobId = generateId('job');\n const now = new Date().toISOString();\n\n // Validate all assignees exist\n for (const taskReq of request.tasks) {\n const agent = getAgent(taskReq.assignee_id, conn);\n if (!agent) {\n throw new Error(`Agent not found: ${taskReq.assignee_id}`);\n }\n if (agent.status === 'OFFLINE') {\n throw new Error(`Agent is offline: ${taskReq.assignee_id} (${agent.name})`);\n }\n }\n\n // Create the job\n const job = createJob(\n {\n job_id: jobId,\n original_prompt: request.original_prompt,\n created_at: now,\n },\n conn\n );\n\n // Create and dispatch all tasks\n const tasks = request.tasks.map(taskReq => {\n const traceId = generateTraceId();\n const task = createTask(\n {\n trace_id: traceId,\n job_id: jobId,\n assignee_id: taskReq.assignee_id,\n todo_description: taskReq.todo_description,\n deadline: taskReq.deadline,\n payload: taskReq.payload ?? {},\n status: 'DISPATCHED',\n },\n conn\n );\n\n // Mark the agent as busy\n updateAgentStatus(taskReq.assignee_id, 'BUSY', conn);\n\n return task;\n });\n\n return { ...job, tasks };\n}\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport type {\n Team,\n TeamRow,\n TeamMember,\n TeamMemberRow,\n TeamWithMembers,\n AgentStatus,\n} from './types.js';\n\nexport function createTeam(\n team: Omit<Team, 'created_at'>,\n db?: Database.Database\n): Team {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n conn\n .prepare(\n `INSERT INTO teams (team_id, name, description, created_at)\n VALUES (?, ?, ?, ?)`\n )\n .run(team.team_id, team.name, team.description || '', now);\n return { ...team, description: team.description || '', created_at: now };\n}\n\nexport function getTeam(\n teamId: string,\n db?: Database.Database\n): Team | undefined {\n const conn = db ?? getDb();\n const row = conn\n .prepare('SELECT * FROM teams WHERE team_id = ?')\n .get(teamId) as TeamRow | undefined;\n return row || undefined;\n}\n\nexport function listTeams(db?: Database.Database): Team[] {\n const conn = db ?? getDb();\n return conn\n .prepare('SELECT * FROM teams ORDER BY created_at')\n .all() as TeamRow[];\n}\n\nexport function deleteTeam(\n teamId: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const result = conn\n .prepare('DELETE FROM teams WHERE team_id = ?')\n .run(teamId);\n return result.changes > 0;\n}\n\nexport function addTeamMember(\n teamId: string,\n agentId: string,\n relationship: string,\n db?: Database.Database\n): void {\n const conn = db ?? getDb();\n conn\n .prepare(\n `INSERT OR REPLACE INTO team_members (team_id, agent_id, relationship)\n VALUES (?, ?, ?)`\n )\n .run(teamId, agentId, relationship || '');\n}\n\nexport function removeTeamMember(\n teamId: string,\n agentId: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const result = conn\n .prepare('DELETE FROM team_members WHERE team_id = ? AND agent_id = ?')\n .run(teamId, agentId);\n return result.changes > 0;\n}\n\nexport function updateTeamMemberRelationship(\n teamId: string,\n agentId: string,\n relationship: string,\n db?: Database.Database\n): boolean {\n const conn = db ?? getDb();\n const result = conn\n .prepare(\n 'UPDATE team_members SET relationship = ? WHERE team_id = ? AND agent_id = ?'\n )\n .run(relationship, teamId, agentId);\n return result.changes > 0;\n}\n\nexport function getTeamWithMembers(\n teamId: string,\n db?: Database.Database\n): TeamWithMembers | undefined {\n const conn = db ?? getDb();\n const team = getTeam(teamId, conn);\n if (!team) return undefined;\n\n const members = conn\n .prepare(\n `SELECT tm.team_id, tm.agent_id, tm.relationship, a.name AS agent_name, a.status AS agent_status\n FROM team_members tm\n JOIN agents a ON tm.agent_id = a.agent_id\n WHERE tm.team_id = ?\n ORDER BY a.created_at`\n )\n .all(teamId) as (TeamMemberRow & { agent_name: string; agent_status: AgentStatus })[];\n\n return { ...team, members };\n}\n\nexport function listTeamsByAgent(\n agentId: string,\n db?: Database.Database\n): (Team & { relationship: string })[] {\n const conn = db ?? getDb();\n return conn\n .prepare(\n `SELECT t.*, tm.relationship\n FROM teams t\n JOIN team_members tm ON t.team_id = tm.team_id\n WHERE tm.agent_id = ?\n ORDER BY t.created_at`\n )\n .all(agentId) as (TeamRow & { relationship: string })[];\n}\n\nexport function getTeamMemberRelationship(\n teamId: string,\n agentId: string,\n db?: Database.Database\n): string | undefined {\n const conn = db ?? getDb();\n const row = conn\n .prepare(\n 'SELECT relationship FROM team_members WHERE team_id = ? AND agent_id = ?'\n )\n .get(teamId, agentId) as { relationship: string } | undefined;\n return row?.relationship;\n}\n","import type { LlmProvider, LlmCompletionRequest, LlmCompletionResponse } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.anthropic.com';\n\nexport class ClaudeProvider implements LlmProvider {\n private apiKey: string;\n private model: string;\n private baseUrl: string;\n\n constructor(apiKey: string, model?: string, baseUrl?: string) {\n this.apiKey = apiKey;\n this.model = model || 'claude-sonnet-4-20250514';\n this.baseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\\/+$/, '');\n }\n\n async complete(request: LlmCompletionRequest): Promise<LlmCompletionResponse> {\n const systemMessages = request.messages.filter(m => m.role === 'system');\n const nonSystemMessages = request.messages.filter(m => m.role !== 'system');\n\n const body: Record<string, unknown> = {\n model: this.model,\n max_tokens: request.max_tokens || 4096,\n messages: nonSystemMessages.map(m => ({ role: m.role, content: m.content })),\n };\n\n if (request.temperature !== undefined) {\n body.temperature = request.temperature;\n }\n\n if (systemMessages.length > 0) {\n body.system = systemMessages.map(m => m.content).join('\\n\\n');\n }\n\n const response = await fetch(`${this.baseUrl}/v1/messages`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': this.apiKey,\n 'anthropic-version': '2023-06-01',\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Claude API error (${response.status}): ${errorText}`);\n }\n\n const data = await response.json() as { content: Array<{ type: string; text: string }> };\n const textBlock = data.content.find(c => c.type === 'text');\n\n if (!textBlock) {\n throw new Error('Claude API returned no text content');\n }\n\n return { content: textBlock.text };\n }\n}\n","import type { LlmProvider, LlmCompletionRequest, LlmCompletionResponse } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.openai.com';\n\nexport class OpenAIProvider implements LlmProvider {\n private apiKey: string;\n private model: string;\n private baseUrl: string;\n\n constructor(apiKey: string, model?: string, baseUrl?: string) {\n this.apiKey = apiKey;\n this.model = model || 'gpt-4o';\n this.baseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\\/+$/, '');\n }\n\n async complete(request: LlmCompletionRequest): Promise<LlmCompletionResponse> {\n const body: Record<string, unknown> = {\n model: this.model,\n max_tokens: request.max_tokens || 4096,\n messages: request.messages.map(m => ({ role: m.role, content: m.content })),\n };\n\n if (request.temperature !== undefined) {\n body.temperature = request.temperature;\n }\n\n const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`OpenAI API error (${response.status}): ${errorText}`);\n }\n\n const data = await response.json() as {\n choices: Array<{ message: { content: string } }>;\n };\n\n const content = data.choices[0]?.message?.content;\n if (!content) {\n throw new Error('OpenAI API returned no content');\n }\n\n return { content };\n }\n}\n","import type { LlmProvider, LlmCompletionRequest, LlmCompletionResponse } from './types.js';\n\nconst DEFAULT_BASE_URL = 'https://api.openai.com';\n\nexport class ResponsesProvider implements LlmProvider {\n private apiKey: string;\n private model: string;\n private baseUrl: string;\n\n constructor(apiKey: string, model?: string, baseUrl?: string) {\n this.apiKey = apiKey;\n this.model = model || 'gpt-4o';\n this.baseUrl = (baseUrl || DEFAULT_BASE_URL).replace(/\\/+$/, '');\n }\n\n async complete(request: LlmCompletionRequest): Promise<LlmCompletionResponse> {\n const systemMessages = request.messages.filter(m => m.role === 'system');\n const nonSystemMessages = request.messages.filter(m => m.role !== 'system');\n\n const input = nonSystemMessages.map(m => ({\n role: m.role,\n content: m.content,\n }));\n\n const body: Record<string, unknown> = {\n model: this.model,\n input,\n };\n\n if (systemMessages.length > 0) {\n body.instructions = systemMessages.map(m => m.content).join('\\n\\n');\n }\n\n if (request.temperature !== undefined) {\n body.temperature = request.temperature;\n }\n\n if (request.max_tokens) {\n body.max_output_tokens = request.max_tokens;\n }\n\n const response = await fetch(`${this.baseUrl}/v1/responses`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'Authorization': `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Responses API error (${response.status}): ${errorText}`);\n }\n\n const data = await response.json() as {\n output: Array<{\n type: string;\n content?: Array<{ type: string; text: string }>;\n }>;\n };\n\n // Find the message output with output_text content\n for (const item of data.output) {\n if (item.type === 'message' && item.content) {\n const textBlock = item.content.find(c => c.type === 'output_text');\n if (textBlock) {\n return { content: textBlock.text };\n }\n }\n }\n\n throw new Error('Responses API returned no text content');\n }\n}\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\n\nexport function getConfig(key: string, db?: Database.Database): string | undefined {\n const conn = db ?? getDb();\n const row = conn.prepare('SELECT value FROM config WHERE key = ?').get(key) as { value: string } | undefined;\n return row?.value;\n}\n\nexport function setConfig(key: string, value: string, db?: Database.Database): void {\n const conn = db ?? getDb();\n conn.prepare('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)').run(key, value);\n}\n\nexport function deleteConfig(key: string, db?: Database.Database): void {\n const conn = db ?? getDb();\n conn.prepare('DELETE FROM config WHERE key = ?').run(key);\n}\n\nexport function getAllConfig(db?: Database.Database): Record<string, string> {\n const conn = db ?? getDb();\n const rows = conn.prepare('SELECT key, value FROM config').all() as Array<{ key: string; value: string }>;\n const result: Record<string, string> = {};\n for (const row of rows) {\n result[row.key] = row.value;\n }\n return result;\n}\n","import type { LlmProvider, LlmApiFormat } from './types.js';\nimport { ClaudeProvider } from './claude.js';\nimport { OpenAIProvider } from './openai.js';\nimport { ResponsesProvider } from './responses.js';\nimport { getConfig } from '../models/config.js';\n\nexport interface LlmConfig {\n provider: LlmApiFormat;\n apiKey: string;\n model?: string;\n baseUrl?: string;\n}\n\nfunction normalizeProvider(raw: string): LlmApiFormat {\n // Backward compat: 'claude' -> 'anthropic'\n if (raw === 'claude') return 'anthropic';\n return raw as LlmApiFormat;\n}\n\nexport function getLlmConfig(): LlmConfig {\n // DB config takes priority over env vars\n const dbProvider = getConfig('llm_provider');\n const dbApiKey = getConfig('llm_api_key');\n const dbModel = getConfig('llm_model');\n const dbBaseUrl = getConfig('llm_base_url');\n\n const rawProvider = dbProvider || process.env.HUMANCLAW_LLM_PROVIDER || 'anthropic';\n const provider = normalizeProvider(rawProvider);\n const apiKey = dbApiKey || process.env.HUMANCLAW_LLM_API_KEY || '';\n const model = dbModel || process.env.HUMANCLAW_LLM_MODEL;\n const baseUrl = dbBaseUrl || process.env.HUMANCLAW_LLM_BASE_URL;\n\n if (!apiKey) {\n throw new Error(\n '未配置 LLM API Key。请在 Dashboard 设置中配置,或设置环境变量 HUMANCLAW_LLM_API_KEY。'\n );\n }\n\n const validFormats: LlmApiFormat[] = ['openai', 'anthropic', 'responses'];\n if (!validFormats.includes(provider)) {\n throw new Error(`不支持的 API 格式: ${provider}。支持: openai, anthropic, responses`);\n }\n\n return { provider, apiKey, model: model || undefined, baseUrl: baseUrl || undefined };\n}\n\nexport function createLlmProvider(config?: LlmConfig): LlmProvider {\n const cfg = config || getLlmConfig();\n\n switch (cfg.provider) {\n case 'anthropic':\n return new ClaudeProvider(cfg.apiKey, cfg.model, cfg.baseUrl);\n case 'openai':\n return new OpenAIProvider(cfg.apiKey, cfg.model, cfg.baseUrl);\n case 'responses':\n return new ResponsesProvider(cfg.apiKey, cfg.model, cfg.baseUrl);\n }\n}\n\nexport type { LlmProvider, LlmApiFormat, LlmProviderName } from './types.js';\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { listAgentsWithMetrics, getAgent } from '../models/agent.js';\nimport { getTeamWithMembers } from '../models/team.js';\nimport { createLlmProvider } from '../llm/index.js';\nimport type { PlanRequest, PlanResponse, PlannedTask, AgentWithMetrics } from '../models/types.js';\nimport type { LlmProvider } from '../llm/types.js';\n\nfunction buildSystemPrompt(): string {\n return `你是 HumanClaw 任务编排规划器。你的工作是将用户的需求拆解为可以分发给碳基节点(真实人类)执行的独立子任务。\n\n规则:\n1. 将需求拆解为扁平的、无依赖的子任务列表(每个任务可以独立执行)\n2. 根据每个 Agent 的技能(capabilities)和当前负载来匹配分配\n3. 为每个任务生成一段「话术」—— 这是直接发给该人类执行者的任务说明,语气自然、清晰、专业,包含具体的交付物要求\n4. 根据任务复杂度设置合理的截止时间(相对于当前时间)\n5. 任务分配应以最佳匹配为原则,同一个 Agent 可以承担多个任务\n\n你必须严格输出以下 JSON 格式(不要输出任何其他内容):\n\n\\`\\`\\`json\n[\n {\n \"assignee_id\": \"agent的ID\",\n \"assignee_name\": \"agent的名字\",\n \"todo_description\": \"简短的任务标题/描述\",\n \"briefing\": \"话术:详细的任务说明,包括目标、交付物要求、注意事项。语气像一个靠谱的项目经理在给组员分配任务。\",\n \"deadline\": \"ISO 8601 时间\"\n }\n]\n\\`\\`\\``;\n}\n\nfunction buildUserPrompt(\n prompt: string,\n agents: AgentWithMetrics[],\n teamContext?: { name: string; description: string; relationships: Map<string, string> }\n): string {\n const now = new Date();\n const agentList = agents.map(a => {\n const load = a.active_task_count > 0\n ? `当前有 ${a.active_task_count} 个进行中的任务`\n : '当前空闲';\n const speed = a.avg_delivery_hours !== null\n ? `平均交付时间 ${a.avg_delivery_hours}h`\n : '暂无历史数据';\n // Use team-contextual relationship if available, fallback to general\n const relText = teamContext?.relationships.get(a.agent_id) || a.relationship;\n const rel = relText ? ` 关系: ${relText}` : '';\n return `- ${a.name} (ID: ${a.agent_id}) 技能: [${a.capabilities.join(', ')}]${rel} ${load} ${speed}`;\n }).join('\\n');\n\n let teamInfo = '';\n if (teamContext) {\n teamInfo = `\\n团队: ${teamContext.name}${teamContext.description ? ` — ${teamContext.description}` : ''}\\n`;\n }\n\n return `当前时间: ${now.toISOString()}\n${teamInfo}\n可用的碳基节点:\n${agentList}\n\n需求:\n${prompt}\n\n请根据以上信息拆解任务并分配。输出 JSON 数组。`;\n}\n\nfunction extractJson(raw: string): string {\n // Try to extract JSON from markdown code block\n const codeBlockMatch = raw.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/);\n if (codeBlockMatch) {\n return codeBlockMatch[1].trim();\n }\n // Try to find a JSON array directly\n const arrayMatch = raw.match(/\\[[\\s\\S]*\\]/);\n if (arrayMatch) {\n return arrayMatch[0];\n }\n return raw.trim();\n}\n\nfunction validatePlannedTasks(\n parsed: unknown,\n validAgentIds: Set<string>,\n agents: AgentWithMetrics[]\n): PlannedTask[] {\n if (!Array.isArray(parsed)) {\n throw new Error('LLM 返回的不是有效的任务数组');\n }\n\n if (parsed.length === 0) {\n throw new Error('LLM 未生成任何任务');\n }\n\n return parsed.map((item: Record<string, unknown>, i: number) => {\n if (!item.todo_description || typeof item.todo_description !== 'string') {\n throw new Error(`任务 ${i + 1} 缺少 todo_description`);\n }\n if (!item.briefing || typeof item.briefing !== 'string') {\n throw new Error(`任务 ${i + 1} 缺少 briefing (话术)`);\n }\n\n // Validate or fallback assignee_id\n let assigneeId = String(item.assignee_id || '');\n let assigneeName = String(item.assignee_name || '');\n\n if (!validAgentIds.has(assigneeId)) {\n // Fallback to first available agent\n const fallback = agents[0];\n if (fallback) {\n assigneeId = fallback.agent_id;\n assigneeName = fallback.name;\n }\n }\n\n // Validate deadline or generate a default\n let deadline = String(item.deadline || '');\n if (!deadline || isNaN(Date.parse(deadline))) {\n // Default: 24 hours from now\n deadline = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();\n }\n\n return {\n assignee_id: assigneeId,\n assignee_name: assigneeName,\n todo_description: String(item.todo_description),\n briefing: String(item.briefing),\n deadline,\n };\n });\n}\n\nexport async function planJob(\n request: PlanRequest,\n provider?: LlmProvider,\n db?: Database.Database\n): Promise<PlanResponse> {\n const conn = db ?? getDb();\n\n // Get available agents\n const allAgents = listAgentsWithMetrics(conn);\n let agents: AgentWithMetrics[];\n let teamContext: { name: string; description: string; relationships: Map<string, string> } | undefined;\n\n // If team_id specified, use team members as agent pool\n if (request.team_id) {\n const team = getTeamWithMembers(request.team_id, conn);\n if (!team) {\n throw new Error(`团队不存在: ${request.team_id}`);\n }\n if (team.members.length === 0) {\n throw new Error(`团队 \"${team.name}\" 中没有成员`);\n }\n const memberIds = new Set(team.members.map(m => m.agent_id));\n agents = allAgents.filter(a => memberIds.has(a.agent_id));\n\n // Build team relationship map\n const relationships = new Map<string, string>();\n for (const m of team.members) {\n if (m.relationship) {\n relationships.set(m.agent_id, m.relationship);\n }\n }\n teamContext = { name: team.name, description: team.description, relationships };\n } else if (request.agent_ids && request.agent_ids.length > 0) {\n agents = allAgents.filter(a => request.agent_ids!.includes(a.agent_id));\n if (agents.length === 0) {\n throw new Error('指定的 Agent 均不存在');\n }\n } else {\n // Default: only IDLE agents\n agents = allAgents.filter(a => a.status === 'IDLE');\n if (agents.length === 0) {\n // Fallback: include BUSY agents too (but not OFFLINE/OOM)\n agents = allAgents.filter(a => a.status !== 'OFFLINE' && a.status !== 'OOM');\n }\n if (agents.length === 0) {\n throw new Error('没有可用的碳基节点。请先在碳基算力池中添加节点。');\n }\n }\n\n // Get LLM provider\n const llm = provider ?? createLlmProvider();\n\n const systemPrompt = buildSystemPrompt();\n const userPrompt = buildUserPrompt(request.prompt, agents, teamContext);\n\n const response = await llm.complete({\n messages: [\n { role: 'system', content: systemPrompt },\n { role: 'user', content: userPrompt },\n ],\n temperature: 0.3,\n max_tokens: 4096,\n });\n\n // Parse LLM response\n const jsonStr = extractJson(response.content);\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonStr);\n } catch {\n throw new Error('AI 返回的内容无法解析为 JSON,请重试');\n }\n\n const validIds = new Set(agents.map(a => a.agent_id));\n const plannedTasks = validatePlannedTasks(parsed, validIds, agents);\n\n return {\n original_prompt: request.prompt,\n planned_tasks: plannedTasks,\n };\n}\n","import { Router } from 'express';\nimport { resumeTask, rejectTask } from '../services/resume.js';\nimport { simulateDelivery } from '../services/simulator.js';\n\nconst router = Router();\n\n// POST /api/v1/tasks/resume - Submit result for a task\nrouter.post('/resume', (req, res) => {\n const { trace_id, result_data } = req.body;\n\n if (!trace_id) {\n res.status(400).json({ error: 'trace_id is required' });\n return;\n }\n\n if (result_data === undefined) {\n res.status(400).json({ error: 'result_data is required' });\n return;\n }\n\n try {\n const result = resumeTask(trace_id, result_data);\n res.json({\n task: result.task,\n job_complete: result.jobComplete,\n job: result.job ?? null,\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n res.status(400).json({ error: message });\n }\n});\n\n// POST /api/v1/tasks/reject - Reject and retry a task\nrouter.post('/reject', (req, res) => {\n const { trace_id, new_deadline } = req.body;\n\n if (!trace_id) {\n res.status(400).json({ error: 'trace_id is required' });\n return;\n }\n\n try {\n const task = rejectTask(trace_id, new_deadline);\n res.json({ task });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n res.status(400).json({ error: message });\n }\n});\n\n// POST /api/v1/tasks/simulate - AI simulate delivery from agent's perspective\nrouter.post('/simulate', async (req, res) => {\n const { trace_id, team_id } = req.body;\n\n if (!trace_id) {\n res.status(400).json({ error: 'trace_id is required' });\n return;\n }\n\n try {\n const result = await simulateDelivery(trace_id, undefined, undefined, team_id);\n res.json(result);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n const status = message.includes('API Key') || message.includes('API key') ? 503 : 400;\n res.status(status).json({ error: message });\n }\n});\n\nexport default router;\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { getTask, resolveTask, resetTaskDeadline } from '../models/task.js';\nimport { isJobComplete, getJobWithTasks } from '../models/job.js';\nimport { updateAgentStatus } from '../models/agent.js';\nimport { listTasksByAssignee } from '../models/task.js';\nimport type { HumanTask, JobWithTasks } from '../models/types.js';\n\nexport interface ResumeResult {\n task: HumanTask;\n jobComplete: boolean;\n job: JobWithTasks | undefined;\n}\n\nexport function resumeTask(\n traceId: string,\n resultData: unknown,\n db?: Database.Database\n): ResumeResult {\n const conn = db ?? getDb();\n\n // Validate trace_id exists\n const task = getTask(traceId, conn);\n if (!task) {\n throw new Error(`Task not found: ${traceId}`);\n }\n\n if (task.status === 'RESOLVED') {\n throw new Error(`Task already resolved: ${traceId}`);\n }\n\n if (task.status === 'PENDING') {\n throw new Error(`Task not yet dispatched: ${traceId}`);\n }\n\n // Resolve the task\n const updated = resolveTask(traceId, resultData, conn);\n if (!updated) {\n throw new Error(`Failed to resolve task: ${traceId}`);\n }\n\n // Check if the agent has other active tasks; if not, mark IDLE\n const activeTasks = listTasksByAssignee(task.assignee_id, conn).filter(\n t => t.status === 'DISPATCHED' || t.status === 'PENDING'\n );\n if (activeTasks.length === 0) {\n updateAgentStatus(task.assignee_id, 'IDLE', conn);\n }\n\n // Check if the whole job is now complete\n const jobComplete = isJobComplete(task.job_id, conn);\n const job = jobComplete ? getJobWithTasks(task.job_id, conn) : undefined;\n\n const resolvedTask = getTask(traceId, conn)!;\n\n return { task: resolvedTask, jobComplete, job };\n}\n\nexport function rejectTask(\n traceId: string,\n newDeadline?: string,\n db?: Database.Database\n): HumanTask {\n const conn = db ?? getDb();\n\n const task = getTask(traceId, conn);\n if (!task) {\n throw new Error(`Task not found: ${traceId}`);\n }\n\n // Default: extend deadline by 24h from now\n const deadline =\n newDeadline ?? new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();\n\n resetTaskDeadline(traceId, deadline, conn);\n\n return getTask(traceId, conn)!;\n}\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { getAgent } from '../models/agent.js';\nimport { getTask } from '../models/task.js';\nimport { getTeamMemberRelationship } from '../models/team.js';\nimport { createLlmProvider } from '../llm/index.js';\nimport type { LlmProvider } from '../llm/types.js';\n\nexport interface SimulateResult {\n trace_id: string;\n simulated_delivery: string;\n}\n\nfunction buildSimulatePrompt(\n agentName: string,\n relationship: string,\n capabilities: string[],\n taskDescription: string,\n deadline: string\n): string {\n return `你现在扮演一个名叫「${agentName}」的人,你的技能标签是 [${capabilities.join(', ')}]。\n${relationship ? `你和布置任务的人的关系是:${relationship}。` : ''}\n\n你收到了一个任务:\n${taskDescription}\n\n截止时间:${new Date(deadline).toLocaleString('zh-CN')}\n\n请站在「${agentName}」的视角,用这个人物自然的语气和口吻,写一段任务交付汇报。\n要求:\n1. 内容要贴合任务描述,体现专业能力\n2. 语气符合人物身份${relationship ? '和与上级的关系' : ''}\n3. 汇报内容具体、有细节,包含做了什么、遇到了什么问题、最终结果如何\n4. 字数 200-400 字\n5. 直接输出汇报内容,不要加任何格式前缀或说明`;\n}\n\nexport async function simulateDelivery(\n traceId: string,\n provider?: LlmProvider,\n db?: Database.Database,\n teamId?: string\n): Promise<SimulateResult> {\n const conn = db ?? getDb();\n\n const task = getTask(traceId, conn);\n if (!task) {\n throw new Error(`Task not found: ${traceId}`);\n }\n\n const agent = getAgent(task.assignee_id, conn);\n if (!agent) {\n throw new Error(`Agent not found: ${task.assignee_id}`);\n }\n\n // Use team-contextual relationship if available, fallback to general\n let relationship = agent.relationship;\n if (teamId) {\n const teamRel = getTeamMemberRelationship(teamId, agent.agent_id, conn);\n if (teamRel) relationship = teamRel;\n }\n\n const llm = provider ?? createLlmProvider();\n\n const response = await llm.complete({\n messages: [\n {\n role: 'system',\n content: '你是一个角色扮演专家,擅长模拟不同人物的语气和汇报风格。',\n },\n {\n role: 'user',\n content: buildSimulatePrompt(\n agent.name,\n relationship,\n agent.capabilities,\n task.todo_description,\n task.deadline\n ),\n },\n ],\n temperature: 0.7,\n max_tokens: 1024,\n });\n\n return {\n trace_id: traceId,\n simulated_delivery: response.content,\n };\n}\n","import { Router } from 'express';\nimport { reviewJob } from '../services/reviewer.js';\n\nconst router = Router();\n\n// POST /api/v1/jobs/:job_id/review - AI review of all deliverables\nrouter.post('/:job_id/review', async (req, res) => {\n const { job_id } = req.params;\n const { rating_system, task_weights } = req.body as {\n rating_system?: string;\n task_weights?: Record<string, number>;\n };\n\n const validSystems = ['ali', 'letter', 'em'];\n const ratingSystem = rating_system && validSystems.includes(rating_system)\n ? (rating_system as 'ali' | 'letter' | 'em')\n : undefined;\n\n try {\n const result = await reviewJob(job_id, undefined, undefined, ratingSystem, task_weights);\n res.json(result);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n const status = message.includes('API Key') || message.includes('API key') ? 503 : 400;\n res.status(status).json({ error: message });\n }\n});\n\nexport default router;\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport type {\n Evaluation,\n EvaluationRow,\n RatingSystem,\n} from './types.js';\n\nexport const RATING_SYSTEMS: Record<RatingSystem, readonly string[]> = {\n ali: ['3.25', '3.5', '3.5+', '3.75', '4.0'],\n letter: ['S', 'A', 'B', 'C', 'D'],\n em: ['EM+', 'EM', 'MM+', 'MM', 'MM-'],\n} as const;\n\nexport function validateRating(system: RatingSystem, rating: string): boolean {\n const values = RATING_SYSTEMS[system];\n return values ? values.includes(rating) : false;\n}\n\nexport function createEvaluation(\n evaluation: Omit<Evaluation, 'created_at'>,\n db?: Database.Database\n): Evaluation {\n const conn = db ?? getDb();\n const now = new Date().toISOString();\n conn\n .prepare(\n `INSERT INTO evaluations (eval_id, job_id, agent_id, trace_id, rating_system, rating, weight, comment, created_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`\n )\n .run(\n evaluation.eval_id,\n evaluation.job_id,\n evaluation.agent_id,\n evaluation.trace_id,\n evaluation.rating_system,\n evaluation.rating,\n evaluation.weight,\n evaluation.comment || '',\n now\n );\n return { ...evaluation, comment: evaluation.comment || '', created_at: now };\n}\n\nfunction rowToEvaluation(row: EvaluationRow): Evaluation {\n return {\n ...row,\n rating_system: row.rating_system as RatingSystem,\n };\n}\n\nexport function listEvaluationsByJob(\n jobId: string,\n db?: Database.Database\n): Evaluation[] {\n const conn = db ?? getDb();\n const rows = conn\n .prepare(\n `SELECT e.*\n FROM evaluations e\n WHERE e.job_id = ?\n ORDER BY e.created_at`\n )\n .all(jobId) as EvaluationRow[];\n return rows.map(rowToEvaluation);\n}\n\nexport function listEvaluationsByAgent(\n agentId: string,\n db?: Database.Database\n): (Evaluation & { original_prompt?: string; todo_description?: string })[] {\n const conn = db ?? getDb();\n const rows = conn\n .prepare(\n `SELECT e.*, j.original_prompt, t.todo_description\n FROM evaluations e\n LEFT JOIN jobs j ON e.job_id = j.job_id\n LEFT JOIN tasks t ON e.trace_id = t.trace_id\n WHERE e.agent_id = ?\n ORDER BY e.created_at DESC`\n )\n .all(agentId) as (EvaluationRow & { original_prompt?: string; todo_description?: string })[];\n return rows.map(r => ({ ...rowToEvaluation(r), original_prompt: r.original_prompt, todo_description: r.todo_description }));\n}\n\nexport function getAgentEvaluationSummary(\n agentId: string,\n db?: Database.Database\n): { total_evaluations: number; by_system: Record<string, { count: number; ratings: string[] }> } {\n const conn = db ?? getDb();\n const rows = conn\n .prepare(\n `SELECT rating_system, rating, COUNT(*) as cnt\n FROM evaluations\n WHERE agent_id = ?\n GROUP BY rating_system, rating\n ORDER BY rating_system, rating`\n )\n .all(agentId) as { rating_system: string; rating: string; cnt: number }[];\n\n const by_system: Record<string, { count: number; ratings: string[] }> = {};\n let total = 0;\n\n for (const row of rows) {\n if (!by_system[row.rating_system]) {\n by_system[row.rating_system] = { count: 0, ratings: [] };\n }\n by_system[row.rating_system].count += row.cnt;\n by_system[row.rating_system].ratings.push(`${row.rating}(${row.cnt})`);\n total += row.cnt;\n }\n\n return { total_evaluations: total, by_system };\n}\n\nexport function deleteEvaluationsByJob(\n jobId: string,\n db?: Database.Database\n): number {\n const conn = db ?? getDb();\n const result = conn\n .prepare('DELETE FROM evaluations WHERE job_id = ?')\n .run(jobId);\n return result.changes;\n}\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { getJobWithTasks, isJobComplete } from '../models/job.js';\nimport { getAgent } from '../models/agent.js';\nimport { createLlmProvider } from '../llm/index.js';\nimport type { LlmProvider } from '../llm/types.js';\nimport type { RatingSystem } from '../models/types.js';\nimport { RATING_SYSTEMS } from '../models/evaluation.js';\n\nexport interface ReviewResult {\n job_id: string;\n original_prompt: string;\n review: string;\n evaluations?: Array<{\n agent_id: string;\n agent_name: string;\n trace_id: string;\n rating: string;\n comment: string;\n }>;\n reviewed_at: string;\n}\n\nfunction buildReviewSystemPrompt(ratingSystem?: RatingSystem): string {\n let base = `你是 HumanClaw 交付审查员。你的工作是审查碳基节点提交的所有任务交付物,生成整体审查报告。\n\n审查要点:\n1. 检查每个交付物是否满足原始任务要求\n2. 如果交付物包含 GitHub URL(PR、commit、issue 等),分析其内容和质量\n3. 指出潜在问题或改进建议\n4. 给出整体完成度评分(1-10)和总结\n\n输出格式:\n- 使用 Markdown 格式\n- 先给总结评分,再逐个分析每个子任务的交付质量`;\n\n if (ratingSystem) {\n const ratings = RATING_SYSTEMS[ratingSystem];\n const systemLabels: Record<RatingSystem, string> = {\n ali: '阿里绩效体系',\n letter: 'SABCD 等级',\n em: 'EM/MM 绩效体系',\n };\n base += `\n\n同时,请为每个参与的碳基节点生成个人绩效评价。\n使用「${systemLabels[ratingSystem]}」,可选评分为: ${ratings.join(' / ')}\n在审查报告末尾,额外输出一个 JSON 块(用 \\`\\`\\`json 包裹),格式如下:\n\\`\\`\\`json\n[\n {\n \"agent_id\": \"执行者ID\",\n \"trace_id\": \"任务追踪码\",\n \"rating\": \"评分\",\n \"comment\": \"一句话评语\"\n }\n]\n\\`\\`\\``;\n }\n\n return base;\n}\n\nfunction buildReviewUserPrompt(\n originalPrompt: string,\n tasks: Array<{\n trace_id: string;\n assignee_id: string;\n assignee_name: string;\n todo_description: string;\n result_data: unknown;\n weight?: number;\n }>\n): string {\n let prompt = `原始需求: ${originalPrompt}\\n\\n`;\n\n for (let i = 0; i < tasks.length; i++) {\n const t = tasks[i];\n let resultText = '';\n if (t.result_data) {\n if (typeof t.result_data === 'string') {\n resultText = t.result_data;\n } else if (typeof t.result_data === 'object') {\n const rd = t.result_data as Record<string, unknown>;\n resultText = (rd.text as string) || JSON.stringify(rd, null, 2);\n } else {\n resultText = String(t.result_data);\n }\n }\n prompt += `--- 子任务 ${i + 1} ---\\n`;\n prompt += `执行者: ${t.assignee_name} (${t.assignee_id})\\n`;\n prompt += `追踪码: ${t.trace_id}\\n`;\n prompt += `任务: ${t.todo_description}\\n`;\n if (t.weight !== undefined && t.weight !== 1) {\n prompt += `任务权重: ${t.weight}\\n`;\n }\n prompt += `交付物:\\n${resultText}\\n\\n`;\n }\n\n prompt += '请审查以上所有交付物,生成审查报告。';\n return prompt;\n}\n\nfunction extractEvaluationJson(\n raw: string\n): Array<{ agent_id: string; trace_id: string; rating: string; comment: string }> | undefined {\n // Try to extract JSON from the last code block\n const blocks = raw.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/g);\n if (!blocks) return undefined;\n\n // Use the last JSON block (evaluation data is appended at the end)\n const lastBlock = blocks[blocks.length - 1];\n const jsonMatch = lastBlock.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/);\n if (!jsonMatch) return undefined;\n\n try {\n const parsed = JSON.parse(jsonMatch[1].trim());\n if (Array.isArray(parsed)) {\n return parsed;\n }\n } catch {\n // Evaluation extraction is best-effort\n }\n return undefined;\n}\n\nexport async function reviewJob(\n jobId: string,\n provider?: LlmProvider,\n db?: Database.Database,\n ratingSystem?: RatingSystem,\n taskWeights?: Record<string, number>\n): Promise<ReviewResult> {\n const conn = db ?? getDb();\n\n const job = getJobWithTasks(jobId, conn);\n if (!job) {\n throw new Error(`Job not found: ${jobId}`);\n }\n\n if (!isJobComplete(jobId, conn)) {\n const resolved = job.tasks.filter(t => t.status === 'RESOLVED').length;\n throw new Error(\n `Job 尚未全部完成: ${resolved}/${job.tasks.length} 个任务已交付`\n );\n }\n\n const llm = provider ?? createLlmProvider();\n\n const tasksWithNames = job.tasks.map(t => {\n const agent = getAgent(t.assignee_id, conn);\n return {\n trace_id: t.trace_id,\n assignee_id: t.assignee_id,\n assignee_name: agent?.name || t.assignee_id,\n todo_description: t.todo_description,\n result_data: t.result_data,\n weight: taskWeights?.[t.trace_id],\n };\n });\n\n const response = await llm.complete({\n messages: [\n { role: 'system', content: buildReviewSystemPrompt(ratingSystem) },\n {\n role: 'user',\n content: buildReviewUserPrompt(job.original_prompt, tasksWithNames),\n },\n ],\n temperature: 0.3,\n max_tokens: 4096,\n });\n\n const result: ReviewResult = {\n job_id: jobId,\n original_prompt: job.original_prompt,\n review: response.content,\n reviewed_at: new Date().toISOString(),\n };\n\n // Extract evaluations if rating system was requested\n if (ratingSystem) {\n const evalData = extractEvaluationJson(response.content);\n if (evalData) {\n result.evaluations = evalData.map(e => {\n const agent = getAgent(e.agent_id, conn);\n return {\n ...e,\n agent_name: agent?.name || e.agent_id,\n };\n });\n }\n }\n\n return result;\n}\n","import { Router } from 'express';\nimport { getConfig, setConfig, deleteConfig } from '../models/config.js';\n\nconst router = Router();\n\n// GET /api/v1/config - Get LLM config (masks API key)\nrouter.get('/', (_req, res) => {\n const rawProvider = getConfig('llm_provider') || process.env.HUMANCLAW_LLM_PROVIDER || 'anthropic';\n const provider = rawProvider === 'claude' ? 'anthropic' : rawProvider;\n const apiKey = getConfig('llm_api_key') || process.env.HUMANCLAW_LLM_API_KEY || '';\n const model = getConfig('llm_model') || process.env.HUMANCLAW_LLM_MODEL || '';\n const baseUrl = getConfig('llm_base_url') || process.env.HUMANCLAW_LLM_BASE_URL || '';\n\n const keySource = getConfig('llm_api_key') ? 'dashboard' : (process.env.HUMANCLAW_LLM_API_KEY ? 'env' : 'none');\n\n res.json({\n provider,\n api_key_set: apiKey.length > 0,\n api_key_masked: apiKey ? apiKey.slice(0, 8) + '...' + apiKey.slice(-4) : '',\n api_key_source: keySource,\n model,\n base_url: baseUrl,\n });\n});\n\n// PUT /api/v1/config - Update LLM config\nrouter.put('/', (req, res) => {\n const { provider, api_key, model, base_url } = req.body as {\n provider?: string;\n api_key?: string;\n model?: string;\n base_url?: string;\n };\n\n if (provider !== undefined) {\n // Backward compat: 'claude' -> 'anthropic'\n const normalized = provider === 'claude' ? 'anthropic' : provider;\n const validFormats = ['openai', 'anthropic', 'responses'];\n if (!validFormats.includes(normalized)) {\n res.status(400).json({ error: `不支持的 API 格式。支持: ${validFormats.join(', ')}` });\n return;\n }\n setConfig('llm_provider', normalized);\n }\n\n if (api_key !== undefined) {\n if (api_key === '') {\n deleteConfig('llm_api_key');\n } else {\n setConfig('llm_api_key', api_key);\n }\n }\n\n if (model !== undefined) {\n if (model === '') {\n deleteConfig('llm_model');\n } else {\n setConfig('llm_model', model);\n }\n }\n\n if (base_url !== undefined) {\n if (base_url === '') {\n deleteConfig('llm_base_url');\n } else {\n setConfig('llm_base_url', base_url);\n }\n }\n\n res.json({ ok: true });\n});\n\nexport default router;\n","import { Router } from 'express';\nimport {\n createTeam,\n listTeams,\n deleteTeam,\n addTeamMember,\n removeTeamMember,\n updateTeamMemberRelationship,\n getTeamWithMembers,\n} from '../models/team.js';\nimport { getAgent } from '../models/agent.js';\nimport { generateId } from '../utils/trace-id.js';\n\nconst router = Router();\n\n// GET /api/v1/teams - List all teams with members\nrouter.get('/', (_req, res) => {\n const teams = listTeams();\n const teamsWithMembers = teams.map(t => {\n const full = getTeamWithMembers(t.team_id);\n return full || { ...t, members: [] };\n });\n res.json({ teams: teamsWithMembers });\n});\n\n// GET /api/v1/teams/:id - Get team with members\nrouter.get('/:id', (req, res) => {\n const team = getTeamWithMembers(req.params.id);\n if (!team) {\n res.status(404).json({ error: `团队不存在: ${req.params.id}` });\n return;\n }\n res.json(team);\n});\n\n// POST /api/v1/teams - Create team\nrouter.post('/', (req, res) => {\n const { name, description } = req.body;\n\n if (!name || typeof name !== 'string') {\n res.status(400).json({ error: 'name is required' });\n return;\n }\n\n const team = createTeam({\n team_id: generateId('team'),\n name,\n description: description || '',\n });\n\n res.status(201).json(team);\n});\n\n// DELETE /api/v1/teams/:id - Delete team\nrouter.delete('/:id', (req, res) => {\n const deleted = deleteTeam(req.params.id);\n if (!deleted) {\n res.status(404).json({ error: `团队不存在: ${req.params.id}` });\n return;\n }\n res.json({ deleted: req.params.id });\n});\n\n// POST /api/v1/teams/:id/members - Add member to team\nrouter.post('/:id/members', (req, res) => {\n const { agent_id, relationship } = req.body;\n\n if (!agent_id || typeof agent_id !== 'string') {\n res.status(400).json({ error: 'agent_id is required' });\n return;\n }\n\n const agent = getAgent(agent_id);\n if (!agent) {\n res.status(404).json({ error: `Agent 不存在: ${agent_id}` });\n return;\n }\n\n try {\n addTeamMember(req.params.id, agent_id, relationship || '');\n res.status(201).json({ team_id: req.params.id, agent_id, relationship: relationship || '' });\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n res.status(400).json({ error: message });\n }\n});\n\n// DELETE /api/v1/teams/:id/members/:agent_id - Remove member\nrouter.delete('/:id/members/:agent_id', (req, res) => {\n const removed = removeTeamMember(req.params.id, req.params.agent_id);\n if (!removed) {\n res.status(404).json({ error: '成员不在该团队中' });\n return;\n }\n res.json({ removed: req.params.agent_id, team_id: req.params.id });\n});\n\n// PUT /api/v1/teams/:id/members/:agent_id - Update member relationship\nrouter.put('/:id/members/:agent_id', (req, res) => {\n const { relationship } = req.body;\n\n if (relationship === undefined || typeof relationship !== 'string') {\n res.status(400).json({ error: 'relationship is required' });\n return;\n }\n\n const updated = updateTeamMemberRelationship(req.params.id, req.params.agent_id, relationship);\n if (!updated) {\n res.status(404).json({ error: '成员不在该团队中' });\n return;\n }\n res.json({ team_id: req.params.id, agent_id: req.params.agent_id, relationship });\n});\n\nexport default router;\n","import { Router } from 'express';\nimport { generateEvaluations, getPerformanceDashboard } from '../services/evaluator.js';\nimport { listEvaluationsByJob, listEvaluationsByAgent } from '../models/evaluation.js';\nimport type { RatingSystem } from '../models/types.js';\n\nconst router = Router();\n\n// POST /api/v1/evaluations/generate - Generate performance evaluations for a job\nrouter.post('/generate', async (req, res) => {\n const { job_id, rating_system, task_weights } = req.body as {\n job_id?: string;\n rating_system?: string;\n task_weights?: Record<string, number>;\n };\n\n if (!job_id || typeof job_id !== 'string') {\n res.status(400).json({ error: 'job_id is required' });\n return;\n }\n\n const validSystems: RatingSystem[] = ['ali', 'letter', 'em'];\n if (!rating_system || !validSystems.includes(rating_system as RatingSystem)) {\n res.status(400).json({\n error: `rating_system is required. Must be one of: ${validSystems.join(', ')}`,\n });\n return;\n }\n\n try {\n const result = await generateEvaluations({\n job_id,\n rating_system: rating_system as RatingSystem,\n task_weights,\n });\n res.json(result);\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n const status = message.includes('API Key') || message.includes('API key') ? 503 : 400;\n res.status(status).json({ error: message });\n }\n});\n\n// GET /api/v1/evaluations/job/:job_id - Get evaluations for a job\nrouter.get('/job/:job_id', (req, res) => {\n const evaluations = listEvaluationsByJob(req.params.job_id);\n res.json({ evaluations });\n});\n\n// GET /api/v1/evaluations/agent/:agent_id - Get evaluation history for an agent\nrouter.get('/agent/:agent_id', (req, res) => {\n const evaluations = listEvaluationsByAgent(req.params.agent_id);\n res.json({ evaluations });\n});\n\n// GET /api/v1/evaluations/dashboard - Performance dashboard\nrouter.get('/dashboard', (req, res) => {\n const agentId = req.query.agent_id as string | undefined;\n const dashboard = getPerformanceDashboard(agentId);\n res.json(dashboard);\n});\n\nexport default router;\n","import type Database from 'better-sqlite3';\nimport { getDb } from '../db/connection.js';\nimport { getJobWithTasks, isJobComplete } from '../models/job.js';\nimport { getAgent } from '../models/agent.js';\nimport {\n createEvaluation,\n deleteEvaluationsByJob,\n listEvaluationsByJob,\n listEvaluationsByAgent,\n RATING_SYSTEMS,\n validateRating,\n} from '../models/evaluation.js';\nimport { createLlmProvider } from '../llm/index.js';\nimport type { LlmProvider } from '../llm/types.js';\nimport type { EvaluationRequest, EvaluationResult, Evaluation, RatingSystem } from '../models/types.js';\n\nfunction generateEvalId(): string {\n return 'eval_' + Math.random().toString(36).substring(2, 10);\n}\n\nfunction buildEvalPrompt(\n originalPrompt: string,\n ratingSystem: RatingSystem,\n tasks: Array<{\n trace_id: string;\n assignee_id: string;\n assignee_name: string;\n todo_description: string;\n result_data: unknown;\n weight: number;\n }>\n): string {\n const ratings = RATING_SYSTEMS[ratingSystem];\n const systemLabels: Record<RatingSystem, string> = {\n ali: '阿里绩效体系(3.25=不合格, 3.5=基本达标, 3.5+=达标, 3.75=优秀, 4.0=卓越)',\n letter: 'SABCD 等级(S=卓越, A=优秀, B=达标, C=待改进, D=不合格)',\n em: 'EM/MM 体系(EM+=远超预期, EM=超出预期, MM+=达标, MM=基本达标, MM-=不达标)',\n };\n\n let prompt = `原始需求: ${originalPrompt}\\n\\n`;\n prompt += `评分体系: ${systemLabels[ratingSystem]}\\n`;\n prompt += `可选评分: ${ratings.join(' / ')}\\n\\n`;\n\n for (const t of tasks) {\n let resultText = '';\n if (t.result_data) {\n if (typeof t.result_data === 'string') {\n resultText = t.result_data;\n } else if (typeof t.result_data === 'object') {\n const rd = t.result_data as Record<string, unknown>;\n resultText = (rd.text as string) || JSON.stringify(rd, null, 2);\n } else {\n resultText = String(t.result_data);\n }\n }\n prompt += `--- 任务 ${t.trace_id} (权重: ${t.weight}) ---\\n`;\n prompt += `执行者: ${t.assignee_name} (${t.assignee_id})\\n`;\n prompt += `任务: ${t.todo_description}\\n`;\n prompt += `交付物:\\n${resultText}\\n\\n`;\n }\n\n prompt += `请为每个执行者的每个任务单独评分。严格输出 JSON 数组(不要输出其他内容):\n\\`\\`\\`json\n[\n {\n \"agent_id\": \"执行者ID\",\n \"trace_id\": \"任务追踪码\",\n \"rating\": \"从 ${ratings.join('/')} 中选一个\",\n \"comment\": \"一句话评语,说明给此评分的原因\"\n }\n]\n\\`\\`\\``;\n\n return prompt;\n}\n\nfunction extractJson(raw: string): string {\n const codeBlockMatch = raw.match(/```(?:json)?\\s*\\n?([\\s\\S]*?)\\n?\\s*```/);\n if (codeBlockMatch) {\n return codeBlockMatch[1].trim();\n }\n const arrayMatch = raw.match(/\\[[\\s\\S]*\\]/);\n if (arrayMatch) {\n return arrayMatch[0];\n }\n return raw.trim();\n}\n\nexport async function generateEvaluations(\n request: EvaluationRequest,\n provider?: LlmProvider,\n db?: Database.Database\n): Promise<EvaluationResult> {\n const conn = db ?? getDb();\n\n const job = getJobWithTasks(request.job_id, conn);\n if (!job) {\n throw new Error(`Job not found: ${request.job_id}`);\n }\n\n if (!isJobComplete(request.job_id, conn)) {\n const resolved = job.tasks.filter(t => t.status === 'RESOLVED').length;\n throw new Error(\n `Job 尚未全部完成: ${resolved}/${job.tasks.length} 个任务已交付`\n );\n }\n\n // Delete existing evaluations for re-evaluation\n deleteEvaluationsByJob(request.job_id, conn);\n\n const llm = provider ?? createLlmProvider();\n\n const tasksWithInfo = job.tasks.map(t => {\n const agent = getAgent(t.assignee_id, conn);\n return {\n trace_id: t.trace_id,\n assignee_id: t.assignee_id,\n assignee_name: agent?.name || t.assignee_id,\n todo_description: t.todo_description,\n result_data: t.result_data,\n weight: request.task_weights?.[t.trace_id] ?? 1.0,\n };\n });\n\n const response = await llm.complete({\n messages: [\n {\n role: 'system',\n content: '你是一个客观公正的绩效评估专家。根据任务完成质量和交付物内容进行评分。',\n },\n {\n role: 'user',\n content: buildEvalPrompt(job.original_prompt, request.rating_system, tasksWithInfo),\n },\n ],\n temperature: 0.3,\n max_tokens: 4096,\n });\n\n const jsonStr = extractJson(response.content);\n let parsed: unknown;\n try {\n parsed = JSON.parse(jsonStr);\n } catch {\n throw new Error('AI 返回的绩效评价无法解析为 JSON,请重试');\n }\n\n if (!Array.isArray(parsed)) {\n throw new Error('AI 返回的不是有效的评价数组');\n }\n\n const evaluations: Evaluation[] = [];\n for (const item of parsed as Array<Record<string, unknown>>) {\n const agentId = String(item.agent_id || '');\n const traceId = String(item.trace_id || '');\n let rating = String(item.rating || '');\n const comment = String(item.comment || '');\n\n // Validate rating, use middle-tier default if invalid\n if (!validateRating(request.rating_system, rating)) {\n const ratings = RATING_SYSTEMS[request.rating_system];\n rating = ratings[Math.floor(ratings.length / 2)];\n }\n\n const weight = request.task_weights?.[traceId] ?? 1.0;\n\n const evaluation = createEvaluation({\n eval_id: generateEvalId(),\n job_id: request.job_id,\n agent_id: agentId,\n trace_id: traceId,\n rating_system: request.rating_system,\n rating,\n weight,\n comment,\n }, conn);\n\n evaluations.push(evaluation);\n }\n\n return {\n job_id: request.job_id,\n evaluations,\n generated_at: new Date().toISOString(),\n };\n}\n\nexport function getPerformanceDashboard(\n agentId?: string,\n db?: Database.Database\n): { evaluations: ReturnType<typeof listEvaluationsByAgent | typeof listEvaluationsByJob> } {\n const conn = db ?? getDb();\n if (agentId) {\n return { evaluations: listEvaluationsByAgent(agentId, conn) };\n }\n // Return all recent evaluations (limit 50)\n const rows = conn\n .prepare(\n `SELECT e.*, j.original_prompt, t.todo_description, a.name AS agent_name\n FROM evaluations e\n LEFT JOIN jobs j ON e.job_id = j.job_id\n LEFT JOIN tasks t ON e.trace_id = t.trace_id\n LEFT JOIN agents a ON e.agent_id = a.agent_id\n ORDER BY e.created_at DESC\n LIMIT 50`\n )\n .all();\n return { evaluations: rows as Evaluation[] };\n}\n\nexport { RATING_SYSTEMS } from '../models/evaluation.js';\n","export function getDashboardHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n<meta charset=\"UTF-8\"/>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0\"/>\n<title>HumanClaw - 碳基节点编排</title>\n<style>\n:root{--bg:#0f1117;--surface:#1a1d27;--surface-hover:#242836;--border:#2e3346;--text:#e2e4ed;--text-dim:#7b8196;--accent:#00d4ff;--accent-dim:rgba(0,212,255,.12);--green:#22c55e;--yellow:#eab308;--red:#ef4444;--purple:#a855f7;--font-mono:'SF Mono','JetBrains Mono','Fira Code',monospace;--font-sans:-apple-system,'Inter','Segoe UI',sans-serif;--radius:10px}\n*{margin:0;padding:0;box-sizing:border-box}\nbody{background:var(--bg);color:var(--text);font-family:var(--font-sans);min-height:100vh;line-height:1.5}\nheader{padding:20px 32px 12px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:14px}\nheader h1{font-family:var(--font-mono);font-size:22px;color:var(--accent);letter-spacing:1.5px}\n.subtitle{color:var(--text-dim);font-size:12px;border-left:1px solid var(--border);padding-left:14px}\n.hdr-spacer{flex:1}\n.gear-btn{background:none;border:1px solid var(--border);color:var(--text-dim);width:34px;height:34px;border-radius:8px;cursor:pointer;font-size:18px;display:flex;align-items:center;justify-content:center;transition:all .15s}\n.gear-btn:hover{color:var(--accent);border-color:var(--accent);background:var(--accent-dim)}\nnav{display:flex;border-bottom:1px solid var(--border);padding:0 32px;gap:4px}\n.tab{background:none;border:none;color:var(--text-dim);padding:12px 18px;font-size:13px;cursor:pointer;border-bottom:2px solid transparent;transition:all .15s;font-family:var(--font-sans);font-weight:500}\n.tab:hover{color:var(--text);background:var(--surface)}\n.tab.active{color:var(--accent);border-bottom-color:var(--accent)}\nmain{padding:24px 32px;max-width:1200px}\n.panel{display:none}.panel.active{display:block}\n/* Cards */\n.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;transition:border-color .15s}\n.card:hover{border-color:color-mix(in srgb,var(--accent) 40%,transparent)}\n.grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:14px;margin-top:16px}\n/* Agent Card */\n.agent-header{display:flex;align-items:center;gap:10px;margin-bottom:10px}\n.dot{width:10px;height:10px;border-radius:50%;flex-shrink:0}\n.dot.IDLE{background:var(--green);box-shadow:0 0 8px var(--green)}.dot.BUSY{background:var(--yellow);box-shadow:0 0 8px var(--yellow)}.dot.OFFLINE{background:var(--red);box-shadow:0 0 6px var(--red)}.dot.OOM{background:var(--purple);box-shadow:0 0 8px var(--purple)}\n.agent-name{font-weight:600;font-size:15px}\n.agent-id{color:var(--text-dim);font-family:var(--font-mono);font-size:11px;margin-bottom:8px}\n.caps{display:flex;flex-wrap:wrap;gap:5px;margin-top:6px}\n.cap{background:var(--accent-dim);border:1px solid color-mix(in srgb,var(--accent) 25%,transparent);border-radius:4px;padding:1px 8px;font-size:11px;color:var(--accent)}\n.agent-meta{margin-top:10px;font-size:11px;color:var(--text-dim);display:flex;gap:12px}\n.agent-actions{margin-top:12px;display:flex;gap:6px}\n.agent-actions select{background:var(--surface-hover);color:var(--text);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:11px;cursor:pointer;outline:none}\n.agent-actions button{background:var(--red);color:#fff;border:none;border-radius:6px;padding:4px 10px;font-size:11px;cursor:pointer}\n.agent-actions button:hover{opacity:.8}\n/* Stats Bar */\n.stats{display:flex;gap:12px;flex-wrap:wrap}\n.stat{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px 22px;text-align:center;min-width:90px}\n.stat-val{font-size:28px;font-weight:700;font-family:var(--font-mono)}\n.stat-lbl{font-size:10px;color:var(--text-dim);text-transform:uppercase;letter-spacing:1px;margin-top:2px}\n/* Forms */\n.form-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:24px;margin-top:16px;max-width:620px}\n.form-card h3{font-size:15px;color:var(--accent);font-family:var(--font-mono);margin-bottom:16px}\n.fg{margin-bottom:14px}\n.fg label{display:block;font-size:12px;color:var(--text-dim);margin-bottom:5px;font-weight:500}\n.fg input,.fg textarea,.fg select{width:100%;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:10px 12px;color:var(--text);font-size:13px;font-family:var(--font-sans);outline:none;transition:border-color .15s}\n.fg input:focus,.fg textarea:focus,.fg select:focus{border-color:var(--accent)}\n.fg textarea{min-height:80px;resize:vertical;font-family:var(--font-mono);font-size:12px}\n.fg .hint{font-size:11px;color:var(--text-dim);margin-top:4px}\n.btn{padding:10px 22px;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;transition:all .15s;display:inline-flex;align-items:center;gap:6px}\n.btn:hover{opacity:.85;transform:translateY(-1px)}\n.btn:active{transform:translateY(0)}\n.btn:disabled{opacity:.5;cursor:not-allowed;transform:none}\n.btn-primary{background:var(--accent);color:var(--bg)}\n.btn-green{background:var(--green);color:#fff}\n.btn-danger{background:var(--red);color:#fff}\n.btn-ghost{background:transparent;color:var(--accent);border:1px solid var(--border)}\n.btn-ghost:hover{background:var(--accent-dim)}\n.btn-sm{padding:6px 14px;font-size:12px}\n.btn-group{display:flex;gap:10px;margin-top:16px;flex-wrap:wrap}\n/* Section header */\n.section-hd{display:flex;justify-content:space-between;align-items:center;margin-bottom:4px}\n.section-hd h2{font-size:16px;font-weight:600}\n/* Job card */\n.job-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;margin-top:14px}\n.job-hd{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}\n.job-title{font-weight:600;font-size:14px}\n.job-id{font-family:var(--font-mono);font-size:11px;color:var(--text-dim)}\n.pbar-wrap{margin:6px 0 14px}\n.pbar-label{font-size:11px;color:var(--text-dim);margin-bottom:4px}\n.pbar{background:var(--surface-hover);border-radius:4px;height:6px;overflow:hidden}\n.pbar-fill{background:var(--accent);height:100%;border-radius:4px;transition:width .3s}\n.kanban{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px}\n.lane{min-height:40px}\n.lane-hd{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:1px;margin-bottom:6px;padding-bottom:4px;border-bottom:2px solid var(--border)}\n.lane-hd.y{color:var(--yellow);border-color:var(--yellow)}.lane-hd.r{color:var(--red);border-color:var(--red)}.lane-hd.g{color:var(--green);border-color:var(--green)}\n.tcard{background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:8px 10px;margin-bottom:6px;font-size:12px;cursor:pointer;transition:all .15s}\n.tcard:hover{border-color:var(--accent);background:var(--surface-hover)}\n.tcard-trace{font-family:var(--font-mono);font-size:10px;color:var(--accent);margin-bottom:3px}\n.tcard-meta{font-size:10px;color:var(--text-dim);margin-top:3px}\n/* Task detail modal */\n.task-detail-hd{display:flex;align-items:center;gap:10px;margin-bottom:16px}\n.task-detail-hd .dot{width:12px;height:12px}\n.task-detail-status{font-size:11px;padding:3px 10px;border-radius:12px;font-weight:600;text-transform:uppercase}\n.task-detail-status.DISPATCHED,.task-detail-status.PENDING{background:rgba(234,179,8,.15);color:var(--yellow)}\n.task-detail-status.OVERDUE{background:rgba(239,68,68,.15);color:var(--red)}\n.task-detail-status.RESOLVED{background:rgba(34,197,94,.15);color:var(--green)}\n.trace-copy-row{display:flex;align-items:center;gap:8px;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:8px 12px;margin-bottom:14px;font-family:var(--font-mono);font-size:13px;color:var(--accent)}\n.trace-copy-row button{background:var(--surface-hover);border:1px solid var(--border);color:var(--text-dim);border-radius:4px;padding:3px 10px;font-size:11px;cursor:pointer;margin-left:auto;flex-shrink:0}\n.trace-copy-row button:hover{color:var(--accent);border-color:var(--accent)}\n.detail-row{margin-bottom:12px}\n.detail-label{font-size:11px;color:var(--text-dim);margin-bottom:3px;font-weight:500}\n.detail-value{font-size:13px}\n.result-display{background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:12px;font-family:var(--font-mono);font-size:12px;white-space:pre-wrap;max-height:200px;overflow-y:auto}\n/* Demo cards */\n.demo-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px;margin-top:16px}\n.demo-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:20px;cursor:pointer;transition:all .2s;position:relative;overflow:hidden}\n.demo-card:hover{border-color:var(--accent);transform:translateY(-2px);box-shadow:0 8px 24px rgba(0,212,255,.08)}\n.demo-card-emoji{font-size:36px;margin-bottom:10px}\n.demo-card-title{font-weight:700;font-size:15px;margin-bottom:4px}\n.demo-card-role{font-size:12px;color:var(--accent);margin-bottom:8px;font-family:var(--font-mono)}\n.demo-card-desc{font-size:12px;color:var(--text-dim);line-height:1.6;margin-bottom:10px}\n.demo-card-agents{display:flex;flex-wrap:wrap;gap:4px}\n.demo-card-agents span{background:var(--accent-dim);border:1px solid color-mix(in srgb,var(--accent) 20%,transparent);border-radius:4px;padding:1px 6px;font-size:10px;color:var(--accent)}\n.demo-card-prompt{margin-top:10px;padding-top:10px;border-top:1px solid var(--border);font-size:11px;color:var(--text-dim);font-style:italic}\n.demo-loading{position:absolute;inset:0;background:rgba(15,17,23,.85);display:flex;align-items:center;justify-content:center;flex-direction:column;gap:8px;z-index:2}\n/* Toast */\n.toast{position:fixed;bottom:24px;right:24px;padding:12px 20px;border-radius:8px;font-size:13px;z-index:9999;animation:slide-up .25s ease;font-weight:500;box-shadow:0 8px 24px rgba(0,0,0,.4)}\n.toast.ok{background:var(--green);color:#fff}.toast.err{background:var(--red);color:#fff}\n@keyframes slide-up{from{transform:translateY(16px);opacity:0}to{transform:translateY(0);opacity:1}}\n/* Empty state */\n.empty-state{text-align:center;padding:48px 20px;color:var(--text-dim)}\n.empty-state .icon{font-size:48px;margin-bottom:12px;opacity:.6}\n.empty-state p{font-size:14px;margin-bottom:16px;max-width:360px;margin-left:auto;margin-right:auto;line-height:1.6}\n/* Modal/Overlay */\n.overlay{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:100;display:flex;align-items:flex-start;justify-content:center;padding-top:60px;animation:fade-in .15s ease}\n.overlay .form-card{max-width:620px;width:100%;max-height:85vh;overflow-y:auto;margin:0}\n@keyframes fade-in{from{opacity:0}to{opacity:1}}\n/* Agent chips for AI planning */\n.chip-bar{display:flex;gap:6px;margin-bottom:10px;flex-wrap:wrap}\n.chip-filter{background:var(--surface-hover);color:var(--text-dim);border:1px solid var(--border);border-radius:14px;padding:3px 12px;font-size:11px;cursor:pointer;transition:all .15s}\n.chip-filter.active{background:var(--accent-dim);color:var(--accent);border-color:var(--accent)}\n.agent-chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:8px}\n.achip{background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:6px 12px;font-size:12px;cursor:pointer;transition:all .15s;display:flex;align-items:center;gap:6px;user-select:none}\n.achip:hover{border-color:var(--text-dim)}\n.achip.selected{border-color:var(--accent);background:var(--accent-dim)}\n.achip .adot{width:8px;height:8px;border-radius:50%;flex-shrink:0}\n.achip .adot.IDLE{background:var(--green)}.achip .adot.BUSY{background:var(--yellow)}.achip .adot.OFFLINE{background:var(--red)}.achip .adot.OOM{background:var(--purple)}\n/* Plan preview cards */\n.plan-card{background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:16px;margin-bottom:12px}\n.plan-card-hd{display:flex;align-items:center;gap:8px;margin-bottom:8px}\n.plan-card-hd .adot{width:8px;height:8px;border-radius:50%}\n.plan-card-agent{font-weight:600;font-size:13px}\n.plan-card-agentid{font-family:var(--font-mono);font-size:10px;color:var(--text-dim)}\n.plan-card-desc{font-size:13px;margin-bottom:10px;font-weight:500}\n.briefing-box{background:var(--surface);border-left:3px solid var(--accent);border-radius:0 6px 6px 0;padding:10px 14px;font-size:12px;line-height:1.6;color:var(--text);margin-bottom:8px;position:relative}\n.briefing-label{font-size:10px;color:var(--accent);font-weight:600;margin-bottom:4px;font-family:var(--font-mono)}\n.briefing-copy{position:absolute;top:8px;right:8px;background:var(--surface-hover);border:1px solid var(--border);color:var(--text-dim);border-radius:4px;padding:2px 8px;font-size:10px;cursor:pointer}\n.briefing-copy:hover{color:var(--accent);border-color:var(--accent)}\n.plan-card-dl{font-size:11px;color:var(--text-dim);font-family:var(--font-mono)}\n/* Spinner */\n.spinner-wrap{text-align:center;padding:40px 20px}\n.spinner{display:inline-block;width:28px;height:28px;border:3px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin .6s linear infinite}\n@keyframes spin{to{transform:rotate(360deg)}}\n.spinner-text{color:var(--text-dim);font-size:13px;margin-top:12px}\n/* Manual task row */\n.task-row{background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:14px;margin-bottom:10px;position:relative}\n.task-row .remove-task{position:absolute;top:8px;right:10px;background:none;border:none;color:var(--red);cursor:pointer;font-size:16px;line-height:1}\n.task-row .fg{margin-bottom:10px}\n.task-row .fg:last-child{margin-bottom:0}\n/* Markdown Editor */\n.md-editor{position:relative}\n.md-editor textarea{min-height:120px;resize:vertical;font-family:var(--font-mono);font-size:12px;width:100%;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:10px 12px;color:var(--text);outline:none;transition:border-color .15s}\n.md-editor textarea:focus{border-color:var(--accent)}\n.md-expand-btn{position:absolute;top:6px;right:6px;background:var(--surface-hover);border:1px solid var(--border);color:var(--text-dim);border-radius:4px;width:24px;height:24px;font-size:14px;cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:2;transition:all .15s;line-height:1}\n.md-expand-btn:hover{color:var(--accent);border-color:var(--accent)}\n.md-editor.fullscreen{position:fixed;inset:20px;z-index:200;background:var(--surface);border-radius:var(--radius);padding:16px;display:flex;flex-direction:column;box-shadow:0 20px 60px rgba(0,0,0,.7)}\n.md-editor.fullscreen textarea{flex:1;min-height:unset;height:100%;resize:none;font-size:14px}\n.md-editor.fullscreen .md-expand-btn{top:22px;right:22px}\n.md-fullscreen-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.6);z-index:199}\n/* Team badge */\n.team-badge{display:inline-block;background:rgba(168,85,247,.12);border:1px solid rgba(168,85,247,.25);border-radius:4px;padding:1px 8px;font-size:10px;color:var(--purple);margin-right:4px;margin-bottom:2px}\n/* Evaluation */\n.eval-badge{display:inline-block;padding:3px 10px;border-radius:12px;font-weight:700;font-size:13px;font-family:var(--font-mono)}\n.eval-badge.top{background:rgba(34,197,94,.15);color:var(--green)}\n.eval-badge.mid{background:rgba(234,179,8,.15);color:var(--yellow)}\n.eval-badge.low{background:rgba(239,68,68,.15);color:var(--red)}\n</style>\n</head>\n<body>\n<header>\n <h1>HumanClaw</h1>\n <span class=\"subtitle\">碳基节点编排框架</span>\n <span class=\"hdr-spacer\"></span>\n <button class=\"gear-btn\" onclick=\"showSettings()\" title=\"设置\">&#9881;</button>\n</header>\n<nav>\n <button class=\"tab active\" data-panel=\"fleet\">碳基算力池</button>\n <button class=\"tab\" data-panel=\"pipeline\">编排大盘</button>\n <button class=\"tab\" data-panel=\"terminal\">I/O 终端</button>\n</nav>\n<main>\n <section id=\"fleet\" class=\"panel active\"></section>\n <section id=\"pipeline\" class=\"panel\"></section>\n <section id=\"terminal\" class=\"panel\"></section>\n</main>\n\n<script>\nconst API='/api/v1';\nfunction esc(s){const d=document.createElement('div');d.textContent=s;return d.innerHTML}\nfunction toast(msg,ok){\n document.querySelectorAll('.toast').forEach(t=>t.remove());\n const t=document.createElement('div');t.className='toast '+(ok?'ok':'err');t.textContent=msg;document.body.appendChild(t);\n setTimeout(()=>t.remove(),3500);\n}\nlet cachedAgents=[];\nlet cachedTeams=[];\nlet currentPlan=null;\nlet selectedAgentIds=new Set();\nlet selectedTeamId='';\n\nwindow.toggleFullscreen=function(btn){\n const editor=btn.closest('.md-editor');\n if(editor.classList.contains('fullscreen')){\n editor.classList.remove('fullscreen');\n const bd=document.querySelector('.md-fullscreen-backdrop');\n if(bd)bd.remove();\n }else{\n const bd=document.createElement('div');bd.className='md-fullscreen-backdrop';\n bd.onclick=()=>{editor.classList.remove('fullscreen');bd.remove()};\n document.body.appendChild(bd);\n editor.classList.add('fullscreen');\n const ta=editor.querySelector('textarea');if(ta)ta.focus();\n }\n};\n\n// ═══════════════════════════════════════════════\n// DEMO SCENARIOS\n// ═══════════════════════════════════════════════\nconst DEMOS={\n sanguo:{\n emoji:'&#128009;',title:'三国蜀汉',role:'你是刘备',\n desc:'桃园结义,三顾茅庐。作为蜀汉之主,统领五虎上将和卧龙凤雏,逐鹿中原。',\n prompt:'北伐中原,兵分三路,需要攻城、断粮和外交三管齐下',\n teams:[\n {name:'五虎上将',description:'蜀汉五大猛将',members:['关羽','张飞','赵云','黄忠','马超'],relationships:{'关羽':'义弟兼军团统帅','张飞':'义弟兼先锋大将','赵云':'心腹爱将','黄忠':'老当益壮的猛将','马超':'归降的西凉虎将'}},\n {name:'谋士团',description:'运筹帷幄的智囊团',members:['诸葛亮','庞统'],relationships:{'诸葛亮':'首席军师,如鱼得水','庞统':'副军师,奇谋百出'}}\n ],\n agents:[\n {name:'关羽',capabilities:['武艺','统兵','镇守要地','水军指挥'],relationship:'义弟,桃园结义二弟,最信任的兄弟和大将'},\n {name:'张飞',capabilities:['武艺','先锋突击','骑兵指挥','威慑敌军'],relationship:'义弟,桃园结义三弟,性如烈火但忠心耿耿'},\n {name:'赵云',capabilities:['武艺','护卫','骑兵突击','侦察敏捷'],relationship:'四弟级别的心腹爱将,长坂坡救阿斗'},\n {name:'诸葛亮',capabilities:['战略规划','内政治理','外交','发明创造','阵法'],relationship:'三顾茅庐请来的军师,如鱼得水的关系'},\n {name:'庞统',capabilities:['战略规划','奇谋','攻城战术','地形分析'],relationship:'凤雏,与诸葛亮齐名的军师,副军师中郎将'},\n {name:'黄忠',capabilities:['武艺','弓箭','老当益壮','攻城战'],relationship:'老将军,定军山斩夏侯渊,五虎上将之一'},\n {name:'马超',capabilities:['武艺','骑兵','西凉作战','威慑羌族'],relationship:'归降的西凉猛将,五虎上将之一'}\n ]\n },\n tech:{\n emoji:'&#128187;',title:'互联网大厂',role:'你是技术总监',\n desc:'带领一支全栈团队,从前端到运维一应俱全。应对高并发、搞 AI、上线新系统。',\n prompt:'上线一个 AI 智能客服系统,包括前端界面、后端 API、推荐算法、压力测试和灰度发布方案',\n teams:[\n {name:'前端组',description:'负责所有前端页面开发',members:['前端老李','设计师小林'],relationships:{'前端老李':'前端TL,核心骨干','设计师小林':'资深设计师'}},\n {name:'后端组',description:'后端架构与服务开发',members:['后端大王','算法小陈'],relationships:{'后端大王':'架构师,技术决策者','算法小陈':'算法工程师,需要指导'}},\n {name:'产品组',description:'产品需求与项目管理',members:['产品经理 Amy'],relationships:{'产品经理 Amy':'产品负责人'}},\n {name:'质量组',description:'测试与运维保障',members:['测试负责人老赵','运维 DevOps 阿杰'],relationships:{'测试负责人老赵':'质量把关人','运维 DevOps 阿杰':'SRE负责人'}}\n ],\n agents:[\n {name:'前端老李',capabilities:['React','TypeScript','Next.js','移动端适配','性能优化'],relationship:'P7 前端 TL,跟了你三年,技术过硬但最近有点倦怠'},\n {name:'后端大王',capabilities:['Java','Go','微服务','数据库设计','高并发架构'],relationship:'P8 后端架构师,技术大拿,说话直来直去'},\n {name:'算法小陈',capabilities:['机器学习','推荐系统','NLP','Python','数据分析'],relationship:'P6 算法工程师,刚从学校毕业一年,潜力很大但经验不足'},\n {name:'产品经理 Amy',capabilities:['需求分析','用户调研','PRD撰写','项目管理','数据驱动'],relationship:'P7 产品经理,业务感觉很好,跨部门沟通能力强'},\n {name:'设计师小林',capabilities:['UI设计','交互设计','Figma','设计系统','用户体验'],relationship:'P6 资深设计师,审美在线,偶尔和产品经理吵架'},\n {name:'测试负责人老赵',capabilities:['自动化测试','性能测试','安全测试','测试用例设计','CI集成'],relationship:'P7 测试负责人,入职五年老员工,对质量要求极高'},\n {name:'运维 DevOps 阿杰',capabilities:['Kubernetes','Docker','CI/CD','监控告警','云原生架构'],relationship:'P7 SRE,半夜被 oncall 叫起来过无数次,求稳派'}\n ]\n },\n gov:{\n emoji:'&#127482;&#127480;',title:'美国政府',role:'你是特朗普 (POTUS)',\n desc:'Make the executive branch great again! 管理你的核心内阁成员,推行政策议程。',\n prompt:'制定一个让美国制造业回流的综合计划,需要关税政策、减税方案、能源保障、边境安全配合和政府效率优化',\n teams:[\n {name:'经济安全团队',description:'经济政策与财政管理',members:['Scott Bessent','Elon Musk'],relationships:{'Scott Bessent':'财政部长,首席经济顾问','Elon Musk':'DOGE 负责人,效率改革推动者'}},\n {name:'国防外交团队',description:'国防与外交事务',members:['Marco Rubio','Pete Hegseth','Tulsi Gabbard'],relationships:{'Marco Rubio':'国务卿,外交总管','Pete Hegseth':'国防部长,军事事务','Tulsi Gabbard':'情报总监,安全评估'}},\n {name:'国内事务团队',description:'国内安全与公共服务',members:['Kristi Noem','Robert F. Kennedy Jr.'],relationships:{'Kristi Noem':'国土安全部长,边境强硬派','Robert F. Kennedy Jr.':'卫生部长,医疗改革'}}\n ],\n agents:[\n {name:'Elon Musk',capabilities:['政府效率','成本削减','科技创新','SpaceX','Tesla','社交媒体'],relationship:'DOGE 负责人,世界首富,Twitter/X 老板,最具影响力的盟友'},\n {name:'Marco Rubio',capabilities:['外交政策','拉美事务','国际谈判','制裁政策','国家安全'],relationship:'国务卿,佛罗里达参议员,曾经的竞选对手变忠实支持者'},\n {name:'Pete Hegseth',capabilities:['国防战略','军事改革','退伍军人事务','军费预算','作战指挥'],relationship:'国防部长,前 Fox News 主持人,坚定的 MAGA 支持者'},\n {name:'Scott Bessent',capabilities:['经济政策','金融市场','税收改革','债务管理','贸易政策'],relationship:'财政部长,华尔街老将,关键经济顾问'},\n {name:'Kristi Noem',capabilities:['国土安全','边境管控','移民执法','反恐','网络安全'],relationship:'国土安全部长,前南达科他州长,边境强硬派'},\n {name:'Tulsi Gabbard',capabilities:['国家情报','情报分析','反间谍','网络战','安全评估'],relationship:'国家情报总监,前民主党国会议员,转投共和党的盟友'},\n {name:'Robert F. Kennedy Jr.',capabilities:['公共卫生','疫苗政策','食品安全','药品监管','医疗改革'],relationship:'卫生与公众服务部长,反建制派,疫苗怀疑论者'}\n ]\n }\n};\n\nfunction renderDemoCards(){\n let h='<div style=\"margin-top:24px\"><div style=\"font-size:14px;font-weight:600;margin-bottom:4px\">&#127918; 快速体验 Demo</div><div style=\"font-size:12px;color:var(--text-dim);margin-bottom:12px\">选择一个场景,一键加载碳基节点,立即开始编排</div></div>';\n h+='<div class=\"demo-grid\">';\n for(const[key,d]of Object.entries(DEMOS)){\n h+='<div class=\"demo-card\" id=\"demo-card-'+key+'\" onclick=\"loadDemo(\\\\''+key+'\\\\')\">';\n h+='<div class=\"demo-card-emoji\">'+d.emoji+'</div>';\n h+='<div class=\"demo-card-title\">'+d.title+'</div>';\n h+='<div class=\"demo-card-role\">'+d.role+'</div>';\n h+='<div class=\"demo-card-desc\">'+esc(d.desc)+'</div>';\n h+='<div class=\"demo-card-agents\">';\n for(const a of d.agents)h+='<span>'+esc(a.name)+'</span>';\n h+='</div>';\n h+='<div class=\"demo-card-prompt\">&#128161; &quot;'+esc(d.prompt)+'&quot;</div>';\n h+='</div>';\n }\n h+='</div>';\n return h;\n}\n\nwindow.loadDemo=async function(key){\n const demo=DEMOS[key];\n if(!demo)return;\n const card=document.getElementById('demo-card-'+key);\n if(card){\n const ld=document.createElement('div');ld.className='demo-loading';\n ld.innerHTML='<div class=\"spinner\"></div><div style=\"font-size:12px;color:var(--text-dim)\">加载 '+demo.title+' 场景中...</div>';\n card.appendChild(ld);\n }\n let ok=0;\n const demoAgentIds=[];\n const agentNameToId={};\n for(const a of demo.agents){\n try{\n const r=await fetch(API+'/nodes',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:a.name,capabilities:a.capabilities,relationship:a.relationship})});\n if(r.ok){ok++;const d=await r.json();demoAgentIds.push(d.agent_id);agentNameToId[a.name]=d.agent_id;}\n }catch{}\n }\n // Create demo teams\n if(demo.teams){\n for(const tm of demo.teams){\n try{\n const tr=await fetch(API+'/teams',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name:tm.name,description:tm.description||''})});\n if(tr.ok){\n const td=await tr.json();\n for(const mName of tm.members){\n const agentId=agentNameToId[mName];\n if(agentId){\n const rel=tm.relationships?tm.relationships[mName]||'':'';\n await fetch(API+'/teams/'+td.team_id+'/members',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({agent_id:agentId,relationship:rel})});\n }\n }\n }\n }catch{}\n }\n }\n toast(demo.title+' 场景已加载!'+ok+'/'+demo.agents.length+' 个节点注册成功',true);\n // Switch to pipeline and open AI planning with suggested prompt\n load('fleet');\n setTimeout(()=>{\n // Switch tab to pipeline\n const tabs=document.querySelectorAll('.tab');\n const panels=document.querySelectorAll('.panel');\n tabs.forEach(x=>x.classList.remove('active'));\n panels.forEach(x=>x.classList.remove('active'));\n tabs[1].classList.add('active');\n document.getElementById('pipeline').classList.add('active');\n load('pipeline');\n // Wait for pipeline to render, then open create job with demo prompt\n setTimeout(()=>showCreateJob(demo.prompt,demoAgentIds),300);\n },200);\n};\n\nwindow.showDemoSelector=function(){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n let h='<div class=\"form-card\"><h3>&#127918; 选择 Demo 场景</h3>';\n h+='<div style=\"font-size:12px;color:var(--text-dim);margin-bottom:16px\">一键加载碳基节点,立即开始编排体验</div>';\n for(const[key,d]of Object.entries(DEMOS)){\n h+='<div class=\"demo-card\" id=\"demo-card-'+key+'\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove();loadDemo(\\\\''+key+'\\\\')\" style=\"margin-bottom:10px\">';\n h+='<div style=\"display:flex;align-items:center;gap:10px;margin-bottom:8px\"><span style=\"font-size:28px\">'+d.emoji+'</span><div><div class=\"demo-card-title\">'+d.title+'</div><div class=\"demo-card-role\">'+d.role+'</div></div></div>';\n h+='<div class=\"demo-card-desc\">'+esc(d.desc)+'</div>';\n h+='<div class=\"demo-card-agents\">';\n for(const a of d.agents)h+='<span>'+esc(a.name)+'</span>';\n h+='</div></div>';\n }\n h+='<div class=\"btn-group\"><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button></div></div>';\n ov.innerHTML=h;\n document.body.appendChild(ov);\n};\n\n// ═══════════════════════════════════════════════\n// FLEET\n// ═══════════════════════════════════════════════\nasync function loadFleet(el){\n el.innerHTML='<div class=\"empty-state\"><p>加载中...</p></div>';\n try{\n const r=await fetch(API+'/nodes/status');\n const d=await r.json();\n cachedAgents=d.agents||[];\n // Also load teams\n try{const tr=await fetch(API+'/teams');const td=await tr.json();cachedTeams=td.teams||[]}catch{cachedTeams=[]}\n let h='<div class=\"section-hd\"><h2>碳基算力池</h2><div style=\"display:flex;gap:6px\"><button class=\"btn btn-ghost btn-sm\" onclick=\"showDemoSelector()\">&#127918; Demo</button><button class=\"btn btn-primary btn-sm\" onclick=\"showAddAgent()\">+ 添加节点</button></div></div>';\n h+='<div class=\"stats\">';\n h+='<div class=\"stat\"><div class=\"stat-val\">'+d.total+'</div><div class=\"stat-lbl\">总计</div></div>';\n h+='<div class=\"stat\"><div class=\"stat-val\" style=\"color:var(--green)\">'+d.idle+'</div><div class=\"stat-lbl\">空闲</div></div>';\n h+='<div class=\"stat\"><div class=\"stat-val\" style=\"color:var(--yellow)\">'+d.busy+'</div><div class=\"stat-lbl\">忙碌</div></div>';\n h+='<div class=\"stat\"><div class=\"stat-val\" style=\"color:var(--red)\">'+d.offline+'</div><div class=\"stat-lbl\">离线</div></div>';\n h+='<div class=\"stat\"><div class=\"stat-val\" style=\"color:var(--purple)\">'+d.oom+'</div><div class=\"stat-lbl\">崩溃</div></div>';\n h+='</div>';\n if(!d.agents.length){\n h+='<div class=\"empty-state\" style=\"margin-top:32px\"><div class=\"icon\">&#128100;</div><p>还没有碳基节点。点击上方「+ 添加节点」手动注册,或选择一个 Demo 场景快速体验。</p></div>';\n h+=renderDemoCards();\n el.innerHTML=h;return;\n }\n h+='<div class=\"grid\">';\n for(const a of d.agents){\n h+='<div class=\"card\"><div class=\"agent-header\"><span class=\"dot '+a.status+'\"></span><span class=\"agent-name\">'+esc(a.name)+'</span></div>';\n h+='<div class=\"agent-id\">'+a.agent_id+'</div>';\n if(a.relationship)h+='<div style=\"font-size:11px;color:var(--purple);margin-bottom:4px\">&#128101; '+esc(a.relationship)+'</div>';\n // Team badges\n const agentTeams=cachedTeams.filter(tm=>tm.members&&tm.members.some(m=>m.agent_id===a.agent_id));\n if(agentTeams.length){h+='<div style=\"margin-bottom:4px\">';for(const tm of agentTeams)h+='<span class=\"team-badge\">'+esc(tm.name)+'</span>';h+='</div>'}\n h+='<div class=\"caps\">';for(const c of a.capabilities)h+='<span class=\"cap\">'+esc(c)+'</span>';h+='</div>';\n h+='<div class=\"agent-meta\"><span>任务: '+a.active_task_count+'</span>';\n if(a.avg_delivery_hours!==null)h+='<span>平均交付: '+a.avg_delivery_hours+'h</span>';\n h+='</div>';\n h+='<div class=\"agent-actions\">';\n h+='<select onchange=\"changeStatus(\\\\''+a.agent_id+'\\\\',this.value)\">';\n for(const s of ['IDLE','BUSY','OFFLINE','OOM'])h+='<option'+(s===a.status?' selected':'')+'>'+s+'</option>';\n h+='</select>';\n h+='<button onclick=\"deleteAgent(\\\\''+a.agent_id+'\\\\')\">删除</button>';\n h+='</div></div>';\n }\n h+='</div>';\n // Teams section\n h+='<div style=\"margin-top:32px\"><div class=\"section-hd\"><h2>团队管理</h2><button class=\"btn btn-primary btn-sm\" onclick=\"showCreateTeam()\">+ 创建团队</button></div>';\n if(cachedTeams.length){\n h+='<div class=\"grid\">';\n for(const tm of cachedTeams){\n const memberCount=tm.members?tm.members.length:0;\n h+='<div class=\"card\" style=\"cursor:pointer\" onclick=\"showTeamDetail(\\\\''+tm.team_id+'\\\\')\">';\n h+='<div style=\"display:flex;align-items:center;gap:8px;margin-bottom:8px\"><span style=\"font-size:20px\">&#128101;</span><span class=\"agent-name\">'+esc(tm.name)+'</span></div>';\n if(tm.description)h+='<div style=\"font-size:12px;color:var(--text-dim);margin-bottom:8px\">'+esc(tm.description)+'</div>';\n h+='<div style=\"font-size:11px;color:var(--accent)\">'+memberCount+' 个成员</div>';\n h+='</div>';\n }\n h+='</div>';\n }else{\n h+='<div style=\"font-size:12px;color:var(--text-dim);margin-top:8px\">暂无团队。创建团队后可按团队分配任务。</div>';\n }\n h+='</div>';\n el.innerHTML=h;\n }catch(e){el.innerHTML='<div class=\"empty-state\"><p>加载失败: '+e.message+'</p></div>'}\n}\nwindow.showAddAgent=function(){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n ov.innerHTML='<div class=\"form-card\"><h3>+ 添加碳基节点</h3>'\n +'<div class=\"fg\"><label>节点名称</label><input id=\"aa-name\" placeholder=\"例: 前端老李\"/></div>'\n +'<div class=\"fg\"><label>技能标签</label><input id=\"aa-caps\" placeholder=\"例: UI/UX, 前端开发, 抗压能力强\"/><div class=\"hint\">多个标签用逗号分隔</div></div>'\n +'<div class=\"fg\"><label>与你的关系 <span style=\"color:var(--text-dim);font-weight:400\">(可选)</span></label><input id=\"aa-rel\" placeholder=\"例: 直属下属 / 实习生 / 外包同事 / 义弟\"/><div class=\"hint\">描述此人与你的关系,AI 规划和模拟交付时会参考</div></div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-primary\" onclick=\"submitAgent()\">注册节点</button><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button></div></div>';\n document.body.appendChild(ov);\n document.getElementById('aa-name').focus();\n};\nwindow.submitAgent=async function(){\n const name=document.getElementById('aa-name').value.trim();\n const caps=document.getElementById('aa-caps').value.trim();\n const rel=document.getElementById('aa-rel').value.trim();\n if(!name){toast('请输入节点名称',false);return}\n if(!caps){toast('请输入至少一个技能标签',false);return}\n try{\n const r=await fetch(API+'/nodes',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name,capabilities:caps.split(',').map(s=>s.trim()).filter(Boolean),relationship:rel})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'注册失败',false);return}\n toast('节点 '+d.agent_id+' 注册成功!',true);\n document.getElementById('overlay').remove();\n load('fleet');\n }catch{toast('网络错误',false)}\n};\nwindow.changeStatus=async function(id,status){\n try{\n const r=await fetch(API+'/nodes/'+id+'/status',{method:'PATCH',headers:{'Content-Type':'application/json'},body:JSON.stringify({status})});\n if(!r.ok){const d=await r.json();toast(d.error||'更新失败',false);return}\n toast('状态已更新',true);\n }catch{toast('网络错误',false)}\n};\nwindow.deleteAgent=async function(id){\n if(!confirm('确定删除节点 '+id+'?'))return;\n try{\n const r=await fetch(API+'/nodes/'+id,{method:'DELETE'});\n if(!r.ok){const d=await r.json();toast(d.error||'删除失败',false);return}\n toast('节点已删除',true);load('fleet');\n }catch{toast('网络错误',false)}\n};\n\n// ─── Team Management ────────────────────────\nwindow.showCreateTeam=function(){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n let membersHtml='<div id=\"team-members-list\">';\n for(const a of cachedAgents){\n membersHtml+='<div style=\"display:flex;align-items:center;gap:8px;margin-bottom:8px\">';\n membersHtml+='<label style=\"display:flex;align-items:center;gap:6px;cursor:pointer;font-size:12px\"><input type=\"checkbox\" class=\"tm-check\" value=\"'+a.agent_id+'\"/><span class=\"dot '+a.status+'\" style=\"width:8px;height:8px\"></span>'+esc(a.name)+'</label>';\n membersHtml+='<input class=\"tm-rel\" data-agent=\"'+a.agent_id+'\" placeholder=\"团队中的关系\" style=\"flex:1;background:var(--bg);border:1px solid var(--border);border-radius:6px;padding:4px 8px;font-size:11px;color:var(--text);outline:none\"/>';\n membersHtml+='</div>';\n }\n membersHtml+='</div>';\n ov.innerHTML='<div class=\"form-card\"><h3>+ 创建团队</h3>'\n +'<div class=\"fg\"><label>团队名称</label><input id=\"tm-name\" placeholder=\"例: 前端组\"/></div>'\n +'<div class=\"fg\"><label>团队描述 <span style=\"color:var(--text-dim);font-weight:400\">(可选)</span></label><input id=\"tm-desc\" placeholder=\"例: 负责所有前端页面开发\"/></div>'\n +'<div class=\"fg\"><label>选择成员及团队关系</label>'+membersHtml+'</div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-primary\" onclick=\"submitTeam()\">创建团队</button><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button></div></div>';\n document.body.appendChild(ov);\n document.getElementById('tm-name').focus();\n};\nwindow.submitTeam=async function(){\n const name=document.getElementById('tm-name').value.trim();\n const desc=document.getElementById('tm-desc').value.trim();\n if(!name){toast('请输入团队名称',false);return}\n try{\n const r=await fetch(API+'/teams',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({name,description:desc})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'创建失败',false);return}\n const checks=document.querySelectorAll('.tm-check:checked');\n for(const chk of checks){\n const agentId=chk.value;\n const relInput=document.querySelector('.tm-rel[data-agent=\"'+agentId+'\"]');\n const rel=relInput?relInput.value.trim():'';\n await fetch(API+'/teams/'+d.team_id+'/members',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({agent_id:agentId,relationship:rel})});\n }\n toast('团队「'+name+'」创建成功!',true);\n document.getElementById('overlay').remove();\n load('fleet');\n }catch{toast('网络错误',false)}\n};\nwindow.showTeamDetail=async function(teamId){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n ov.innerHTML='<div class=\"form-card\"><h3>团队详情</h3><div class=\"spinner-wrap\"><div class=\"spinner\"></div></div></div>';\n document.body.appendChild(ov);\n try{\n const r=await fetch(API+'/teams/'+teamId);\n const tm=await r.json();\n if(!r.ok){toast(tm.error||'加载失败',false);ov.remove();return}\n const fc=ov.querySelector('.form-card');\n let h='<h3>'+esc(tm.name)+'</h3>';\n if(tm.description)h+='<div style=\"font-size:12px;color:var(--text-dim);margin-bottom:16px\">'+esc(tm.description)+'</div>';\n h+='<div style=\"font-size:11px;color:var(--text-dim);margin-bottom:12px;font-family:var(--font-mono)\">'+tm.team_id+'</div>';\n if(tm.members&&tm.members.length){\n h+='<div style=\"font-size:13px;font-weight:600;margin-bottom:8px\">成员 ('+tm.members.length+')</div>';\n for(const m of tm.members){\n h+='<div style=\"display:flex;align-items:center;gap:8px;padding:8px;background:var(--bg);border:1px solid var(--border);border-radius:8px;margin-bottom:6px\">';\n h+='<span class=\"dot '+(m.agent_status||'IDLE')+'\" style=\"width:8px;height:8px\"></span>';\n h+='<span style=\"font-size:13px;font-weight:500\">'+esc(m.agent_name||m.agent_id)+'</span>';\n if(m.relationship)h+='<span style=\"font-size:11px;color:var(--purple);margin-left:auto\">'+esc(m.relationship)+'</span>';\n h+='<button style=\"margin-left:'+(m.relationship?'8px':'auto')+';background:none;border:none;color:var(--red);cursor:pointer;font-size:14px\" onclick=\"removeTeamMember(\\\\''+teamId+'\\\\',\\\\''+m.agent_id+'\\\\')\">&times;</button>';\n h+='</div>';\n }\n }else{\n h+='<div style=\"font-size:12px;color:var(--text-dim)\">暂无成员</div>';\n }\n h+='<div class=\"btn-group\"><button class=\"btn btn-danger btn-sm\" onclick=\"deleteTeam(\\\\''+teamId+'\\\\')\">删除团队</button><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">关闭</button></div>';\n fc.innerHTML=h;\n }catch{toast('加载失败',false);ov.remove()}\n};\nwindow.removeTeamMember=async function(teamId,agentId){\n try{\n await fetch(API+'/teams/'+teamId+'/members/'+agentId,{method:'DELETE'});\n toast('成员已移除',true);\n document.getElementById('overlay').remove();\n showTeamDetail(teamId);\n }catch{toast('网络错误',false)}\n};\nwindow.deleteTeam=async function(teamId){\n if(!confirm('确定删除此团队?'))return;\n try{\n const r=await fetch(API+'/teams/'+teamId,{method:'DELETE'});\n if(!r.ok){toast('删除失败',false);return}\n toast('团队已删除',true);\n document.getElementById('overlay').remove();\n load('fleet');\n }catch{toast('网络错误',false)}\n};\n\n// ═══════════════════════════════════════════════\n// PIPELINE\n// ═══════════════════════════════════════════════\nlet allTasks=[];\nfunction tcard(t){\n return '<div class=\"tcard\" onclick=\"showTaskDetail(\\\\''+t.trace_id+'\\\\')\"><div class=\"tcard-trace\">'+t.trace_id+'</div><div>'+esc(t.todo_description)+'</div><div class=\"tcard-meta\">'+t.assignee_id+' | '+new Date(t.deadline).toLocaleString()+'</div></div>';\n}\nasync function loadPipeline(el){\n el.innerHTML='<div class=\"empty-state\"><p>加载中...</p></div>';\n allTasks=[];\n try{\n try{const ar=await fetch(API+'/nodes/status');const ad=await ar.json();cachedAgents=ad.agents||[]}catch{}\n const r=await fetch(API+'/jobs/active');\n const d=await r.json();\n let h='<div class=\"section-hd\"><h2>碳基编排大盘</h2><button class=\"btn btn-primary btn-sm\" onclick=\"showCreateJob()\">+ 创建任务</button></div>';\n if(!d.jobs.length){\n h+='<div class=\"empty-state\" style=\"margin-top:32px\"><div class=\"icon\">📋</div><p>暂无进行中的任务。点击上方「+ 创建任务」,输入需求后 AI 自动规划分发。';\n if(!cachedAgents.length)h+='<br/><br/>⚠️ 需要先在「碳基算力池」中添加碳基节点。';\n h+='</p></div>';\n el.innerHTML=h;return;\n }\n for(const j of d.jobs){\n const res=j.tasks.filter(t=>t.status==='RESOLVED').length;\n const tot=j.tasks.length;\n const pct=tot?Math.round(res/tot*100):0;\n const dispatched=j.tasks.filter(t=>t.status==='DISPATCHED'||t.status==='PENDING');\n const overdue=j.tasks.filter(t=>t.status==='OVERDUE');\n const done=j.tasks.filter(t=>t.status==='RESOLVED');\n allTasks.push(...j.tasks);\n h+='<div class=\"job-card\"><div class=\"job-hd\"><span class=\"job-title\">'+esc(j.original_prompt)+'</span><span class=\"job-id\">'+j.job_id+'</span></div>';\n h+='<div class=\"pbar-wrap\"><div class=\"pbar-label\">'+res+'/'+tot+' 已完成 ('+pct+'%)</div><div class=\"pbar\"><div class=\"pbar-fill\" style=\"width:'+pct+'%\"></div></div></div>';\n if(pct===100)h+='<div style=\"margin-bottom:12px;display:flex;gap:6px;flex-wrap:wrap\"><button class=\"btn btn-green btn-sm\" onclick=\"reviewJob(\\\\''+j.job_id+'\\\\')\">AI 聚合审查</button><button class=\"btn btn-primary btn-sm\" onclick=\"showEvalDialog(\\\\''+j.job_id+'\\\\')\">&#128202; 生成绩效评价</button></div>';\n h+='<div class=\"kanban\"><div class=\"lane\"><div class=\"lane-hd y\">已分发 ('+dispatched.length+')</div>'+dispatched.map(tcard).join('')+'</div>';\n h+='<div class=\"lane\"><div class=\"lane-hd r\">已超时 ('+overdue.length+')</div>'+overdue.map(tcard).join('')+'</div>';\n h+='<div class=\"lane\"><div class=\"lane-hd g\">已交付 ('+done.length+')</div>'+done.map(tcard).join('')+'</div></div></div>';\n }\n el.innerHTML=h;\n }catch(e){el.innerHTML='<div class=\"empty-state\"><p>加载失败: '+e.message+'</p></div>'}\n}\n\n// ─── AI Planning Flow ────────────────────────\nlet pendingDemoPrompt='';\nwindow.showCreateJob=function(demoPrompt,demoAgentIds){\n if(!cachedAgents.length){toast('请先在「碳基算力池」中添加至少一个碳基节点',false);return}\n pendingDemoPrompt=demoPrompt||'';\n currentPlan=null;\n // Pre-select: demo agents if provided, otherwise all IDLE agents\n if(demoAgentIds&&demoAgentIds.length){\n selectedAgentIds=new Set(demoAgentIds);\n }else{\n selectedAgentIds=new Set(cachedAgents.filter(a=>a.status==='IDLE').map(a=>a.agent_id));\n }\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n renderPlanStep1(ov);\n document.body.appendChild(ov);\n};\n\nfunction renderPlanStep1(ov){\n let chipFilter='all';\n function renderChips(filter){\n chipFilter=filter;\n const filtered=filter==='idle'?cachedAgents.filter(a=>a.status==='IDLE'):cachedAgents;\n let ch='<div class=\"chip-bar\">';\n ch+='<span class=\"chip-filter'+(filter==='all'?' active':'')+'\" onclick=\"window._filterChips(\\\\'all\\\\')\">全部</span>';\n ch+='<span class=\"chip-filter'+(filter==='idle'?' active':'')+'\" onclick=\"window._filterChips(\\\\'idle\\\\')\">仅空闲</span>';\n ch+='</div>';\n ch+='<div class=\"agent-chips\">';\n for(const a of filtered){\n const sel=selectedAgentIds.has(a.agent_id);\n ch+='<div class=\"achip'+(sel?' selected':'')+'\" onclick=\"window._toggleChip(\\\\''+a.agent_id+'\\\\')\">';\n ch+='<span class=\"adot '+a.status+'\"></span>';\n ch+=esc(a.name);\n ch+='</div>';\n }\n ch+='</div>';\n return ch;\n }\n window._filterChips=function(f){\n chipFilter=f;\n const el=document.getElementById('agent-chip-area');\n if(el)el.innerHTML=renderChips(f);\n };\n window._toggleChip=function(id){\n if(selectedAgentIds.has(id))selectedAgentIds.delete(id);\n else selectedAgentIds.add(id);\n const el=document.getElementById('agent-chip-area');\n if(el)el.innerHTML=renderChips(chipFilter);\n };\n window._selectTeam=function(teamId){\n selectedTeamId=teamId;\n if(teamId){\n const tm=cachedTeams.find(t=>t.team_id===teamId);\n if(tm&&tm.members){\n selectedAgentIds=new Set(tm.members.map(m=>m.agent_id));\n }\n }\n const el=document.getElementById('agent-chip-area');\n if(el)el.innerHTML=renderChips(chipFilter);\n // Re-render to show team hint\n renderPlanStep1(document.getElementById('overlay'));\n };\n\n ov.innerHTML='<div class=\"form-card\"><h3>AI 智能规划</h3>'\n +'<div class=\"fg\"><label>输入你的需求</label><div class=\"md-editor\"><textarea id=\"plan-prompt\" rows=\"3\" placeholder=\"例: 完成首页重构,包括导航栏、内容区和页脚的响应式改版\" style=\"font-family:var(--font-sans);font-size:13px\">'+esc(pendingDemoPrompt)+'</textarea><button class=\"md-expand-btn\" onclick=\"toggleFullscreen(this)\" title=\"展开/收起\">&#x26F6;</button></div></div>'\n +'<div class=\"fg\"><label>按团队选择 <span style=\"color:var(--text-dim);font-weight:400\">(可选)</span></label><select id=\"plan-team\" onchange=\"window._selectTeam(this.value)\"><option value=\"\">-- 不按团队 --</option>'+cachedTeams.map(t=>'<option value=\"'+t.team_id+'\"'+(selectedTeamId===t.team_id?' selected':'')+'>'+esc(t.name)+'</option>').join('')+'</select>'\n +(selectedTeamId?'<div class=\"hint\" style=\"color:var(--accent)\">&#128101; 使用团队关系上下文</div>':'')+'</div>'\n +'<div class=\"fg\"><label>选择参与的碳基节点 <span style=\"color:var(--text-dim);font-weight:400\">(默认选中空闲节点)</span></label>'\n +'<div id=\"agent-chip-area\">'+renderChips('all')+'</div></div>'\n +'<div class=\"btn-group\">'\n +'<button class=\"btn btn-primary\" onclick=\"planWithAI()\">AI 规划</button>'\n +'<button class=\"btn btn-ghost\" onclick=\"showManualCreate()\">手动创建</button>'\n +'<button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button>'\n +'</div></div>';\n setTimeout(()=>{const ta=document.getElementById('plan-prompt');if(ta)ta.focus()},50);\n};\n\nwindow.planWithAI=async function(){\n const prompt=document.getElementById('plan-prompt').value.trim();\n if(!prompt){toast('请输入需求描述',false);return}\n if(selectedAgentIds.size===0){toast('请至少选择一个碳基节点',false);return}\n\n const ov=document.getElementById('overlay');\n const fc=ov.querySelector('.form-card');\n fc.innerHTML='<h3>AI 智能规划</h3><div class=\"spinner-wrap\"><div class=\"spinner\"></div><div class=\"spinner-text\">AI 正在分析需求、匹配节点、生成话术...</div></div>';\n\n try{\n const reqBody={prompt,agent_ids:Array.from(selectedAgentIds)};\n if(selectedTeamId)reqBody.team_id=selectedTeamId;\n const r=await fetch(API+'/jobs/plan',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify(reqBody)});\n const d=await r.json();\n if(!r.ok){\n toast(d.error||'规划失败',false);\n renderPlanStep1(ov);\n return;\n }\n currentPlan={...d};\n renderPlanStep2(ov);\n }catch(e){\n toast('网络错误: '+e.message,false);\n renderPlanStep1(ov);\n }\n};\n\nfunction renderPlanStep2(ov){\n const p=currentPlan;\n let h='<h3>规划预览</h3>';\n h+='<div style=\"font-size:12px;color:var(--text-dim);margin-bottom:14px\">需求: '+esc(p.original_prompt)+'</div>';\n for(let i=0;i<p.planned_tasks.length;i++){\n const t=p.planned_tasks[i];\n const agent=cachedAgents.find(a=>a.agent_id===t.assignee_id);\n const status=agent?agent.status:'IDLE';\n h+='<div class=\"plan-card\">';\n h+='<div class=\"plan-card-hd\"><span class=\"adot '+status+'\"></span><span class=\"plan-card-agent\">'+esc(t.assignee_name)+'</span><span class=\"plan-card-agentid\">'+t.assignee_id+'</span></div>';\n h+='<div class=\"plan-card-desc\">'+esc(t.todo_description)+'</div>';\n h+='<div class=\"briefing-box\"><div class=\"briefing-label\">话术 (可直接发给 TA)</div><button class=\"briefing-copy\" onclick=\"window._copyBriefing('+i+')\">复制</button>'+esc(t.briefing)+'</div>';\n const dlVal=t.deadline.slice(0,16);\n h+='<div class=\"plan-card-dl\"><label style=\"font-size:10px;color:var(--text-dim);margin-right:6px\">截止时间</label><input type=\"datetime-local\" class=\"plan-dl-input\" data-idx=\"'+i+'\" value=\"'+dlVal+'\" style=\"background:var(--surface);border:1px solid var(--border);border-radius:6px;padding:4px 8px;color:var(--text);font-family:var(--font-mono);font-size:12px;outline:none\"/></div>';\n h+='</div>';\n }\n h+='<div class=\"btn-group\">';\n h+='<button class=\"btn btn-green\" onclick=\"dispatchPlan()\">确认分发</button>';\n h+='<button class=\"btn btn-ghost\" onclick=\"renderPlanStep1(document.getElementById(\\\\'overlay\\\\'))\">重新规划</button>';\n h+='<button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button>';\n h+='</div>';\n ov.querySelector('.form-card').innerHTML=h;\n}\nwindow.renderPlanStep1=renderPlanStep1;\n\nwindow._copyBriefing=function(i){\n const text=currentPlan.planned_tasks[i].briefing;\n navigator.clipboard.writeText(text).then(()=>toast('话术已复制',true)).catch(()=>toast('复制失败',false));\n};\n\nwindow.dispatchPlan=async function(){\n const p=currentPlan;\n // Read adjusted deadlines from inputs\n const dlInputs=document.querySelectorAll('.plan-dl-input');\n const tasks=p.planned_tasks.map((t,i)=>{\n const input=dlInputs[i];\n const dl=input?new Date(input.value).toISOString():t.deadline;\n return {assignee_id:t.assignee_id,todo_description:t.todo_description,deadline:dl};\n });\n try{\n const r=await fetch(API+'/jobs/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({original_prompt:p.original_prompt,tasks})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'分发失败',false);return}\n toast('任务已分发!Job: '+d.job_id,true);\n document.getElementById('overlay').remove();\n load('pipeline');\n }catch{toast('网络错误',false)}\n};\n\n// ─── Manual creation (fallback) ──────────────\nwindow.showManualCreate=function(){\n const ov=document.getElementById('overlay');\n const optionsHtml=cachedAgents.map(a=>'<option value=\"'+a.agent_id+'\">'+esc(a.name)+' ('+a.agent_id+')</option>').join('');\n const tomorrow=new Date(Date.now()+86400000).toISOString().slice(0,16);\n ov.querySelector('.form-card').innerHTML='<h3>手动创建任务</h3>'\n +'<div class=\"fg\"><label>任务描述 (原始需求)</label><input id=\"cj-prompt\" placeholder=\"例: 完成首页改版\"/></div>'\n +'<div style=\"margin-bottom:10px;display:flex;justify-content:space-between;align-items:center\"><label style=\"font-size:13px;font-weight:600;color:var(--text)\">子任务列表</label><button class=\"btn btn-ghost btn-sm\" onclick=\"addTaskRow()\">+ 添加子任务</button></div>'\n +'<div id=\"task-rows\"><div class=\"task-row\"><div class=\"fg\"><label>指派节点</label><select class=\"tr-agent\">'+optionsHtml+'</select></div><div class=\"fg\"><label>任务描述</label><input class=\"tr-desc\" placeholder=\"具体要做什么...\"/></div><div class=\"fg\"><label>截止时间</label><input class=\"tr-deadline\" type=\"datetime-local\" value=\"'+tomorrow+'\"/></div></div></div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-primary\" onclick=\"submitJob()\">创建并分发</button><button class=\"btn btn-ghost\" onclick=\"renderPlanStep1(document.getElementById(\\\\'overlay\\\\'))\">返回 AI 规划</button><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button></div>';\n};\nwindow.addTaskRow=function(){\n const optionsHtml=cachedAgents.map(a=>'<option value=\"'+a.agent_id+'\">'+esc(a.name)+' ('+a.agent_id+')</option>').join('');\n const tomorrow=new Date(Date.now()+86400000).toISOString().slice(0,16);\n const row=document.createElement('div');row.className='task-row';\n row.innerHTML='<button class=\"remove-task\" onclick=\"this.parentElement.remove()\">&times;</button>'\n +'<div class=\"fg\"><label>指派节点</label><select class=\"tr-agent\">'+optionsHtml+'</select></div>'\n +'<div class=\"fg\"><label>任务描述</label><input class=\"tr-desc\" placeholder=\"具体要做什么...\"/></div>'\n +'<div class=\"fg\"><label>截止时间</label><input class=\"tr-deadline\" type=\"datetime-local\" value=\"'+tomorrow+'\"/></div>';\n document.getElementById('task-rows').appendChild(row);\n};\nwindow.submitJob=async function(){\n const prompt=document.getElementById('cj-prompt').value.trim();\n if(!prompt){toast('请输入任务描述',false);return}\n const rows=document.querySelectorAll('.task-row');\n const tasks=[];\n for(const row of rows){\n const aid=row.querySelector('.tr-agent').value;\n const desc=row.querySelector('.tr-desc').value.trim();\n const dl=row.querySelector('.tr-deadline').value;\n if(!desc){toast('每个子任务都需要填写描述',false);return}\n tasks.push({assignee_id:aid,todo_description:desc,deadline:new Date(dl).toISOString()});\n }\n if(!tasks.length){toast('至少添加一个子任务',false);return}\n try{\n const r=await fetch(API+'/jobs/create',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({original_prompt:prompt,tasks})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'创建失败',false);return}\n toast('任务已创建并分发!Job: '+d.job_id,true);\n document.getElementById('overlay').remove();\n load('pipeline');\n }catch{toast('网络错误',false)}\n};\nwindow.reviewJob=async function(jobId){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n ov.innerHTML='<div class=\"form-card\"><h3>AI 聚合审查</h3><div class=\"spinner-wrap\"><div class=\"spinner\"></div><div class=\"spinner-text\">AI 正在审查所有交付物...</div></div></div>';\n document.body.appendChild(ov);\n try{\n const r=await fetch(API+'/jobs/'+jobId+'/review',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'审查失败',false);ov.remove();return}\n const fc=ov.querySelector('.form-card');\n fc.innerHTML='<h3>AI 聚合审查</h3>'\n +'<div style=\"font-size:12px;color:var(--text-dim);margin-bottom:12px\">需求: '+esc(d.original_prompt)+'</div>'\n +'<div class=\"md-editor\"><div class=\"result-display\" style=\"max-height:400px\">'+esc(d.review).replace(/\\\\n/g,'<br>')+'</div></div>'\n +'<div style=\"font-size:10px;color:var(--text-dim);margin-top:8px;font-family:var(--font-mono)\">审查时间: '+new Date(d.reviewed_at).toLocaleString()+'</div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">关闭</button></div>';\n }catch(e){toast('网络错误: '+e.message,false);ov.remove()}\n};\n\nwindow.showEvalDialog=function(jobId){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n ov.innerHTML='<div class=\"form-card\"><h3>&#128202; 生成绩效评价</h3>'\n +'<div class=\"fg\"><label>评分体系</label><select id=\"eval-system\">'\n +'<option value=\"ali\">阿里绩效 (3.25 / 3.5 / 3.5+ / 3.75 / 4.0)</option>'\n +'<option value=\"letter\">SABCD 等级 (S / A / B / C / D)</option>'\n +'<option value=\"em\">EM/MM 体系 (EM+ / EM / MM+ / MM / MM-)</option>'\n +'</select></div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-primary\" onclick=\"generateEval(\\\\''+jobId+'\\\\')\">生成评价</button><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button></div></div>';\n document.body.appendChild(ov);\n};\nwindow.generateEval=async function(jobId){\n const ratingSystem=document.getElementById('eval-system').value;\n const ov=document.getElementById('overlay');\n const fc=ov.querySelector('.form-card');\n fc.innerHTML='<h3>&#128202; 生成绩效评价</h3><div class=\"spinner-wrap\"><div class=\"spinner\"></div><div class=\"spinner-text\">AI 正在评估每个碳基节点的表现...</div></div>';\n try{\n const r=await fetch(API+'/evaluations/generate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({job_id:jobId,rating_system:ratingSystem})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'评价生成失败',false);ov.remove();return}\n let h='<h3>&#128202; 绩效评价结果</h3>';\n h+='<div style=\"font-size:11px;color:var(--text-dim);margin-bottom:16px;font-family:var(--font-mono)\">生成时间: '+new Date(d.generated_at).toLocaleString()+'</div>';\n const ratingTiers={ali:['4.0','3.75'],letter:['S','A'],em:['EM+','EM']};\n const lowTiers={ali:['3.25'],letter:['D'],em:['MM-']};\n for(const ev of d.evaluations){\n const agent=cachedAgents.find(a=>a.agent_id===ev.agent_id);\n const isTop=(ratingTiers[ratingSystem]||[]).includes(ev.rating);\n const isLow=(lowTiers[ratingSystem]||[]).includes(ev.rating);\n const tier=isTop?'top':isLow?'low':'mid';\n h+='<div style=\"padding:12px;background:var(--bg);border:1px solid var(--border);border-radius:8px;margin-bottom:8px\">';\n h+='<div style=\"display:flex;align-items:center;gap:10px;margin-bottom:6px\"><span style=\"font-weight:600;font-size:13px\">'+esc(agent?agent.name:ev.agent_id)+'</span><span class=\"eval-badge '+tier+'\">'+esc(ev.rating)+'</span></div>';\n h+='<div style=\"font-size:12px;color:var(--text-dim)\">'+esc(ev.comment)+'</div>';\n h+='<div style=\"font-size:10px;color:var(--text-dim);margin-top:4px;font-family:var(--font-mono)\">任务: '+ev.trace_id+'</div>';\n h+='</div>';\n }\n h+='<div class=\"btn-group\"><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">关闭</button></div>';\n fc.innerHTML=h;\n }catch(e){toast('网络错误: '+e.message,false);ov.remove()}\n};\n\n// ─── Task Detail Modal ──────────────────────\nwindow.showTaskDetail=function(traceId){\n const t=allTasks.find(x=>x.trace_id===traceId);\n if(!t)return;\n const agent=cachedAgents.find(a=>a.agent_id===t.assignee_id);\n const agentName=agent?agent.name:t.assignee_id;\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n\n let h='<div class=\"form-card\"><div class=\"task-detail-hd\"><span class=\"task-detail-status '+t.status+'\">'+t.status+'</span><span style=\"font-size:15px;font-weight:600\">任务详情</span></div>';\n\n // Trace ID with copy\n h+='<div class=\"trace-copy-row\"><span>'+t.trace_id+'</span><button onclick=\"navigator.clipboard.writeText(\\\\''+t.trace_id+'\\\\').then(()=>toast(\\\\'已复制\\\\',true))\">复制</button></div>';\n\n // Info rows\n h+='<div class=\"detail-row\"><div class=\"detail-label\">指派节点</div><div class=\"detail-value\">'+esc(agentName)+' <span style=\"color:var(--text-dim);font-family:var(--font-mono);font-size:11px\">'+t.assignee_id+'</span></div></div>';\n h+='<div class=\"detail-row\"><div class=\"detail-label\">任务描述</div><div class=\"detail-value\">'+esc(t.todo_description)+'</div></div>';\n h+='<div class=\"detail-row\"><div class=\"detail-label\">截止时间</div><div class=\"detail-value\" style=\"font-family:var(--font-mono)\">'+new Date(t.deadline).toLocaleString()+'</div></div>';\n\n if(t.status==='RESOLVED'&&t.result_data){\n // Show result + reject option (manager can reject after reviewing)\n let resultText='';\n if(typeof t.result_data==='object'&&t.result_data){resultText=t.result_data.text||JSON.stringify(t.result_data,null,2)}else{resultText=String(t.result_data||'')}\n h+='<div class=\"detail-row\"><div class=\"detail-label\">交付结果</div><div class=\"md-editor\"><div class=\"result-display\">'+esc(resultText)+'</div></div></div>';\n h+='<div class=\"btn-group\"><button class=\"btn btn-danger btn-sm\" onclick=\"taskReject(\\\\''+t.trace_id+'\\\\')\">打回重做 (Reject)</button><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">关闭</button></div>';\n }else{\n // Input for resume (no reject for unsubmitted tasks)\n h+='<div class=\"fg\" style=\"margin-top:16px\"><label>提交 Human 交付物</label><div class=\"md-editor\"><textarea id=\"td-payload\" rows=\"4\" placeholder=\"粘贴交付物内容、GitHub PR/Commit URL、工作汇报等...\"></textarea><button class=\"md-expand-btn\" onclick=\"toggleFullscreen(this)\" title=\"展开/收起\">&#x26F6;</button></div><div class=\"hint\">支持贴 GitHub URL(PR、Commit、Issue),AI 审查时会分析</div></div>';\n h+='<div class=\"btn-group\">';\n h+='<button class=\"btn btn-primary btn-sm\" onclick=\"simulateDelivery(\\\\''+t.trace_id+'\\\\')\">&#129302; 模拟交付</button>';\n h+='<button class=\"btn btn-green\" onclick=\"taskResume(\\\\''+t.trace_id+'\\\\')\">提交交付 (Resume)</button>';\n h+='<button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button>';\n h+='</div>';\n }\n h+='</div>';\n ov.innerHTML=h;\n document.body.appendChild(ov);\n};\n\nwindow.taskResume=async function(traceId){\n const payload=document.getElementById('td-payload').value.trim();\n if(!payload){toast('请输入交付内容',false);return}\n try{\n const r=await fetch(API+'/tasks/resume',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:traceId,result_data:{text:payload}})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'提交失败',false);return}\n toast(d.job_complete?'任务已交付!Job 已全部完成,可以聚合同步。':'任务 '+traceId+' 已交付。',true);\n document.getElementById('overlay').remove();\n load('pipeline');\n }catch{toast('网络错误',false)}\n};\n\nwindow.taskReject=async function(traceId){\n if(!confirm('确定打回任务 '+traceId+'?截止时间将延长 24 小时。'))return;\n try{\n const r=await fetch(API+'/tasks/reject',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:traceId})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'操作失败',false);return}\n toast('任务 '+traceId+' 已打回,截止时间已延长 24 小时。',true);\n document.getElementById('overlay').remove();\n load('pipeline');\n }catch{toast('网络错误',false)}\n};\n\nwindow.simulateDelivery=async function(traceId){\n const btn=event.target;\n const origText=btn.innerHTML;\n btn.disabled=true;btn.innerHTML='&#8987; AI 生成中...';\n try{\n const r=await fetch(API+'/tasks/simulate',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:traceId})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'模拟失败',false);btn.disabled=false;btn.innerHTML=origText;return}\n const ta=document.getElementById('td-payload');\n if(ta)ta.value=d.simulated_delivery;\n toast('已生成模拟交付内容',true);\n btn.disabled=false;btn.innerHTML=origText;\n }catch(e){toast('网络错误: '+e.message,false);btn.disabled=false;btn.innerHTML=origText}\n};\n\n// ═══════════════════════════════════════════════\n// TERMINAL\n// ═══════════════════════════════════════════════\nfunction loadTerminal(el){\n el.innerHTML='<div class=\"section-hd\"><h2>I/O 交付终端</h2></div>'\n +'<div class=\"form-card\" style=\"margin-top:12px\"><h3>> 提交碳基节点产出</h3>'\n +'<div class=\"fg\"><label>Trace ID (追踪码)</label><input id=\"t-tid\" placeholder=\"例: TK-9527\"/></div>'\n +'<div class=\"fg\"><label>交付载荷</label><div class=\"md-editor\"><textarea id=\"t-payload\" placeholder=\"粘贴交付物内容、GitHub PR/Commit URL、工作汇报等...\"></textarea><button class=\"md-expand-btn\" onclick=\"toggleFullscreen(this)\" title=\"展开/收起\">&#x26F6;</button></div><div class=\"hint\">支持贴 GitHub URL(PR、Commit、Issue),AI 审查时会分析</div></div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-primary\" onclick=\"doResume()\">提交并恢复 (Resume)</button><button class=\"btn btn-danger\" onclick=\"doReject()\">打回重做 (Reject)</button></div></div>';\n}\nwindow.doResume=async function(){\n const tid=document.getElementById('t-tid').value.trim();\n const payload=document.getElementById('t-payload').value.trim();\n if(!tid){toast('请输入 Trace ID',false);return}\n if(!payload){toast('请输入交付载荷',false);return}\n try{\n const r=await fetch(API+'/tasks/resume',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:tid,result_data:{text:payload}})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'提交失败',false);return}\n toast(d.job_complete?'任务已交付!Job 已全部完成,可以聚合同步。':'任务 '+tid+' 已交付。',true);\n document.getElementById('t-tid').value='';document.getElementById('t-payload').value='';\n }catch{toast('网络错误',false)}\n};\nwindow.doReject=async function(){\n const tid=document.getElementById('t-tid').value.trim();\n if(!tid){toast('请输入 Trace ID',false);return}\n try{\n const r=await fetch(API+'/tasks/reject',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({trace_id:tid})});\n const d=await r.json();\n if(!r.ok){toast(d.error||'操作失败',false);return}\n toast('任务 '+tid+' 已打回,截止时间已延长 24 小时。',true);\n document.getElementById('t-tid').value='';document.getElementById('t-payload').value='';\n }catch{toast('网络错误',false)}\n};\n\n// ═══════════════════════════════════════════════\n// SETTINGS\n// ═══════════════════════════════════════════════\nwindow.showSettings=async function(){\n const ov=document.createElement('div');ov.className='overlay';ov.id='overlay';\n ov.addEventListener('click',e=>{if(e.target===ov)ov.remove()});\n ov.innerHTML='<div class=\"form-card\"><h3>LLM 设置</h3><div class=\"spinner-wrap\"><div class=\"spinner\"></div></div></div>';\n document.body.appendChild(ov);\n\n try{\n const r=await fetch(API+'/config');\n const cfg=await r.json();\n const fc=ov.querySelector('.form-card');\n let statusHtml='';\n if(cfg.api_key_set){\n const src=cfg.api_key_source==='dashboard'?'Dashboard 配置':'环境变量';\n statusHtml='<div style=\"background:var(--accent-dim);border:1px solid color-mix(in srgb,var(--accent) 25%,transparent);border-radius:8px;padding:10px 14px;margin-bottom:16px;font-size:12px\"><span style=\"color:var(--green)\">&#10003;</span> API Key 已配置 <span style=\"color:var(--text-dim)\">('+esc(cfg.api_key_masked)+' | 来源: '+src+')</span></div>';\n }else{\n statusHtml='<div style=\"background:rgba(239,68,68,.1);border:1px solid rgba(239,68,68,.25);border-radius:8px;padding:10px 14px;margin-bottom:16px;font-size:12px;color:var(--red)\">&#9888; 未配置 API Key,AI 规划功能不可用</div>';\n }\n fc.innerHTML='<h3>LLM 设置</h3>'+statusHtml\n +'<div class=\"fg\"><label>API 格式</label><select id=\"cfg-provider\"><option value=\"anthropic\"'+(cfg.provider==='anthropic'||cfg.provider==='claude'?' selected':'')+'>Anthropic Messages API</option><option value=\"openai\"'+(cfg.provider==='openai'?' selected':'')+'>OpenAI Chat Completions API</option><option value=\"responses\"'+(cfg.provider==='responses'?' selected':'')+'>OpenAI Responses API</option></select><div class=\"hint\">选择 API 格式。自定义 Base URL 可连接任何兼容服务。</div></div>'\n +'<div class=\"fg\"><label>API Key</label><input id=\"cfg-key\" type=\"password\" placeholder=\"'+(cfg.api_key_set?'已配置,留空则不修改':'输入你的 API Key...')+'\"/><div class=\"hint\">Anthropic: sk-ant-... | OpenAI: sk-...</div></div>'\n +'<div class=\"fg\"><label>模型 <span style=\"color:var(--text-dim);font-weight:400\">(可选,留空用默认)</span></label><input id=\"cfg-model\" value=\"'+esc(cfg.model||'')+'\" placeholder=\"例: claude-sonnet-4-20250514 / gpt-4o\"/></div>'\n +'<div class=\"fg\"><label>API Base URL <span style=\"color:var(--text-dim);font-weight:400\">(可选,留空用官方地址)</span></label><input id=\"cfg-baseurl\" value=\"'+esc(cfg.base_url||'')+'\" placeholder=\"例: https://your-proxy.com\"/><div class=\"hint\">私有部署: 填写你的模型服务地址,如 vLLM / Ollama / Azure 等</div></div>'\n +'<div class=\"btn-group\"><button class=\"btn btn-primary\" onclick=\"saveSettings()\">保存</button><button class=\"btn btn-ghost\" onclick=\"document.getElementById(\\\\'overlay\\\\').remove()\">取消</button></div>';\n }catch{\n ov.querySelector('.form-card').innerHTML='<h3>LLM 设置</h3><p style=\"color:var(--red)\">加载配置失败</p>';\n }\n};\nwindow.saveSettings=async function(){\n const provider=document.getElementById('cfg-provider').value;\n const apiKey=document.getElementById('cfg-key').value.trim();\n const model=document.getElementById('cfg-model').value.trim();\n const baseUrl=document.getElementById('cfg-baseurl').value.trim();\n const body={provider};\n if(apiKey)body.api_key=apiKey;\n body.model=model;\n body.base_url=baseUrl;\n try{\n const r=await fetch(API+'/config',{method:'PUT',headers:{'Content-Type':'application/json'},body:JSON.stringify(body)});\n if(!r.ok){const d=await r.json();toast(d.error||'保存失败',false);return}\n toast('设置已保存',true);\n document.getElementById('overlay').remove();\n }catch{toast('网络错误',false)}\n};\n\n// ═══════════════════════════════════════════════\n// TABS & INIT\n// ═══════════════════════════════════════════════\nconst tabs=document.querySelectorAll('.tab');\nconst panels=document.querySelectorAll('.panel');\nfunction load(id){\n const el=document.getElementById(id);\n if(id==='fleet')loadFleet(el);\n else if(id==='pipeline')loadPipeline(el);\n else if(id==='terminal')loadTerminal(el);\n}\ntabs.forEach(t=>t.addEventListener('click',()=>{\n tabs.forEach(x=>x.classList.remove('active'));\n panels.forEach(x=>x.classList.remove('active'));\n t.classList.add('active');\n const id=t.dataset.panel;\n document.getElementById(id).classList.add('active');\n load(id);\n}));\nload('fleet');\nsetInterval(()=>{\n const a=document.querySelector('.tab.active');\n if(a&&a.dataset.panel!=='terminal')load(a.dataset.panel);\n},15000);\n</script>\n</body>\n</html>`;\n}\n"],"mappings":"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AA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["db","db","Router","db","db","db","db","db","db","db","DEFAULT_BASE_URL","DEFAULT_BASE_URL","db","db","router","Router","Router","db","db","router","Router","Router","db","db","db","router","Router","Router","router","Router","Router","router","Router","Router","extractJson","db","router","Router","db","db","confirm"]}