agentix-cli 0.14.0 → 0.15.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/README.md +64 -34
- package/dist/agent-LRSSVFDR.js +2 -0
- package/dist/board-dashboard-DAPHL6W2.js +4508 -0
- package/dist/board-dashboard-DAPHL6W2.js.map +1 -0
- package/dist/chunk-3XULOWYB.js +199 -0
- package/dist/chunk-3XULOWYB.js.map +1 -0
- package/dist/{chunk-ZRYBSI5G.js → chunk-7MBTMCKY.js} +2 -2
- package/dist/{chunk-XVYMZ4M7.js → chunk-AU37OREL.js} +5 -5
- package/dist/chunk-B4Q4X7D5.js +6 -0
- package/dist/chunk-B4Q4X7D5.js.map +1 -0
- package/dist/chunk-CXY2RYNI.js +11 -0
- package/dist/chunk-CXY2RYNI.js.map +1 -0
- package/dist/chunk-FIOB3MEX.js +2 -0
- package/dist/chunk-HLRVDCNH.js +5 -0
- package/dist/{chunk-NJR5WF5E.js.map → chunk-HLRVDCNH.js.map} +1 -1
- package/dist/chunk-IEVEMKAI.js +168 -0
- package/dist/chunk-IEVEMKAI.js.map +1 -0
- package/dist/{chunk-O72LPMBR.js → chunk-RB7265LO.js} +2 -2
- package/dist/chunk-RGUYBH77.js +7 -0
- package/dist/chunk-RGUYBH77.js.map +1 -0
- package/dist/chunk-WVNSWBCU.js +138 -0
- package/dist/chunk-WVNSWBCU.js.map +1 -0
- package/dist/cli.js +34 -24
- package/dist/cli.js.map +1 -1
- package/dist/{compaction-7I3E5QUO.js → compaction-JBZZ7I7Y.js} +4 -4
- package/dist/config-DCJYECL4.js +2 -0
- package/dist/config-mutator-6EJHSP3Y.js +2 -0
- package/dist/debug-3MEUO2P2.js +2 -0
- package/dist/{heal-OTGT5HHJ.js → heal-WKY5I4KB.js} +2 -2
- package/dist/index.d.ts +224 -1
- package/dist/index.js +11 -11
- package/dist/index.js.map +1 -1
- package/dist/loader-PTCXLJEX.js +2 -0
- package/dist/memory-extract-AD7KMRPC.js +2 -0
- package/dist/{providers-G3WZ4RVJ.js → providers-VUIWO3ZK.js} +2 -2
- package/dist/registry-5ND7GLFJ.js +2 -0
- package/dist/scheduler-FCXOXCN5.js +2 -0
- package/dist/{subagent-WV7QKQ3L.js → subagent-2SJHSSJS.js} +2 -2
- package/dist/{usage-dashboard-M7AFIGMJ.js → usage-dashboard-QX3CF6KK.js} +8 -8
- package/dist/usage-dashboard-QX3CF6KK.js.map +1 -0
- package/dist/whatsapp-state-6D6FHHD2.js +2 -0
- package/dist/whatsapp-state-6D6FHHD2.js.map +1 -0
- package/dist/workspace-setup-ZWTT42XT.js +2 -0
- package/dist/workspace-setup-ZWTT42XT.js.map +1 -0
- package/package.json +2 -1
- package/dist/agent-DW3NYIIL.js +0 -2
- package/dist/board-dashboard-6TKE6NMV.js +0 -1435
- package/dist/board-dashboard-6TKE6NMV.js.map +0 -1
- package/dist/chunk-4YCH6IZV.js +0 -2
- package/dist/chunk-DYOBFJLX.js +0 -11
- package/dist/chunk-DYOBFJLX.js.map +0 -1
- package/dist/chunk-ITZEVXZ4.js +0 -2
- package/dist/chunk-ITZEVXZ4.js.map +0 -1
- package/dist/chunk-M33CB2H4.js +0 -6
- package/dist/chunk-M33CB2H4.js.map +0 -1
- package/dist/chunk-NJR5WF5E.js +0 -5
- package/dist/chunk-RB73FZXJ.js +0 -133
- package/dist/chunk-RB73FZXJ.js.map +0 -1
- package/dist/chunk-UPT2UMF6.js +0 -159
- package/dist/chunk-UPT2UMF6.js.map +0 -1
- package/dist/config-7QJ2QJIC.js +0 -2
- package/dist/config-mutator-GAEVGHDD.js +0 -2
- package/dist/debug-N3B5NVJU.js +0 -2
- package/dist/loader-YQE6XJBT.js +0 -2
- package/dist/memory-extract-RGPUPMKU.js +0 -2
- package/dist/registry-YVAG4EO5.js +0 -2
- package/dist/usage-dashboard-M7AFIGMJ.js.map +0 -1
- package/dist/workspace-setup-QQTCJY4B.js +0 -2
- /package/dist/{agent-DW3NYIIL.js.map → agent-LRSSVFDR.js.map} +0 -0
- /package/dist/{chunk-ZRYBSI5G.js.map → chunk-7MBTMCKY.js.map} +0 -0
- /package/dist/{chunk-XVYMZ4M7.js.map → chunk-AU37OREL.js.map} +0 -0
- /package/dist/{chunk-4YCH6IZV.js.map → chunk-FIOB3MEX.js.map} +0 -0
- /package/dist/{chunk-O72LPMBR.js.map → chunk-RB7265LO.js.map} +0 -0
- /package/dist/{compaction-7I3E5QUO.js.map → compaction-JBZZ7I7Y.js.map} +0 -0
- /package/dist/{config-7QJ2QJIC.js.map → config-DCJYECL4.js.map} +0 -0
- /package/dist/{config-mutator-GAEVGHDD.js.map → config-mutator-6EJHSP3Y.js.map} +0 -0
- /package/dist/{debug-N3B5NVJU.js.map → debug-3MEUO2P2.js.map} +0 -0
- /package/dist/{heal-OTGT5HHJ.js.map → heal-WKY5I4KB.js.map} +0 -0
- /package/dist/{loader-YQE6XJBT.js.map → loader-PTCXLJEX.js.map} +0 -0
- /package/dist/{memory-extract-RGPUPMKU.js.map → memory-extract-AD7KMRPC.js.map} +0 -0
- /package/dist/{providers-G3WZ4RVJ.js.map → providers-VUIWO3ZK.js.map} +0 -0
- /package/dist/{registry-YVAG4EO5.js.map → registry-5ND7GLFJ.js.map} +0 -0
- /package/dist/{workspace-setup-QQTCJY4B.js.map → scheduler-FCXOXCN5.js.map} +0 -0
- /package/dist/{subagent-WV7QKQ3L.js.map → subagent-2SJHSSJS.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/daemon/board-dashboard.ts","../src/daemon/vendor/sortable.ts"],"sourcesContent":["import { createServer, type IncomingMessage, type ServerResponse } from \"http\"\nimport { appendFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\nimport type { DaemonConfig } from \"./config\"\nimport type { BoardConfig, BoardColumn } from \"@/boards/config\"\nimport { deriveStage, transitionDiff } from \"@/boards/config\"\nimport type { WorkSource, WorkItem } from \"@/business/work-pool\"\nimport { GitLabWorkSource } from \"@/business/work-pool\"\nimport { SORTABLE_JS } from \"./vendor/sortable\"\n\n// --- Kanban Board Dashboard ---\n//\n// Zero-build web UI, served on its own port (default 4202, bound 127.0.0.1).\n// Mirrors src/daemon/usage-dashboard.ts — inline HTML/CSS/JS, manual routing,\n// no framework.\n//\n// Column kinds (see src/boards/config.ts):\n// - open-backlog: opened issues with no Status::* scoped label\n// - scoped-label: opened issues with a specific Status::X label\n// - closed: closed issues (bounded by closedWindowDays)\n// - label: opened issues with a flat mapsToLabel (legacy)\n//\n// Drag-drop produces a ColumnTransition that may: add/remove labels, close,\n// or reopen an issue (via GitLab `state_event`).\n\nexport function startBoardDashboard(config: DaemonConfig): void {\n const dashboard = config.dashboard\n const boards = config.boards\n if (boards.length === 0) {\n console.log(\" No boards configured in agentx.json (boards[]). Nothing to serve.\")\n return\n }\n\n const port = dashboard.port\n const bind = dashboard.bind\n const token = dashboard.token\n\n const sources = new Map<string, WorkSource>()\n for (const board of boards) {\n try {\n sources.set(board.id, buildWorkSource(board, config))\n } catch (e: any) {\n console.error(` Board \"${board.id}\" failed to initialize: ${e.message}`)\n }\n }\n\n const server = createServer(async (req, res) => {\n try {\n await handleRequest(req, res, { boards, sources, token, config })\n } catch (e: any) {\n sendJson(res, 500, { error: e.message || \"internal error\" })\n }\n })\n\n server.listen(port, bind, () => {\n const displayHost = bind === \"0.0.0.0\" ? \"localhost\" : bind\n console.log(`\\n Kanban dashboard: http://${displayHost}:${port}\\n`)\n console.log(` Boards: ${boards.map((b) => b.id).join(\", \")}`)\n if (bind === \"127.0.0.1\" && !token) {\n console.log(\" (bound to localhost; no auth token — safe default)\")\n } else if (bind !== \"127.0.0.1\" && !token) {\n console.log(\" ⚠ bound to a non-loopback address without a token — writes are UNAUTHENTICATED\")\n }\n })\n}\n\ninterface Ctx {\n boards: BoardConfig[]\n sources: Map<string, WorkSource>\n token?: string\n config: DaemonConfig\n}\n\nasync function handleRequest(req: IncomingMessage, res: ServerResponse, ctx: Ctx): Promise<void> {\n const url = new URL(req.url || \"/\", \"http://localhost\")\n const path = url.pathname\n const method = (req.method || \"GET\").toUpperCase()\n\n res.setHeader(\"Access-Control-Allow-Origin\", \"*\")\n res.setHeader(\"Access-Control-Allow-Methods\", \"GET, POST, PATCH, OPTIONS\")\n res.setHeader(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization, X-Requested-With\")\n if (method === \"OPTIONS\") { res.writeHead(204); res.end(); return }\n\n if (method === \"GET\" && path === \"/\") {\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" })\n res.end(renderBoardHtml())\n return\n }\n if (method === \"GET\" && path === \"/live\") {\n res.writeHead(200, { \"Content-Type\": \"text/html; charset=utf-8\" })\n res.end(renderLiveHtml())\n return\n }\n if (method === \"GET\" && path === \"/sortable.min.js\") {\n res.writeHead(200, { \"Content-Type\": \"application/javascript; charset=utf-8\", \"Cache-Control\": \"public, max-age=86400\" })\n res.end(SORTABLE_JS)\n return\n }\n\n // Live mesh view endpoints (JSON snapshot + SSE push every 2s)\n if (method === \"GET\" && path === \"/api/live\") {\n try {\n const snap = await buildLiveSnapshot(ctx.config)\n sendJson(res, 200, snap)\n } catch (e: any) { sendJson(res, 502, { error: e.message }) }\n return\n }\n if (method === \"GET\" && path === \"/api/live/stream\") {\n startLiveStream(req, res, ctx.config)\n return\n }\n\n // Agent roster (proxy to configured daemon) — used by draft-agent picker.\n if (method === \"GET\" && path === \"/api/agents\") {\n try {\n const headers: Record<string, string> = {}\n if (ctx.config.dashboard.token) headers[\"Authorization\"] = `Bearer ${ctx.config.dashboard.token}`\n const r = await fetch(ctx.config.dashboard.daemonUrl.replace(/\\/+$/, \"\") + \"/agents\", { headers })\n if (!r.ok) throw new Error(`HTTP ${r.status}`)\n const list = await r.json()\n sendJson(res, 200, list)\n } catch (e: any) { sendJson(res, 502, { error: e.message || \"agents fetch failed\" }) }\n return\n }\n\n // AI-assisted issue draft — proxies to daemon /task with convention-aware prompt.\n if (method === \"POST\" && path === \"/api/draft\") {\n const body = await readJson(req)\n try {\n const drafted = await draftIssue(ctx.config, body)\n sendJson(res, 200, drafted)\n } catch (e: any) { sendJson(res, 502, { error: e.message || \"draft failed\" }) }\n return\n }\n\n if (path.startsWith(\"/api/\") && ctx.token) {\n const authHeader = req.headers.authorization || \"\"\n const got = authHeader.startsWith(\"Bearer \") ? authHeader.slice(7) : \"\"\n if (got !== ctx.token) { sendJson(res, 401, { error: \"unauthorized\" }); return }\n }\n\n const isWrite = method === \"POST\" || method === \"PATCH\" || method === \"DELETE\"\n if (isWrite && path.startsWith(\"/api/\")) {\n const xr = req.headers[\"x-requested-with\"]\n if (xr !== \"agentx-board\") { sendJson(res, 400, { error: \"missing X-Requested-With: agentx-board\" }); return }\n }\n\n if (method === \"GET\" && path === \"/api/boards\") {\n sendJson(res, 200, ctx.boards.map((b) => ({\n id: b.id, name: b.name, source: b.source,\n primaryToolLabel: b.primaryToolLabel, labels: b.labels,\n columns: b.columns, timeRangeDays: b.timeRangeDays,\n })))\n return\n }\n\n // /api/boards/:id/members — project members (for assignee picker)\n const membersMatch = path.match(/^\\/api\\/boards\\/([a-z0-9_-]+)\\/members$/)\n if (membersMatch && method === \"GET\") {\n const source = ctx.sources.get(membersMatch[1]) as GitLabWorkSource | undefined\n if (!source) { sendJson(res, 404, { error: \"unknown board\" }); return }\n try {\n const members = typeof source.listMembers === \"function\" ? await source.listMembers() : []\n sendJson(res, 200, members)\n } catch (e: any) { sendJson(res, 502, { error: e.message }) }\n return\n }\n\n // /api/boards/:id/milestones — project milestones (for milestone picker)\n const milestonesMatch = path.match(/^\\/api\\/boards\\/([a-z0-9_-]+)\\/milestones$/)\n if (milestonesMatch && method === \"GET\") {\n const source = ctx.sources.get(milestonesMatch[1]) as GitLabWorkSource | undefined\n if (!source) { sendJson(res, 404, { error: \"unknown board\" }); return }\n try {\n const ms = typeof source.listMilestones === \"function\" ? await source.listMilestones() : []\n sendJson(res, 200, ms)\n } catch (e: any) { sendJson(res, 502, { error: e.message }) }\n return\n }\n\n // /api/boards/:id/items[/...]\n // Legacy shape: /items/:itemId/move (move-only). New shape: /items/:itemId (GET/PATCH), /items (POST, GET).\n const moveMatch = path.match(/^\\/api\\/boards\\/([a-z0-9_-]+)\\/items\\/(.+?)\\/move$/)\n const itemOpMatch = path.match(/^\\/api\\/boards\\/([a-z0-9_-]+)\\/items(?:\\/(.+))?$/)\n\n if (moveMatch && method === \"PATCH\") {\n const [, boardId, itemId] = moveMatch\n const board = ctx.boards.find((b) => b.id === boardId)\n if (!board) { sendJson(res, 404, { error: `unknown board: ${boardId}` }); return }\n const source = ctx.sources.get(boardId)\n if (!source) { sendJson(res, 503, { error: `board \"${boardId}\" source unavailable` }); return }\n const body = await readJson(req)\n const toCol = board.columns.find((c) => c.id === body.to)\n const fromCol = board.columns.find((c) => c.id === body.from)\n if (!toCol) { sendJson(res, 400, { error: `unknown 'to' column: ${body.to}` }); return }\n try {\n await applyTransition(source, itemId, fromCol, toCol)\n audit({ actor: auditActor(req), action: \"move\", boardId, itemId, payload: { from: body.from, to: body.to } })\n sendJson(res, 200, { ok: true })\n } catch (e: any) { sendJson(res, 502, { error: e.message || \"transition failed\" }) }\n return\n }\n\n if (itemOpMatch) {\n const [, boardId, itemId] = itemOpMatch\n const board = ctx.boards.find((b) => b.id === boardId)\n if (!board) { sendJson(res, 404, { error: `unknown board: ${boardId}` }); return }\n const source = ctx.sources.get(boardId) as GitLabWorkSource | undefined\n if (!source) { sendJson(res, 503, { error: `board \"${boardId}\" source unavailable` }); return }\n\n // GET /items — column dump\n if (method === \"GET\" && !itemId) {\n const { columns, totals } = await loadBoardColumns(board, source, url.searchParams)\n sendJson(res, 200, { columns, totals })\n return\n }\n\n // GET /items/:itemId — full detail\n if (method === \"GET\" && itemId) {\n try {\n const detail = typeof source.getItem === \"function\" ? await source.getItem(itemId) : null\n if (!detail) { sendJson(res, 404, { error: \"not found\" }); return }\n sendJson(res, 200, detail)\n } catch (e: any) { sendJson(res, 502, { error: e.message }) }\n return\n }\n\n // PATCH /items/:itemId — update\n if (method === \"PATCH\" && itemId) {\n const body = await readJson(req)\n try {\n const updated = typeof source.updateItem === \"function\"\n ? await source.updateItem(itemId, body)\n : null\n if (!updated) { sendJson(res, 501, { error: \"update unsupported\" }); return }\n audit({ actor: auditActor(req), action: \"update\", boardId, itemId, payload: body })\n sendJson(res, 200, updated)\n } catch (e: any) { sendJson(res, 502, { error: e.message }) }\n return\n }\n\n // POST /items — create new issue\n if (method === \"POST\" && !itemId) {\n const body = await readJson(req)\n if (!body.title) { sendJson(res, 400, { error: \"title required\" }); return }\n if (!source.capabilities.create || !source.create) {\n sendJson(res, 400, { error: `source ${source.type} does not support create` }); return\n }\n try {\n const created = await source.create({\n title: body.title,\n description: body.description,\n labels: body.labels,\n assigneeUsernames: body.assigneeUsernames,\n milestoneTitle: body.milestoneTitle,\n projectHint: body.projectHint,\n })\n audit({ actor: auditActor(req), action: \"create\", boardId, itemId: created.id, payload: { title: body.title } })\n sendJson(res, 201, created)\n } catch (e: any) { sendJson(res, 502, { error: e.message }) }\n return\n }\n }\n\n sendJson(res, 404, { error: \"not found\", path })\n}\n\n// --- Column loading ---\n\nasync function loadBoardColumns(\n board: BoardConfig,\n source: WorkSource,\n query: URLSearchParams,\n): Promise<{ columns: Record<string, WorkItem[]>; totals: Record<string, number> }> {\n if (!source.capabilities.listAll || !source.listAll) {\n const empty: Record<string, WorkItem[]> = {}\n for (const c of board.columns) empty[c.id] = []\n return { columns: empty, totals: Object.fromEntries(board.columns.map((c) => [c.id, 0])) }\n }\n\n const baseLabels: string[] = []\n if (board.primaryToolLabel) baseLabels.push(board.primaryToolLabel)\n const extraLabel = query.get(\"label\") || undefined\n if (extraLabel) baseLabels.push(extraLabel)\n const search = query.get(\"q\") || undefined\n const milestone = query.get(\"milestone\") || undefined\n const assignee = query.get(\"assignee\") || undefined\n const openDays = parseInt(query.get(\"days\") || String(board.timeRangeDays), 10)\n const closedDays = parseInt(query.get(\"closedDays\") || String(board.closedWindowDays ?? 30), 10)\n\n // One query per column kind type. We batch all scoped-label/label/open-backlog\n // columns into a single \"opened\" query (client-side bucket them), and the\n // closed column (if any) into a second \"closed\" query.\n const hasOpen = board.columns.some((c) => c.kind !== \"closed\")\n const hasClosed = board.columns.some((c) => c.kind === \"closed\")\n\n const open: WorkItem[] = hasOpen\n ? await source.listAll({\n sinceDays: openDays, labels: baseLabels.length ? baseLabels : undefined,\n search, milestone, state: \"opened\",\n })\n : []\n const closed: WorkItem[] = hasClosed\n ? await source.listAll({\n sinceDays: closedDays, labels: baseLabels.length ? baseLabels : undefined,\n search, milestone, state: \"closed\",\n })\n : []\n\n const columns: Record<string, WorkItem[]> = {}\n for (const c of board.columns) columns[c.id] = []\n\n // Closed items → the closed column (if any).\n const closedCol = board.columns.find((c) => c.kind === \"closed\")\n for (const item of closed) {\n if (closedCol) columns[closedCol.id].push({ ...item, stage: closedCol.id })\n }\n\n // Open items → bucket by deriveStage over the open columns only.\n // Items with a scoped Status::* label that no column claims are dropped\n // (they're off-board workflow states like Status::Done when no Done column exists).\n const openColumns = board.columns.filter((c) => c.kind !== \"closed\")\n for (const item of open) {\n const stage = deriveStage(item, openColumns)\n if (!stage) continue\n const list = columns[stage]\n if (list) list.push({ ...item, stage })\n }\n\n // Assignee filter (post-query).\n if (assignee) {\n for (const k of Object.keys(columns)) {\n columns[k] = columns[k].filter((i) => i.assignee === assignee)\n }\n }\n\n // Deterministic in-column ordering: priority asc, updatedAt desc.\n for (const k of Object.keys(columns)) {\n columns[k].sort((a, b) => {\n const p = (a.priority ?? 99) - (b.priority ?? 99)\n if (p !== 0) return p\n return (b.updatedAt || \"\").localeCompare(a.updatedAt || \"\")\n })\n }\n\n const totals = Object.fromEntries(Object.entries(columns).map(([k, v]) => [k, v.length]))\n return { columns, totals }\n}\n\nasync function applyTransition(\n source: WorkSource,\n itemId: string,\n fromCol: BoardColumn | undefined,\n toCol: BoardColumn,\n): Promise<void> {\n const diff = transitionDiff(fromCol, toCol)\n const gl = source as GitLabWorkSource\n // Close/reopen first (so the issue exists in the right state before label edits).\n if (diff.closeIssue && typeof gl.setState === \"function\") {\n await gl.setState(itemId, \"close\")\n } else if (diff.reopen && typeof gl.setState === \"function\") {\n await gl.setState(itemId, \"reopen\")\n }\n if ((diff.addLabels?.length || diff.removeLabels?.length) && typeof gl.transitionMany === \"function\") {\n await gl.transitionMany(itemId, diff.addLabels || [], diff.removeLabels || [])\n } else if (diff.addLabels?.length && source.transition) {\n await source.transition(itemId, diff.addLabels[0], diff.removeLabels?.[0])\n }\n}\n\n// --- WorkSource construction ---\n\nfunction buildWorkSource(board: BoardConfig, daemon: DaemonConfig): WorkSource {\n if (board.source.type === \"gitlab\") {\n const gl = daemon.channels.gitlab\n if (!gl?.token) throw new Error(\"channels.gitlab.token is required for gitlab boards\")\n const agentUsernames: Record<string, string[]> = {}\n for (const mapping of gl.agentMappings) {\n if (mapping.gitlabUsernames.length) {\n agentUsernames[mapping.agentId] = mapping.gitlabUsernames\n }\n }\n return new GitLabWorkSource(gl.host, gl.token, board.source.projects, agentUsernames, (...args) => console.log(\"[board]\", ...args))\n }\n throw new Error(`unsupported board source: ${(board.source as any).type}`)\n}\n\n// --- Audit log ---\n\nfunction audit(entry: { actor: string; action: string; boardId: string; itemId?: string; payload?: unknown }): void {\n try {\n const dir = resolve(process.cwd(), \".agentx\")\n if (!existsSync(dir)) mkdirSync(dir, { recursive: true })\n const line = JSON.stringify({ ts: new Date().toISOString(), ...entry }) + \"\\n\"\n appendFileSync(resolve(dir, \"board-audit.jsonl\"), line)\n } catch { /* non-fatal */ }\n}\n\nfunction auditActor(req: IncomingMessage): string {\n const fwd = req.headers[\"x-forwarded-for\"]\n return (Array.isArray(fwd) ? fwd[0] : fwd?.split(\",\")[0]) || req.socket.remoteAddress || \"unknown\"\n}\n\n// --- Live mesh snapshot ---\n//\n// Aggregates /agents + /mesh from the configured primary daemon, then fans out\n// to each mesh peer to collect their /agents (running tasks included) so the\n// dashboard can show a single cross-node view of what every agent is doing.\n\ninterface NodeLive {\n id: string\n name: string\n url: string\n reachable: boolean\n error?: string\n uptimeSec?: number\n agents: Array<{\n id: string\n name: string\n tier: string\n model?: string\n active: number\n total: number\n errors: number\n lastActive?: string\n runningTasks?: Array<{\n id: string\n messagePreview: string\n channel: string\n chatId?: string\n sender?: string\n startedAt: string\n }>\n }>\n}\ninterface LiveSnapshot {\n ts: string\n nodes: NodeLive[]\n}\n\nasync function fetchDaemonAgents(url: string, token?: string, signal?: AbortSignal): Promise<NodeLive> {\n const headers: Record<string, string> = {}\n if (token) headers[\"Authorization\"] = `Bearer ${token}`\n const base: NodeLive = { id: url, name: url, url, reachable: false, agents: [] }\n try {\n const [healthRes, agentsRes, meshRes] = await Promise.all([\n fetch(url + \"/health\", { headers, signal }).catch(() => null),\n fetch(url + \"/agents\", { headers, signal }).catch(() => null),\n fetch(url + \"/mesh\", { headers, signal }).catch(() => null),\n ])\n if (!agentsRes || !agentsRes.ok) {\n base.error = agentsRes ? `HTTP ${agentsRes.status}` : \"unreachable\"\n return base\n }\n const agents: any[] = await agentsRes.json()\n base.agents = agents.map((a) => ({\n id: a.id, name: a.name, tier: a.tier, model: a.model,\n active: a.active || 0, total: a.total || 0, errors: a.errors || 0,\n lastActive: a.lastActive,\n runningTasks: Array.isArray(a.runningTasks) ? a.runningTasks : [],\n }))\n if (healthRes && healthRes.ok) {\n const h: any = await healthRes.json()\n base.uptimeSec = h.uptime\n base.name = h.node?.name || h.node?.id || url\n base.id = h.node?.id || url\n }\n base.reachable = true\n // Expose mesh peer info for discovery, but the caller does fan-out separately.\n ;(base as any)._peers = Array.isArray(meshRes && await meshResSafe(meshRes)) ? (base as any)._peers : undefined\n } catch (e: any) {\n base.error = e.message || String(e)\n }\n return base\n}\n\n// Tiny helper to double-json a Response only once.\nlet _meshJsonCache = new WeakMap<Response, any>()\nasync function meshResSafe(r: Response): Promise<any> {\n if (_meshJsonCache.has(r)) return _meshJsonCache.get(r)\n try { const j = r.ok ? await r.json() : null; _meshJsonCache.set(r, j); return j } catch { return null }\n}\n\nasync function fetchMeshPeers(primaryUrl: string, token?: string, signal?: AbortSignal): Promise<Array<{ url: string; name: string; token?: string }>> {\n try {\n const headers: Record<string, string> = {}\n if (token) headers[\"Authorization\"] = `Bearer ${token}`\n const r = await fetch(primaryUrl + \"/mesh\", { headers, signal })\n if (!r.ok) return []\n const peers: any[] = await r.json()\n return peers\n .filter((p) => p && p.peerUrl)\n .map((p) => ({ url: String(p.peerUrl).replace(/\\/+$/, \"\"), name: p.peer || p.peerUrl }))\n } catch { return [] }\n}\n\nasync function buildLiveSnapshot(daemon: DaemonConfig): Promise<LiveSnapshot> {\n const dash = daemon.dashboard\n const primaryUrl = dash.daemonUrl.replace(/\\/+$/, \"\")\n const primaryToken = dash.token\n // Sources: primary + configured extras + auto-discovered mesh peers.\n const seen = new Map<string, { name: string; url: string; token?: string }>()\n seen.set(primaryUrl, { name: \"primary\", url: primaryUrl, token: primaryToken })\n for (const d of dash.daemons) {\n const key = d.url.replace(/\\/+$/, \"\")\n if (!seen.has(key)) seen.set(key, { name: d.name, url: key, token: d.token })\n }\n const ac = new AbortController()\n const timeout = setTimeout(() => ac.abort(), 3000)\n try {\n const meshPeers = await fetchMeshPeers(primaryUrl, primaryToken, ac.signal)\n for (const p of meshPeers) if (!seen.has(p.url)) seen.set(p.url, p)\n const nodes = await Promise.all([...seen.values()].map((d) => fetchDaemonAgents(d.url, d.token, ac.signal)))\n return { ts: new Date().toISOString(), nodes }\n } finally { clearTimeout(timeout) }\n}\n\nfunction startLiveStream(req: IncomingMessage, res: ServerResponse, daemon: DaemonConfig): void {\n res.writeHead(200, {\n \"Content-Type\": \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n \"Connection\": \"keep-alive\",\n \"X-Accel-Buffering\": \"no\",\n })\n let closed = false\n const send = (ev: string, data: unknown) => {\n if (closed) return\n res.write(`event: ${ev}\\ndata: ${JSON.stringify(data)}\\n\\n`)\n }\n const tick = async () => {\n try {\n const snap = await buildLiveSnapshot(daemon)\n send(\"snapshot\", snap)\n } catch (e: any) {\n send(\"error\", { message: e.message })\n }\n }\n // Initial tick immediately, then every 2s.\n tick()\n const timer = setInterval(tick, 2000)\n req.on(\"close\", () => { closed = true; clearInterval(timer); res.end() })\n}\n\n// --- A2A issue drafting ---\n//\n// Builds a convention-aware prompt, calls the configured agent on the daemon,\n// and parses a strict JSON reply. The agent returns title + refined description\n// + suggested labels + (optional) suggested assignee username.\n\nconst DRAFT_CONVENTIONS = `\nYou are helping draft a new GitLab issue. Follow these conventions strictly.\n\nTitle pattern:\n <Kind>: <page|component> – <short symptom>\nKinds: Bug, UX, Copy, Perf, A11y, Security, UI, Incident (defects)\n Feat, Enh, Deprecate (features)\n Epic, Story, Spike, Req (planning)\n Data, Env, Test, Refactor, Chore, Task, Mgmt (technical)\n\nTitle guidance:\n - Be specific about the location (\"Contact page\", not \"frontend\").\n - Use lowercase for the symptom.\n - Keep the symptom 4-7 words max, present tense.\n - Do NOT include issue IDs.\n\nDescription structure (Markdown):\n For bugs: Problem / Steps to Reproduce / Expected / Actual / Environment / Impact.\n For features/stories: User Story (As a … I want … So that …) + Acceptance Criteria bullets.\n Otherwise: concise problem statement + any relevant context.\n\nLabels:\n - Pick ONE Role label from: Dev, Design, QA, Ops, PM, mgmt.\n - Pick ONE Kind label matching the title prefix (bug, task, ux, ui, perf, security, copy, enhancement, …).\n - Pick ONE Priority: critical, high, medium, low.\n - Pick ONE Difficulty: diff-XS, diff-S, diff-M, diff-L, diff-XL.\n - Optional Area/feature labels if clearly applicable from the known label list.\n\nReturn ONLY a single JSON object on one line — no prose, no code fences, no explanation.\nShape:\n { \"title\": string, \"description\": string, \"labels\": string[], \"assigneeUsername\": string | null, \"kind\": string }\nIf you don't have enough information to pick a label confidently, omit it rather than guessing.\n`\n\nasync function draftIssue(daemon: DaemonConfig, body: any): Promise<{ title: string; description: string; labels: string[]; assigneeUsername?: string; kind?: string; raw?: string }> {\n const rough: string = (body?.rough || \"\").toString().trim()\n if (!rough) throw new Error(\"rough description required\")\n const agentId: string | undefined = body?.agent || daemon.dashboard.draftAgent\n if (!agentId) throw new Error(\"no draft agent configured (set dashboard.draftAgent or pass agent in body)\")\n\n const boardCtx: {\n boardName?: string; primaryLabel?: string; project?: string;\n knownLabels?: string[]; members?: string[]; columnLabel?: string\n } = body?.context || {}\n\n const userPrompt = [\n `Rough description from the user:`,\n `\"\"\"`,\n rough,\n `\"\"\"`,\n ``,\n `Board: ${boardCtx.boardName || \"(unspecified)\"}`,\n boardCtx.primaryLabel ? `Primary board label (include in labels): ${boardCtx.primaryLabel}` : \"\",\n boardCtx.columnLabel ? `Target column label (include in labels): ${boardCtx.columnLabel}` : \"\",\n boardCtx.project ? `GitLab project: ${boardCtx.project}` : \"\",\n boardCtx.knownLabels?.length ? `Known labels in this project (prefer picking from these when they fit):\\n- ${boardCtx.knownLabels.slice(0, 60).join(\"\\n- \")}` : \"\",\n boardCtx.members?.length ? `Possible assignees (GitLab usernames): ${boardCtx.members.slice(0, 20).join(\", \")}` : \"\",\n ``,\n `Produce the JSON draft now.`,\n ].filter(Boolean).join(\"\\n\")\n\n const url = daemon.dashboard.daemonUrl.replace(/\\/+$/, \"\") + \"/task\"\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" }\n if (daemon.dashboard.token) headers[\"Authorization\"] = `Bearer ${daemon.dashboard.token}`\n\n const r = await fetch(url, {\n method: \"POST\", headers,\n body: JSON.stringify({\n agent: agentId,\n message: userPrompt,\n context: { channel: \"a2a\", sender: \"board-dashboard\", systemPrompt: DRAFT_CONVENTIONS },\n }),\n })\n if (!r.ok) throw new Error(`daemon /task HTTP ${r.status}: ${await r.text().catch(() => \"\")}`)\n const data: any = await r.json()\n if (data.error) throw new Error(String(data.error))\n const content: string = (data.content || \"\").toString()\n\n // Agent may wrap JSON in ```json fences or prose; extract the first balanced {...}.\n const parsed = extractJson(content)\n if (!parsed || typeof parsed.title !== \"string\") {\n throw new Error(\"agent did not return a valid JSON draft. Raw: \" + content.slice(0, 400))\n }\n const labels: string[] = Array.isArray(parsed.labels) ? parsed.labels.filter((x: any) => typeof x === \"string\") : []\n // Inject primary/column labels if the agent missed them.\n if (boardCtx.primaryLabel && !labels.includes(boardCtx.primaryLabel)) labels.unshift(boardCtx.primaryLabel)\n if (boardCtx.columnLabel && !labels.includes(boardCtx.columnLabel)) labels.unshift(boardCtx.columnLabel)\n return {\n title: parsed.title.trim(),\n description: (parsed.description || \"\").toString(),\n labels,\n assigneeUsername: typeof parsed.assigneeUsername === \"string\" ? parsed.assigneeUsername : undefined,\n kind: typeof parsed.kind === \"string\" ? parsed.kind : undefined,\n raw: content,\n }\n}\n\nfunction extractJson(text: string): any {\n if (!text) return null\n // Try direct parse\n try { return JSON.parse(text) } catch { /* fallthrough */ }\n // Strip code fences\n const fenced = text.match(/```(?:json)?\\s*([\\s\\S]*?)```/)\n if (fenced) { try { return JSON.parse(fenced[1]) } catch { /* fallthrough */ } }\n // Find first balanced {...}\n const start = text.indexOf(\"{\")\n if (start === -1) return null\n let depth = 0\n for (let i = start; i < text.length; i++) {\n const c = text[i]\n if (c === \"{\") depth++\n else if (c === \"}\") { depth--; if (depth === 0) { try { return JSON.parse(text.slice(start, i + 1)) } catch { return null } } }\n }\n return null\n}\n\n// --- HTTP helpers ---\n\nfunction sendJson(res: ServerResponse, status: number, body: unknown): void {\n res.writeHead(status, { \"Content-Type\": \"application/json; charset=utf-8\" })\n res.end(JSON.stringify(body))\n}\n\nasync function readJson(req: IncomingMessage): Promise<any> {\n return new Promise((resolve, reject) => {\n const chunks: Buffer[] = []\n req.on(\"data\", (c) => chunks.push(c))\n req.on(\"end\", () => {\n try {\n const raw = Buffer.concat(chunks).toString(\"utf-8\")\n resolve(raw ? JSON.parse(raw) : {})\n } catch (e) { reject(e) }\n })\n req.on(\"error\", reject)\n })\n}\n\n// --- HTML ---\n\nfunction renderBoardHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>AgentX Boards</title>\n<style>${BOARD_CSS}</style>\n</head>\n<body>\n<header>\n <div class=\"brand\">AgentX</div>\n <select id=\"boardPicker\" title=\"Switch board\"></select>\n <div class=\"search\">\n <span class=\"icon\">⌕</span>\n <input id=\"filterSearch\" type=\"search\" placeholder=\"Search or filter…\" autocomplete=\"off\" />\n </div>\n <select id=\"filterAssignee\" title=\"Assignee\"><option value=\"\">All assignees</option></select>\n <select id=\"filterMilestone\" title=\"Milestone\"><option value=\"\">Any milestone</option></select>\n <div id=\"filterLabels\" class=\"chips\"></div>\n <div class=\"spacer\"></div>\n <a href=\"/live\" class=\"link\" title=\"Live mesh view (L)\">◎ Live</a>\n <button id=\"activityBtn\" title=\"Agent activity (a)\">◉ Activity</button>\n <button id=\"newIssueBtn\" title=\"New issue\">+ New</button>\n <button id=\"refreshBtn\" title=\"Refresh (r)\">↻</button>\n <div id=\"conn\" class=\"conn ok\" title=\"ready\">●</div>\n</header>\n<aside id=\"activity-panel\" class=\"activity-panel hidden\" aria-hidden=\"true\">\n <header>\n <h2>Agent Activity</h2>\n <span id=\"activity-source\" class=\"source\"></span>\n <button id=\"activity-close\" class=\"x\" title=\"Close\">✕</button>\n </header>\n <div id=\"activity-body\">Loading…</div>\n <footer id=\"activity-events\" class=\"events\"></footer>\n</aside>\n<main id=\"columns\"></main>\n<div id=\"toast\"></div>\n<div id=\"modal\" class=\"modal\" aria-hidden=\"true\">\n <div class=\"modal-backdrop\"></div>\n <div class=\"modal-panel\" role=\"dialog\" aria-modal=\"true\">\n <header class=\"modal-h\">\n <div class=\"modal-title-row\">\n <span id=\"m-iid\" class=\"iid\"></span>\n <input id=\"m-title\" type=\"text\" placeholder=\"Issue title\" />\n </div>\n <button id=\"m-close\" class=\"x\" title=\"Close (esc)\">✕</button>\n </header>\n <div class=\"modal-body\">\n <div class=\"m-main\">\n <div id=\"m-draft\" class=\"draft-box\" hidden>\n <label class=\"fld\">\n <span>✨ Describe the issue (the agent will draft a proper title + description)</span>\n <textarea id=\"m-rough\" rows=\"3\" placeholder=\"e.g. when I upload a CSV over 500 rows in Receiving Records the import silently fails\"></textarea>\n </label>\n <div class=\"draft-row\">\n <select id=\"m-draft-agent\" title=\"Drafting agent\"><option value=\"\">Agent…</option></select>\n <button id=\"m-draft-go\" class=\"btn-primary\" type=\"button\">✨ Draft</button>\n <span id=\"m-draft-hint\" class=\"hint\"></span>\n </div>\n </div>\n <label class=\"fld\">\n <span>Description</span>\n <textarea id=\"m-desc\" rows=\"8\" placeholder=\"Markdown supported\"></textarea>\n </label>\n <div class=\"fld\">\n <span>Labels</span>\n <div id=\"m-labels\" class=\"chip-picker\"></div>\n </div>\n </div>\n <aside class=\"m-side\">\n <div class=\"fld\">\n <span>State</span>\n <div id=\"m-state\" class=\"state-toggle\">\n <button data-state=\"opened\">Open</button>\n <button data-state=\"closed\">Closed</button>\n </div>\n </div>\n <div class=\"fld\">\n <span>Assignees</span>\n <div id=\"m-assignees\" class=\"chip-picker\"></div>\n </div>\n <div class=\"fld\">\n <span>Milestone</span>\n <select id=\"m-milestone\"><option value=\"\">None</option></select>\n </div>\n <div class=\"fld muted\">\n <span>Author</span>\n <div id=\"m-author\"></div>\n </div>\n <div class=\"fld muted\">\n <span>Created</span>\n <div id=\"m-created\"></div>\n </div>\n <div class=\"fld muted\">\n <span>Updated</span>\n <div id=\"m-updated\"></div>\n </div>\n <a id=\"m-link\" class=\"ext-link\" href=\"#\" target=\"_blank\" rel=\"noopener\">Open in GitLab ↗</a>\n </aside>\n </div>\n <footer class=\"modal-f\">\n <div id=\"m-hint\" class=\"hint\"></div>\n <div class=\"spacer\"></div>\n <button id=\"m-cancel\" class=\"btn-ghost\">Cancel</button>\n <button id=\"m-save\" class=\"btn-primary\">Save</button>\n </footer>\n </div>\n</div>\n<script src=\"/sortable.min.js\"></script>\n<script>${BOARD_JS}</script>\n</body>\n</html>`\n}\n\n// --- Live full-screen page (/live) ---\n\nfunction renderLiveHtml(): string {\n return `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>AgentX — Live</title>\n<style>${LIVE_CSS}</style>\n</head>\n<body>\n<header>\n <div class=\"brand\">AgentX <span class=\"sub\">· Live</span></div>\n <div id=\"summary\" class=\"summary\"></div>\n <div class=\"spacer\"></div>\n <a href=\"/\" class=\"link\">← Boards</a>\n <span id=\"ts\" class=\"ts\" title=\"Last update\"></span>\n <span id=\"conn\" class=\"conn ok\" title=\"connected\">●</span>\n</header>\n<main id=\"grid\"></main>\n<script>${LIVE_JS}</script>\n</body>\n</html>`\n}\n\nconst LIVE_CSS = `\n:root {\n --bg: #0b0d14; --card: #151823; --node: #1a1d29; --border: #2a2d3a;\n --text: #e6e8ef; --muted: #8b8fa3; --accent: #6366f1;\n --green: #22c55e; --yellow: #f59e0b; --red: #ef4444; --blue: #3b82f6; --gray: #6b7280;\n}\n* { box-sizing: border-box; }\nhtml, body { margin: 0; min-height: 100%; background: var(--bg); color: var(--text);\n font: 13px/1.4 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; }\nheader { display: flex; align-items: center; gap: 14px; padding: 10px 18px;\n background: #10131c; border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 10; }\nheader .brand { font-weight: 600; color: var(--accent); font-size: 15px; }\nheader .brand .sub { color: var(--muted); font-weight: 500; }\nheader .summary { display: flex; gap: 16px; color: var(--muted); font-size: 12px; }\nheader .summary b { color: var(--text); }\nheader .spacer { flex: 1; }\nheader .link { color: var(--muted); text-decoration: none; font-size: 13px; padding: 4px 10px; border: 1px solid var(--border); border-radius: 6px; }\nheader .link:hover { color: var(--accent); border-color: var(--accent); }\nheader .ts { color: var(--muted); font-size: 11px; font-family: ui-monospace, monospace; }\nheader .conn { font-size: 14px; }\nheader .conn.ok { color: var(--green); }\nheader .conn.warn { color: var(--yellow); }\nheader .conn.err { color: var(--red); }\n\nmain#grid { padding: 16px; display: flex; flex-direction: column; gap: 16px; }\n\n.node { background: var(--node); border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }\n.node > header { background: transparent; position: static; padding: 12px 16px; border-bottom: 1px solid var(--border); gap: 10px; }\n.node .name { font-weight: 600; font-size: 14px; }\n.node .url { color: var(--muted); font-family: ui-monospace, monospace; font-size: 11px; }\n.node .tag { font-size: 10px; text-transform: uppercase; letter-spacing: 0.5px;\n padding: 2px 7px; border-radius: 10px; background: rgba(99,102,241,0.2); color: #c5c8d6; }\n.node .tag.down { background: rgba(239,68,68,0.25); color: #fca5a5; }\n.node .tag.up { background: rgba(34,197,94,0.2); color: #86efac; }\n.node .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n gap: 10px; padding: 12px 14px; }\n\n.agent { background: var(--card); border: 1px solid var(--border); border-radius: 8px;\n padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; transition: border-color 0.2s; }\n.agent.busy { border-color: var(--green); box-shadow: 0 0 0 1px rgba(34,197,94,0.3); }\n.agent.errored { border-color: var(--red); }\n.agent > .top { display: flex; align-items: center; gap: 8px; }\n.agent .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gray); flex-shrink: 0; }\n.agent.busy .dot { background: var(--green); box-shadow: 0 0 6px var(--green); animation: pulse 1.2s ease-in-out infinite; }\n.agent.errored .dot { background: var(--red); }\n.agent .name { font-weight: 600; font-size: 13px; flex: 1; }\n.agent .tier { font-size: 9px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); padding: 2px 6px; border: 1px solid var(--border); border-radius: 4px; }\n.agent .meta { display: flex; align-items: center; gap: 6px; font-size: 10px; color: var(--muted); flex-wrap: wrap; }\n.agent .model { font-family: ui-monospace, monospace; font-size: 10px; color: var(--accent);\n background: rgba(99,102,241,0.12); padding: 1px 7px; border-radius: 3px; letter-spacing: 0.2px; }\n.agent .last { font-size: 10px; color: var(--muted); }\n.agent .last.muted { font-style: italic; opacity: 0.7; }\n.agent .stats { display: flex; gap: 10px; font-size: 11px; color: var(--muted); }\n.agent .stats b { color: var(--text); font-weight: 600; }\n.agent .stats .err b { color: var(--red); }\n.agent .tasks { display: flex; flex-direction: column; gap: 6px; padding-top: 6px; border-top: 1px dashed var(--border); }\n.agent .task { display: flex; align-items: center; gap: 6px; font-size: 11px; }\n.agent .task .channel { background: rgba(99,102,241,0.18); color: var(--accent);\n font-size: 9px; text-transform: uppercase; padding: 1px 6px; border-radius: 3px; letter-spacing: 0.5px; }\n.agent .task .elapsed { color: var(--muted); font-family: ui-monospace, monospace; font-size: 10px; }\n.agent .task .preview { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }\n.agent .idle { color: var(--muted); font-size: 11px; font-style: italic; }\n\n@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: 0.4 } }\n`\n\nconst LIVE_JS = `\n'use strict';\n\nconst ui = { nodes: new Map(), summary: { nodes: 0, agents: 0, busy: 0, errors: 0 } };\n\nfunction escapeHtml(s) {\n return String(s).replace(/[&<>\"']/g, c => ({ '&':'&', '<':'<', '>':'>', '\"':'"', \"'\":''' }[c]));\n}\nfunction fmtElapsed(ms) {\n if (ms < 1000) return ms + 'ms';\n const s = Math.floor(ms / 1000); if (s < 60) return s + 's';\n const m = Math.floor(s / 60); const rs = s % 60;\n if (m < 60) return m + 'm ' + (rs < 10 ? '0' : '') + rs + 's';\n const h = Math.floor(m / 60); const rm = m % 60;\n return h + 'h ' + (rm < 10 ? '0' : '') + rm + 'm';\n}\n\nfunction render(snapshot) {\n const grid = document.getElementById('grid');\n const nowTs = new Date(snapshot.ts).toLocaleTimeString();\n document.getElementById('ts').textContent = nowTs;\n const summary = { nodes: snapshot.nodes.length, reachable: 0, agents: 0, busy: 0, errors: 0 };\n grid.innerHTML = '';\n for (const node of snapshot.nodes) {\n if (node.reachable) summary.reachable++;\n summary.agents += node.agents.length;\n for (const a of node.agents) {\n const busy = (a.runningTasks && a.runningTasks.length > 0) || (a.active || 0) > 0;\n if (busy) summary.busy++;\n summary.errors += (a.errors || 0);\n }\n grid.appendChild(renderNode(node));\n }\n document.getElementById('summary').innerHTML =\n '<span><b>' + summary.reachable + '/' + summary.nodes + '</b> nodes</span>' +\n '<span><b>' + summary.agents + '</b> agents</span>' +\n '<span><b>' + summary.busy + '</b> active now</span>' +\n '<span><b>' + summary.errors + '</b> errors</span>';\n}\n\nfunction renderNode(node) {\n const sec = document.createElement('section'); sec.className = 'node';\n const tag = node.reachable\n ? '<span class=\"tag up\">online · ' + (node.uptimeSec ? Math.round(node.uptimeSec / 60) + 'm' : '—') + '</span>'\n : '<span class=\"tag down\">offline — ' + escapeHtml(node.error || 'unreachable') + '</span>';\n sec.innerHTML = '<header><div class=\"name\">' + escapeHtml(node.name) + '</div>' +\n '<div class=\"url\">' + escapeHtml(node.url) + '</div>' +\n tag + '</header><div class=\"grid\"></div>';\n const g = sec.querySelector('.grid');\n if (!node.reachable || node.agents.length === 0) {\n const empty = document.createElement('div'); empty.style.color = 'var(--muted)'; empty.style.padding = '8px 4px';\n empty.textContent = node.reachable ? 'No agents on this node.' : 'Unreachable.';\n g.appendChild(empty);\n } else {\n for (const a of node.agents) g.appendChild(renderAgent(a));\n }\n return sec;\n}\n\nfunction renderAgent(a) {\n const card = document.createElement('div');\n const busy = (a.runningTasks && a.runningTasks.length > 0) || (a.active || 0) > 0;\n const errored = (a.errors || 0) > 0;\n card.className = 'agent' + (busy ? ' busy' : '') + (errored ? ' errored' : '');\n const tasks = (a.runningTasks || []).map(t => {\n const elapsed = fmtElapsed(Date.now() - new Date(t.startedAt).getTime());\n return '<div class=\"task\">' +\n '<span class=\"channel\">' + escapeHtml(t.channel || '—') + '</span>' +\n '<span class=\"preview\" title=\"' + escapeHtml(t.messagePreview || '') + '\">' + escapeHtml(t.messagePreview || '(no preview)') + '</span>' +\n '<span class=\"elapsed\">' + elapsed + '</span>' +\n '</div>';\n }).join('');\n const tasksBlock = tasks\n ? '<div class=\"tasks\">' + tasks + '</div>'\n : (busy ? '<div class=\"tasks\"><div class=\"task\"><span class=\"elapsed\">running · no preview</span></div></div>'\n : '<div class=\"idle\">idle</div>');\n const modelLabel = a.model ? '<span class=\"model\" title=\"Model\">' + escapeHtml(shortenModel(a.model)) + '</span>' : '';\n const lastLabel = a.lastActive\n ? '<span class=\"last\" title=\"' + escapeHtml(new Date(a.lastActive).toLocaleString()) + '\">last ' + escapeHtml(fmtAgo(a.lastActive)) + '</span>'\n : '<span class=\"last muted\">never ran</span>';\n card.innerHTML =\n '<div class=\"top\">' +\n '<span class=\"dot\"></span>' +\n '<span class=\"name\">' + escapeHtml(a.name || a.id) + '</span>' +\n '<span class=\"tier\">' + escapeHtml(a.tier || '') + '</span>' +\n '</div>' +\n '<div class=\"meta\">' + modelLabel + lastLabel + '</div>' +\n '<div class=\"stats\">' +\n '<span>Active <b>' + (a.active || 0) + '</b></span>' +\n '<span>Total <b>' + (a.total || 0) + '</b></span>' +\n '<span class=\"err\">Err <b>' + (a.errors || 0) + '</b></span>' +\n '</div>' + tasksBlock;\n return card;\n}\n\nfunction shortenModel(m) {\n if (!m) return '';\n return String(m)\n .replace(/^claude-/, '')\n .replace(/-\\\\d{8}$/, '')\n .replace(/\\\\[1m\\\\]$/, ' · 1M');\n}\n\nfunction fmtAgo(iso) {\n const t = new Date(iso).getTime();\n if (!t) return '—';\n const s = Math.floor((Date.now() - t) / 1000);\n if (s < 60) return s + 's ago';\n const m = Math.floor(s / 60); if (m < 60) return m + 'm ago';\n const h = Math.floor(m / 60); if (h < 24) return h + 'h ago';\n const d = Math.floor(h / 24); return d + 'd ago';\n}\n\nfunction connect() {\n const conn = document.getElementById('conn');\n let es;\n const open = () => {\n try { es = new EventSource('/api/live/stream'); } catch (e) { setTimeout(open, 2000); return; }\n es.addEventListener('snapshot', (ev) => {\n conn.className = 'conn ok'; conn.title = 'connected';\n try { render(JSON.parse(ev.data)); } catch {}\n });\n es.addEventListener('error', () => {\n conn.className = 'conn err'; conn.title = 'reconnecting…';\n es.close(); setTimeout(open, 2000);\n });\n };\n open();\n}\n\n// Re-render every second to tick elapsed counters even when no new snapshot arrives.\nlet lastSnap = null;\nfetch('/api/live').then(r => r.json()).then(s => { lastSnap = s; render(s); }).catch(() => {});\nsetInterval(() => { if (lastSnap) {\n // Patch elapsed times without a fresh snapshot\n document.querySelectorAll('.agent .task .elapsed').forEach((el) => { /* placeholder: server snapshot advances on stream */ });\n} }, 1000);\nconnect();\n`\n\nconst BOARD_CSS = `\n:root {\n --bg: #0f1117; --card: #1a1d27; --col: #161922; --col-hdr: #1e2230;\n --border: #2a2d3a; --border-soft: #22253220;\n --text: #e6e8ef; --muted: #8b8fa3; --accent: #6366f1;\n --green: #22c55e; --yellow: #f59e0b; --orange: #fb7a35;\n --red: #ef4444; --blue: #3b82f6; --gray: #6b7280;\n}\n* { box-sizing: border-box; }\nhtml, body { margin: 0; height: 100%; background: var(--bg); color: var(--text);\n font: 13px/1.4 -apple-system, BlinkMacSystemFont, \"Segoe UI\", sans-serif; }\nheader { display: flex; align-items: center; gap: 10px; padding: 10px 16px;\n background: var(--card); border-bottom: 1px solid var(--border); position: sticky; top: 0; z-index: 10;\n flex-wrap: wrap; }\nheader .brand { font-weight: 600; letter-spacing: 0.5px; color: var(--accent); font-size: 14px; }\nheader select, header input, header button { background: var(--col); color: var(--text);\n border: 1px solid var(--border); padding: 6px 10px; border-radius: 6px; font: inherit; outline: none; }\nheader select:focus, header input:focus { border-color: var(--accent); }\nheader .search { position: relative; }\nheader .search .icon { position: absolute; left: 10px; top: 50%; transform: translateY(-50%);\n color: var(--muted); pointer-events: none; font-size: 14px; }\nheader .search input { padding-left: 28px; width: 260px; }\nheader button { cursor: pointer; }\nheader button:hover { border-color: var(--accent); }\nheader .chips { display: flex; gap: 4px; flex-wrap: wrap; }\nheader .spacer { flex: 1; }\nheader .conn { font-size: 16px; line-height: 1; }\nheader .conn.ok { color: var(--green); }\nheader .conn.warn { color: var(--yellow); }\nheader .conn.err { color: var(--red); }\n\nmain#columns { display: flex; gap: 10px; padding: 12px; overflow-x: auto; align-items: stretch;\n min-height: calc(100vh - 60px); }\n.col { background: var(--col); border: 1px solid var(--border); border-radius: 8px;\n min-width: 280px; max-width: 320px; flex: 1 0 280px; display: flex; flex-direction: column;\n max-height: calc(100vh - 84px); }\n.col .accent { height: 3px; border-top-left-radius: 7px; border-top-right-radius: 7px; background: var(--gray); }\n.col header.col-h { margin: 0; padding: 10px 12px; background: var(--col-hdr);\n border-bottom: 1px solid var(--border); display: flex; align-items: center; gap: 8px;\n position: sticky; top: 0; z-index: 1; border-radius: 0; }\n.col header.col-h h3 { margin: 0; font-size: 13px; font-weight: 600; color: var(--text); flex: 1; }\n.col header.col-h .count { background: rgba(255,255,255,0.08); color: var(--muted);\n font-size: 11px; padding: 2px 8px; border-radius: 10px; font-weight: 500; }\n.stack { padding: 8px; display: flex; flex-direction: column; gap: 8px; overflow-y: auto;\n flex: 1; min-height: 40px; }\n\n.card { background: var(--card); border: 1px solid var(--border); border-radius: 6px;\n padding: 10px 12px; cursor: grab; transition: border-color .15s, transform .15s;\n display: flex; flex-direction: column; gap: 6px; }\n.card:hover { border-color: var(--accent); }\n.card.closed { opacity: 0.75; }\n.card .title { font-weight: 500; font-size: 13px; line-height: 1.4; color: var(--text);\n overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; word-break: break-word; }\n.card .labels { display: flex; gap: 4px; flex-wrap: wrap; }\n.card .label { font-size: 10px; padding: 2px 8px; border-radius: 10px; font-weight: 500;\n background: rgba(99, 102, 241, 0.25); color: #e6e8ef; white-space: nowrap; line-height: 1.4; }\n.card .foot { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--muted);\n flex-wrap: wrap; }\n.card .foot .iid { font-family: ui-monospace, monospace; }\n.card .foot .milestone { background: rgba(99,102,241,0.15); color: var(--accent);\n padding: 1px 6px; border-radius: 3px; font-size: 10px; }\n.card .foot .est { background: rgba(255,255,255,0.08); padding: 1px 6px; border-radius: 3px; font-size: 10px; }\n.card .foot .date { margin-left: auto; }\n.card .foot a.link { color: var(--muted); text-decoration: none; }\n.card .foot a.link:hover { color: var(--accent); }\n\n.avatar { display: inline-flex; align-items: center; justify-content: center;\n width: 22px; height: 22px; border-radius: 50%; font-size: 10px; font-weight: 600;\n color: white; flex-shrink: 0; }\n.avatar.unassigned { background: var(--gray); }\n\n.sortable-ghost { opacity: 0.4; }\n.sortable-drag { cursor: grabbing; }\n.sortable-chosen { border-color: var(--accent); }\n.empty { color: var(--muted); font-style: italic; text-align: center; padding: 16px; font-size: 12px; }\n\n#toast { position: fixed; right: 16px; bottom: 16px; padding: 10px 14px;\n background: var(--red); color: white; border-radius: 6px; font-size: 13px;\n max-width: 360px; display: none; z-index: 200; box-shadow: 0 4px 12px rgba(0,0,0,0.4); }\n#toast.show { display: block; }\n\n/* Multi-assignee stack on cards */\n.avatar-stack { display: inline-flex; }\n.avatar-stack .avatar { margin-left: -6px; border: 2px solid var(--card); }\n.avatar-stack .avatar:first-child { margin-left: 0; }\n.avatar img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; }\n\n/* Column add button */\n.col header.col-h .add { background: transparent; color: var(--muted); border: 1px solid var(--border);\n padding: 0 8px; line-height: 22px; border-radius: 4px; cursor: pointer; font-size: 14px; }\n.col header.col-h .add:hover { color: var(--accent); border-color: var(--accent); }\n\n/* Header buttons */\nheader #newIssueBtn { background: var(--accent); color: white; border-color: var(--accent); }\nheader #newIssueBtn:hover { filter: brightness(1.1); }\n\n/* --- Modal --- */\n.modal { position: fixed; inset: 0; z-index: 150; display: none; }\n.modal.open { display: block; }\n.modal-backdrop { position: absolute; inset: 0; background: rgba(0,0,0,0.6); backdrop-filter: blur(2px); }\n.modal-panel { position: relative; max-width: 940px; width: calc(100% - 40px); height: calc(100vh - 60px);\n margin: 30px auto; background: var(--card); border: 1px solid var(--border); border-radius: 10px;\n display: flex; flex-direction: column; box-shadow: 0 16px 48px rgba(0,0,0,0.55); overflow: hidden; }\n.modal-h { display: flex; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--border); align-items: center; }\n.modal-h .modal-title-row { flex: 1; display: flex; gap: 10px; align-items: center; }\n.modal-h .iid { font-family: ui-monospace, monospace; color: var(--muted); font-size: 13px; }\n.modal-h input[type=\"text\"] { flex: 1; background: var(--col); border: 1px solid var(--border); color: var(--text);\n padding: 8px 10px; border-radius: 6px; font-size: 15px; font-weight: 500; outline: none; }\n.modal-h input[type=\"text\"]:focus { border-color: var(--accent); }\n.modal-h .x { background: transparent; color: var(--muted); border: none; font-size: 18px; cursor: pointer; padding: 6px 8px; }\n.modal-h .x:hover { color: var(--text); }\n\n.modal-body { flex: 1; display: grid; grid-template-columns: 1fr 300px; gap: 0;\n overflow: hidden; min-height: 0; }\n.modal-body .m-main { padding: 16px; overflow-y: auto; min-height: 0; }\n.modal-body .m-side { padding: 16px; border-left: 1px solid var(--border); background: var(--col);\n overflow-y: auto; min-height: 0; display: flex; flex-direction: column; gap: 14px; }\n\n.fld { display: flex; flex-direction: column; gap: 6px; margin-bottom: 14px; }\n.fld > span { font-size: 11px; font-weight: 600; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }\n.fld.muted > div { font-size: 12px; color: var(--muted); }\n.fld textarea { background: var(--col); border: 1px solid var(--border); color: var(--text);\n padding: 10px; border-radius: 6px; font: 13px/1.5 ui-monospace, \"SF Mono\", monospace; outline: none; resize: vertical; }\n.fld textarea:focus { border-color: var(--accent); }\n.fld select { background: var(--col); border: 1px solid var(--border); color: var(--text);\n padding: 6px 10px; border-radius: 6px; font: inherit; outline: none; }\n\n.chip-picker { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 6px;\n background: var(--col); border: 1px solid var(--border); border-radius: 6px; min-height: 36px; }\n.chip-picker .chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px;\n border-radius: 10px; font-size: 11px; background: rgba(99,102,241,0.25); color: #e6e8ef; font-weight: 500; }\n.chip-picker .chip .remove { cursor: pointer; color: rgba(255,255,255,0.7); margin-left: 2px; }\n.chip-picker .chip .remove:hover { color: white; }\n.chip-picker .add-chip { background: transparent; border: 1px dashed var(--border); color: var(--muted);\n padding: 3px 10px; border-radius: 10px; cursor: pointer; font: inherit; font-size: 11px; }\n.chip-picker .add-chip:hover { border-color: var(--accent); color: var(--accent); }\n\n.chip-picker .dropdown { position: relative; }\n.chip-picker .dropdown .menu { position: absolute; top: 100%; left: 0; margin-top: 4px;\n background: var(--card); border: 1px solid var(--border); border-radius: 6px; min-width: 240px;\n max-height: 280px; overflow-y: auto; box-shadow: 0 8px 20px rgba(0,0,0,0.4); z-index: 10; padding: 4px; }\n.chip-picker .dropdown .menu input.search { width: 100%; background: var(--col); border: 1px solid var(--border);\n color: var(--text); padding: 6px 10px; border-radius: 4px; font: inherit; margin-bottom: 4px; outline: none; }\n.chip-picker .dropdown .menu .opt { padding: 6px 10px; cursor: pointer; border-radius: 4px;\n font-size: 12px; display: flex; align-items: center; gap: 8px; }\n.chip-picker .dropdown .menu .opt:hover { background: var(--col); }\n.chip-picker .dropdown .menu .opt.selected { background: rgba(99,102,241,0.15); color: var(--accent); }\n.chip-picker .dropdown .menu .empty { padding: 10px; color: var(--muted); font-size: 12px; text-align: center; }\n\n.state-toggle { display: inline-flex; background: var(--col); border: 1px solid var(--border); border-radius: 6px; overflow: hidden; }\n.state-toggle button { background: transparent; border: none; color: var(--muted); padding: 6px 12px;\n cursor: pointer; font: inherit; font-size: 12px; }\n.state-toggle button.active[data-state=\"opened\"] { background: var(--green); color: white; }\n.state-toggle button.active[data-state=\"closed\"] { background: var(--gray); color: white; }\n\n.ext-link { color: var(--muted); text-decoration: none; font-size: 12px; margin-top: auto; padding-top: 10px;\n border-top: 1px solid var(--border); }\n.ext-link:hover { color: var(--accent); }\n\n.modal-f { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--border); }\n.modal-f .spacer { flex: 1; }\n.modal-f .hint { color: var(--muted); font-size: 12px; }\n.btn-primary { background: var(--accent); color: white; border: 1px solid var(--accent);\n padding: 7px 16px; border-radius: 6px; cursor: pointer; font: inherit; font-weight: 500; }\n.btn-primary:hover { filter: brightness(1.1); }\n.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }\n.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border);\n padding: 7px 14px; border-radius: 6px; cursor: pointer; font: inherit; }\n.btn-ghost:hover { border-color: var(--accent); color: var(--accent); }\n\n@media (max-width: 720px) {\n .modal-body { grid-template-columns: 1fr; }\n .modal-body .m-side { border-left: none; border-top: 1px solid var(--border); }\n}\n\n/* --- Activity panel --- */\n.activity-panel { position: fixed; top: 0; right: 0; bottom: 0; width: 340px;\n background: var(--card); border-left: 1px solid var(--border); z-index: 140;\n display: flex; flex-direction: column; box-shadow: -8px 0 24px rgba(0,0,0,0.35);\n transition: transform 0.2s ease; }\n.activity-panel.hidden { transform: translateX(100%); pointer-events: none; }\n.activity-panel > header { display: flex; align-items: center; gap: 8px; padding: 12px 14px;\n border-bottom: 1px solid var(--border); background: var(--col-hdr); }\n.activity-panel > header h2 { margin: 0; font-size: 13px; font-weight: 600; flex: 1; }\n.activity-panel > header .source { font-size: 10px; color: var(--muted); font-family: ui-monospace, monospace; }\n.activity-panel > header .x { background: transparent; border: none; color: var(--muted); font-size: 16px;\n cursor: pointer; padding: 4px 8px; }\n.activity-panel > header .x:hover { color: var(--text); }\n#activity-body { flex: 1; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 8px; }\n.agent-card { background: var(--col); border: 1px solid var(--border); border-radius: 6px;\n padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; }\n.agent-card .top { display: flex; align-items: center; gap: 8px; }\n.agent-card .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--gray); flex-shrink: 0; }\n.agent-card .dot.active { background: var(--green); box-shadow: 0 0 6px var(--green); animation: pulse 1.2s ease-in-out infinite; }\n.agent-card .dot.errored { background: var(--red); }\n.agent-card .name { font-weight: 600; font-size: 13px; flex: 1; }\n.agent-card .tier { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.5px; }\n.agent-card .meta { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.agent-card .model { font-family: ui-monospace, monospace; font-size: 10px; color: var(--accent);\n background: rgba(99,102,241,0.12); padding: 1px 7px; border-radius: 3px; letter-spacing: 0.2px; }\n.agent-card .last { font-size: 10px; color: var(--muted); }\n.agent-card .last.muted { font-style: italic; opacity: 0.7; }\n.agent-card .stats { display: flex; gap: 10px; font-size: 11px; color: var(--muted); }\n.agent-card .stats b { color: var(--text); font-weight: 600; }\n.agent-card .stats .errors b { color: var(--red); }\n.agent-card .current { font-size: 11px; color: var(--muted); padding-top: 4px; border-top: 1px dashed var(--border);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.agent-card .current b { color: var(--text); font-weight: 500; }\n@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: 0.4 } }\n\n.activity-panel .events { padding: 10px 14px; max-height: 200px; overflow-y: auto;\n border-top: 1px solid var(--border); background: #0a0c11; font: 11px/1.5 ui-monospace, monospace; }\n.activity-panel .events .evt { color: var(--muted); margin-bottom: 4px; }\n.activity-panel .events .evt .ts { color: #4a4e5a; }\n.activity-panel .events .evt .who { color: var(--accent); }\n.activity-panel .events .evt .what { color: var(--text); }\n.activity-panel .events .evt.err .what { color: var(--red); }\n\n/* Shift the main grid left when activity panel is open so cards aren't hidden */\nbody.panel-open main#columns { padding-right: 352px; }\nbody.panel-open header { padding-right: 356px; }\n\nheader #activityBtn.on { background: var(--accent); color: white; border-color: var(--accent); }\nheader a.link { color: var(--text); text-decoration: none; padding: 6px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 12px; }\nheader a.link:hover { color: var(--accent); border-color: var(--accent); }\n\n/* AI draft row inside the create modal */\n.draft-box { background: var(--col); border: 1px dashed var(--accent); border-radius: 8px;\n padding: 10px 12px; margin-bottom: 14px; }\n.draft-box .fld { margin-bottom: 8px; }\n.draft-box .draft-row { display: flex; gap: 8px; align-items: center; }\n.draft-box .draft-row select { flex: 0 0 160px; }\n.draft-box .draft-row .hint { color: var(--muted); font-size: 12px; flex: 1; }\n.draft-box .draft-row .hint.err { color: var(--red); }\n.draft-box .draft-row .hint.ok { color: var(--green); }\n`\n\nconst BOARD_JS = `\n'use strict';\nconst state = {\n boards: [], current: null, items: {},\n filter: { q: '', assignee: '', label: '', milestone: '' },\n // Per-board caches — members + milestones + known labels.\n meta: { members: [], milestones: [], labels: [] },\n modal: { mode: null, id: null, original: null, draft: null }\n};\n\nfunction csrfHeaders(extra) {\n return Object.assign({ 'Content-Type': 'application/json', 'X-Requested-With': 'agentx-board' }, extra || {});\n}\nfunction toast(msg, kind) {\n const el = document.getElementById('toast');\n el.textContent = msg;\n el.style.background = kind === 'ok' ? 'var(--green)' : 'var(--red)';\n el.classList.add('show');\n clearTimeout(toast._t);\n toast._t = setTimeout(() => el.classList.remove('show'), 4000);\n}\n\nasync function loadBoards() {\n const r = await fetch('/api/boards');\n if (!r.ok) throw new Error('failed to load boards');\n state.boards = await r.json();\n const picker = document.getElementById('boardPicker');\n picker.innerHTML = '';\n for (const b of state.boards) {\n const opt = document.createElement('option');\n opt.value = b.id; opt.textContent = b.name || b.id;\n picker.appendChild(opt);\n }\n picker.onchange = () => switchBoard(picker.value);\n if (state.boards.length) switchBoard(state.boards[0].id);\n}\n\nasync function switchBoard(id) {\n state.current = state.boards.find(b => b.id === id);\n state.meta = { members: [], milestones: [], labels: [] };\n renderColumns();\n populateFilters();\n await loadItems();\n // Fetch members + milestones in the background for pickers.\n loadMeta();\n}\n\nasync function loadMeta() {\n if (!state.current) return;\n try {\n const [mem, ms] = await Promise.all([\n fetch(\\`/api/boards/\\${state.current.id}/members\\`).then(r => r.ok ? r.json() : []),\n fetch(\\`/api/boards/\\${state.current.id}/milestones\\`).then(r => r.ok ? r.json() : []),\n ]);\n state.meta.members = mem;\n state.meta.milestones = ms;\n state.meta.labels = collectKnownLabels();\n } catch (e) { /* non-fatal */ }\n}\n\nfunction collectKnownLabels() {\n const set = new Set();\n for (const list of Object.values(state.items)) for (const i of list) {\n for (const l of (i.labels || [])) set.add(l);\n }\n return [...set].sort();\n}\n\nfunction populateFilters() {\n if (!state.current) return;\n const labelsEl = document.getElementById('filterLabels');\n labelsEl.innerHTML = '';\n for (const lbl of state.current.labels || []) {\n const chip = document.createElement('span');\n chip.className = 'label';\n chip.style.cursor = 'pointer';\n chip.style.background = lbl.color || labelColor(lbl.name);\n chip.style.color = pickTextColor(lbl.color);\n chip.textContent = lbl.name;\n chip.onclick = () => {\n state.filter.label = state.filter.label === lbl.name ? '' : lbl.name;\n renderChipStates(); loadItems();\n };\n labelsEl.appendChild(chip);\n }\n}\nfunction renderChipStates() {\n document.querySelectorAll('#filterLabels .label').forEach(chip => {\n chip.style.opacity = (state.filter.label && chip.textContent !== state.filter.label) ? '0.35' : '1';\n });\n}\n\nfunction renderColumns() {\n const main = document.getElementById('columns');\n main.innerHTML = '';\n if (!state.current) return;\n for (const col of state.current.columns) {\n const section = document.createElement('section');\n section.className = 'col';\n section.dataset.colId = col.id;\n const canAdd = col.kind !== 'closed';\n section.innerHTML = \\`\n <div class=\"accent\" style=\"background:\\${col.accent || 'var(--gray)'}\"></div>\n <header class=\"col-h\">\n <h3>\\${escapeHtml(col.title)}</h3>\n <span class=\"count\">0</span>\n \\${canAdd ? \\`<button class=\"add\" data-col-id=\"\\${col.id}\" title=\"New issue in this column\">+</button>\\` : ''}\n </header>\n <div class=\"stack\" data-col-id=\"\\${col.id}\"></div>\n \\`;\n main.appendChild(section);\n }\n document.querySelectorAll('.col .add').forEach(btn => {\n btn.onclick = () => openCreateModal(btn.dataset.colId);\n });\n document.querySelectorAll('.stack').forEach(el => {\n Sortable.create(el, {\n group: 'board', animation: 150,\n onEnd: async (ev) => {\n const from = ev.from.dataset.colId;\n const to = ev.to.dataset.colId;\n if (from === to) return;\n const itemId = ev.item.dataset.id;\n try {\n const r = await fetch(\\`/api/boards/\\${state.current.id}/items/\\${encodeURIComponent(itemId)}/move\\`, {\n method: 'PATCH', headers: csrfHeaders(), body: JSON.stringify({ from, to })\n });\n if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || r.statusText);\n toast('Moved ✓', 'ok');\n updateCounts();\n } catch (e) {\n ev.from.insertBefore(ev.item, ev.from.children[ev.oldIndex] || null);\n updateCounts();\n toast('Move failed: ' + (e.message || 'unknown'));\n }\n }\n });\n });\n}\n\nfunction updateCounts() {\n document.querySelectorAll('.col').forEach(col => {\n const n = col.querySelector('.stack').children.length;\n const empty = col.querySelector('.stack .empty');\n const real = empty ? 0 : n;\n col.querySelector('.count').textContent = real;\n });\n}\n\nasync function loadItems() {\n const conn = document.getElementById('conn');\n conn.className = 'conn warn'; conn.title = 'loading…';\n if (!state.current) return;\n const params = new URLSearchParams();\n if (state.filter.q) params.set('q', state.filter.q);\n if (state.filter.assignee) params.set('assignee', state.filter.assignee);\n if (state.filter.label) params.set('label', state.filter.label);\n if (state.filter.milestone) params.set('milestone', state.filter.milestone);\n try {\n const r = await fetch(\\`/api/boards/\\${state.current.id}/items?\\${params.toString()}\\`);\n if (!r.ok) throw new Error(await r.text());\n const data = await r.json();\n state.items = data.columns;\n paintItems();\n populateAssigneeFilter();\n populateMilestoneFilter();\n conn.className = 'conn ok'; conn.title = 'ok';\n } catch (e) {\n conn.className = 'conn err'; conn.title = 'load failed';\n toast('Load failed: ' + (e.message || 'unknown'));\n }\n}\n\nfunction populateAssigneeFilter() {\n const set = new Set();\n for (const list of Object.values(state.items)) for (const i of list) if (i.assignee) set.add(i.assignee);\n const sel = document.getElementById('filterAssignee');\n const current = sel.value;\n sel.innerHTML = '<option value=\"\">All assignees</option>';\n for (const a of [...set].sort()) {\n const o = document.createElement('option'); o.value = a; o.textContent = a;\n sel.appendChild(o);\n }\n sel.value = current;\n}\nfunction populateMilestoneFilter() {\n const set = new Set();\n for (const list of Object.values(state.items)) for (const i of list) if (i.milestone) set.add(i.milestone);\n const sel = document.getElementById('filterMilestone');\n const current = sel.value;\n sel.innerHTML = '<option value=\"\">Any milestone</option>';\n for (const m of [...set].sort()) {\n const o = document.createElement('option'); o.value = m; o.textContent = m;\n sel.appendChild(o);\n }\n sel.value = current;\n}\n\nfunction paintItems() {\n if (!state.current) return;\n for (const col of state.current.columns) {\n const stack = document.querySelector(\\`.stack[data-col-id=\"\\${col.id}\"]\\`);\n if (!stack) continue;\n stack.innerHTML = '';\n const list = state.items[col.id] || [];\n if (list.length === 0) {\n const empty = document.createElement('div'); empty.className = 'empty'; empty.textContent = 'Empty';\n stack.appendChild(empty);\n } else {\n for (const item of list) stack.appendChild(renderCard(item, col));\n }\n }\n updateCounts();\n}\n\nfunction renderCard(item, col) {\n const card = document.createElement('div');\n card.className = 'card' + (item.state === 'closed' ? ' closed' : '');\n card.dataset.id = item.id;\n const iid = item.id.split(':').slice(-1)[0];\n\n // Filter out Status::* scoped labels from display (represented by the column).\n const details = (item.labelDetails || (item.labels || []).map(n => ({ name: n })))\n .filter(l => !/^Status::/i.test(l.name));\n const labelChips = details.slice(0, 5).map(l => {\n const bg = l.color || labelColor(l.name);\n const fg = l.text_color || pickTextColor(bg);\n return \\`<span class=\"label\" title=\"\\${escapeHtml(l.name)}\" style=\"background:\\${bg};color:\\${fg}\">\\${escapeHtml(l.name)}</span>\\`;\n }).join('');\n\n const assignees = (item.assignees && item.assignees.length)\n ? item.assignees\n : (item.assignee ? [{ username: item.assignee }] : []);\n const avatarStack = assignees.length\n ? \\`<span class=\"avatar-stack\">\\${assignees.slice(0, 3).map(renderAvatar).join('')}</span>\\`\n : \\`<span class=\"avatar unassigned\" title=\"unassigned\">?</span>\\`;\n\n const milestone = item.milestone ? \\`<span class=\"milestone\" title=\"Milestone\">🏁 \\${escapeHtml(item.milestone)}</span>\\` : '';\n const est = item.estimatedSeconds ? \\`<span class=\"est\" title=\"Estimate\">⏱ \\${fmtDuration(item.estimatedSeconds)}</span>\\` : '';\n const date = item.updatedAt ? \\`<span class=\"date\" title=\"Updated \\${escapeHtml(item.updatedAt)}\">\\${fmtDate(item.updatedAt)}</span>\\` : '';\n const link = item.url ? \\`<a class=\"link\" href=\"\\${escapeHtml(item.url)}\" target=\"_blank\" rel=\"noopener\" title=\"Open in GitLab\" onclick=\"event.stopPropagation()\">↗</a>\\` : '';\n\n card.innerHTML = \\`\n <div class=\"title\">\\${escapeHtml(item.title || '(untitled)')}</div>\n \\${labelChips ? \\`<div class=\"labels\">\\${labelChips}</div>\\` : ''}\n <div class=\"foot\">\n \\${avatarStack}\n <span class=\"iid\">#\\${escapeHtml(iid)}</span>\n \\${milestone}\n \\${est}\n \\${date}\n \\${link}\n </div>\n \\`;\n card.addEventListener('click', (e) => {\n if (e.target.closest('a,button')) return;\n openDetailModal(item.id);\n });\n return card;\n}\n\nfunction renderAvatar(a) {\n const name = a.name || a.username || '?';\n const img = a.avatarUrl ? \\`<img src=\"\\${escapeHtml(a.avatarUrl)}\" alt=\"\">\\` : '';\n const bg = labelColor(a.username || name);\n return \\`<span class=\"avatar\" title=\"\\${escapeHtml(name)}\" style=\"background:\\${bg}\">\\${img || escapeHtml(initials(name))}</span>\\`;\n}\n\nfunction escapeHtml(s) {\n return String(s).replace(/[&<>\"']/g, c => ({ '&': '&', '<': '<', '>': '>', '\"': '"', \"'\": ''' }[c]));\n}\nfunction initials(s) {\n const parts = String(s).replace(/[_\\\\-\\\\.]+/g, ' ').trim().split(/\\\\s+/);\n if (!parts.length) return '?';\n if (parts.length === 1) return parts[0].slice(0, 2).toUpperCase();\n return (parts[0][0] + parts[1][0]).toUpperCase();\n}\nfunction labelColor(s) {\n let h = 0;\n for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;\n return 'hsl(' + (h % 360) + ', 55%, 45%)';\n}\nfunction pickTextColor(bg) {\n if (!bg) return '#fff';\n if (bg.startsWith('#') && bg.length >= 7) {\n const r = parseInt(bg.slice(1,3),16), g = parseInt(bg.slice(3,5),16), b = parseInt(bg.slice(5,7),16);\n return (r*299 + g*587 + b*114) / 1000 >= 160 ? '#111' : '#fff';\n }\n return '#fff';\n}\nfunction fmtDuration(sec) {\n if (!sec) return '';\n const m = Math.round(sec / 60);\n if (m < 60) return m + 'm';\n const h = Math.floor(m / 60); const rem = m % 60;\n return rem ? \\`\\${h}h\\${rem}m\\` : h + 'h';\n}\nfunction fmtDate(iso) {\n const d = new Date(iso); if (Number.isNaN(d.getTime())) return '';\n return d.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });\n}\nfunction debounce(fn, ms) { let t; return (...a) => { clearTimeout(t); t = setTimeout(() => fn(...a), ms); }; }\n\ndocument.getElementById('filterSearch').oninput = debounce(e => { state.filter.q = e.target.value; loadItems(); }, 250);\ndocument.getElementById('filterAssignee').onchange = e => { state.filter.assignee = e.target.value; loadItems(); };\ndocument.getElementById('filterMilestone').onchange = e => { state.filter.milestone = e.target.value; loadItems(); };\ndocument.getElementById('refreshBtn').onclick = () => loadItems();\ndocument.getElementById('newIssueBtn').onclick = () => openCreateModal(null);\ndocument.getElementById('activityBtn').onclick = () => toggleActivity();\ndocument.getElementById('activity-close').onclick = () => toggleActivity(false);\nwindow.addEventListener('keydown', (e) => {\n if (e.key === 'Escape' && document.getElementById('modal').classList.contains('open')) { closeModal(); return; }\n if (e.target.matches('input,select,textarea')) return;\n if (e.key === 'r') loadItems();\n if (e.key === 'a') toggleActivity();\n});\n\n// --- Modal (detail view / create) ---\n\nasync function openDetailModal(itemId) {\n openModal('edit', itemId);\n const hint = document.getElementById('m-hint');\n hint.textContent = 'Loading…';\n try {\n const r = await fetch('/api/boards/' + state.current.id + '/items/' + encodeURIComponent(itemId));\n if (!r.ok) throw new Error((await r.json().catch(()=>({}))).error || r.statusText);\n const detail = await r.json();\n state.modal.original = detail;\n state.modal.draft = cloneForDraft(detail);\n paintModal(state.modal.draft);\n hint.textContent = '';\n } catch (e) {\n hint.textContent = 'Load failed: ' + e.message;\n }\n}\n\nfunction openCreateModal(columnId) {\n state.modal.targetColumnId = columnId || null;\n const blank = {\n id: null, title: '', description: '',\n labels: [], labelDetails: [], assignees: [], milestone: '', state: 'opened',\n };\n // Pre-apply the target column's scoped label so the new issue lands in that column.\n if (columnId && state.current) {\n const col = state.current.columns.find(c => c.id === columnId);\n if (col && col.kind === 'scoped-label' && col.scopedLabel) {\n blank.labels.push(col.scopedLabel);\n blank.labelDetails.push({ name: col.scopedLabel, color: col.accent });\n } else if (col && col.kind === 'label' && col.mapsToLabel) {\n blank.labels.push(col.mapsToLabel);\n blank.labelDetails.push({ name: col.mapsToLabel, color: col.accent });\n }\n }\n // Carry the board's primary label (the team filter) so created issues show up here.\n if (state.current && state.current.primaryToolLabel && !blank.labels.includes(state.current.primaryToolLabel)) {\n blank.labels.push(state.current.primaryToolLabel);\n blank.labelDetails.push({ name: state.current.primaryToolLabel });\n }\n openModal('create', null);\n state.modal.original = null;\n state.modal.draft = blank;\n paintModal(blank);\n}\n\nfunction openModal(mode, id) {\n state.modal.mode = mode;\n state.modal.id = id;\n const m = document.getElementById('modal');\n m.classList.add('open');\n m.setAttribute('aria-hidden', 'false');\n document.body.style.overflow = 'hidden';\n document.getElementById('m-save').textContent = mode === 'create' ? 'Create' : 'Save';\n document.getElementById('m-iid').textContent = id ? ('#' + id.split(':').slice(-1)[0]) : 'NEW';\n // Draft box only in create mode.\n const draftBox = document.getElementById('m-draft');\n if (draftBox) {\n draftBox.hidden = mode !== 'create';\n if (mode === 'create') {\n document.getElementById('m-rough').value = '';\n document.getElementById('m-draft-hint').textContent = '';\n document.getElementById('m-draft-hint').className = 'hint';\n populateDraftAgents();\n }\n }\n // Pre-focus: rough for create, description for edit.\n setTimeout(() => {\n const el = document.getElementById(mode === 'create' ? 'm-rough' : 'm-desc');\n if (el) el.focus();\n }, 50);\n}\n\nasync function populateDraftAgents() {\n const sel = document.getElementById('m-draft-agent');\n if (!sel) return;\n if (sel.options.length > 1) {\n // Already populated — just sync selection.\n const saved = localStorage.getItem('agentx.draftAgent');\n if (saved) sel.value = saved;\n return;\n }\n try {\n const r = await fetch('/api/agents');\n if (!r.ok) return;\n const list = await r.json();\n sel.innerHTML = '<option value=\"\">Agent…</option>';\n for (const a of list) {\n const o = document.createElement('option');\n o.value = a.id; o.textContent = a.name + ' (' + a.tier + ')';\n sel.appendChild(o);\n }\n const saved = localStorage.getItem('agentx.draftAgent');\n if (saved && list.some(a => a.id === saved)) sel.value = saved;\n sel.onchange = () => localStorage.setItem('agentx.draftAgent', sel.value);\n } catch {}\n}\n\nasync function draftWithAgent() {\n const rough = document.getElementById('m-rough').value.trim();\n const agentId = document.getElementById('m-draft-agent').value;\n const hint = document.getElementById('m-draft-hint');\n const goBtn = document.getElementById('m-draft-go');\n if (!rough) { hint.textContent = 'Describe the issue first.'; hint.className = 'hint err'; return; }\n if (!agentId) { hint.textContent = 'Pick a drafting agent.'; hint.className = 'hint err'; return; }\n goBtn.disabled = true;\n hint.textContent = 'Drafting with ' + agentId + '…'; hint.className = 'hint';\n\n // Build board context for convention-aware prompting.\n const board = state.current;\n const col = state.modal.targetColumnId ? board.columns.find(c => c.id === state.modal.targetColumnId) : null;\n const columnLabel = col && (col.scopedLabel || col.mapsToLabel);\n const ctx = {\n boardName: board.name,\n primaryLabel: board.primaryToolLabel,\n project: board.source && board.source.projects && board.source.projects[0],\n knownLabels: state.meta.labels || [],\n members: (state.meta.members || []).map(m => m.username),\n columnLabel: columnLabel,\n };\n\n try {\n const r = await fetch('/api/draft', {\n method: 'POST', headers: csrfHeaders(),\n body: JSON.stringify({ rough, agent: agentId, context: ctx }),\n });\n if (!r.ok) throw new Error((await r.json().catch(() => ({}))).error || r.statusText);\n const drafted = await r.json();\n // Populate the existing form fields.\n document.getElementById('m-title').value = drafted.title || '';\n document.getElementById('m-desc').value = drafted.description || '';\n state.modal.draft.title = drafted.title || '';\n state.modal.draft.description = drafted.description || '';\n state.modal.draft.labels = Array.isArray(drafted.labels) ? drafted.labels.slice() : state.modal.draft.labels;\n if (drafted.assigneeUsername) {\n const m = state.meta.members.find(x => x.username === drafted.assigneeUsername);\n if (!state.modal.draft.assignees.some(a => a.username === drafted.assigneeUsername)) {\n state.modal.draft.assignees.push({\n username: drafted.assigneeUsername, name: m && m.name, avatarUrl: m && m.avatarUrl,\n });\n }\n }\n paintModal(state.modal.draft);\n hint.textContent = 'Draft ready — review and edit before creating.';\n hint.className = 'hint ok';\n } catch (e) {\n hint.textContent = 'Draft failed: ' + e.message;\n hint.className = 'hint err';\n } finally {\n goBtn.disabled = false;\n }\n}\nfunction closeModal() {\n const m = document.getElementById('modal');\n m.classList.remove('open');\n m.setAttribute('aria-hidden', 'true');\n document.body.style.overflow = '';\n state.modal = { mode: null, id: null, original: null, draft: null };\n}\nfunction cloneForDraft(d) {\n return {\n id: d.id, title: d.title || '', description: d.description || '',\n labels: (d.labels || []).slice(), labelDetails: (d.labelDetails || []).slice(),\n assignees: (d.assignees || []).slice(),\n milestone: d.milestone || '',\n state: d.state || 'opened',\n stage: d.stage, url: d.url, createdAt: d.createdAt, updatedAt: d.updatedAt, author: d.author,\n };\n}\n\nfunction paintModal(d) {\n document.getElementById('m-title').value = d.title || '';\n document.getElementById('m-desc').value = d.description || '';\n\n document.querySelectorAll('#m-state button').forEach(b => {\n b.classList.toggle('active', b.dataset.state === (d.state || 'opened'));\n b.onclick = () => { state.modal.draft.state = b.dataset.state; paintModal(state.modal.draft); };\n });\n\n renderChipPicker('m-labels', d.labels || [], state.meta.labels, {\n chipStyle: (name) => {\n const match = (d.labelDetails || []).find(l => l.name === name);\n const bg = match && match.color ? match.color : labelColor(name);\n return { background: bg, color: pickTextColor(bg) };\n },\n onAdd: (name) => { if (!state.modal.draft.labels.includes(name)) { state.modal.draft.labels.push(name); paintModal(state.modal.draft); } },\n onRemove: (name) => { state.modal.draft.labels = state.modal.draft.labels.filter(l => l !== name); paintModal(state.modal.draft); },\n placeholder: 'Add label…',\n allowCustom: true,\n });\n\n const aNames = (d.assignees || []).map(a => a.username);\n renderChipPicker('m-assignees', aNames, state.meta.members.map(m => m.username), {\n chipLabel: (u) => {\n const m = state.meta.members.find(x => x.username === u) || (d.assignees || []).find(x => x.username === u);\n return (m && m.name) || u;\n },\n onAdd: (u) => {\n if (state.modal.draft.assignees.some(a => a.username === u)) return;\n const m = state.meta.members.find(x => x.username === u);\n state.modal.draft.assignees.push({ username: u, name: m && m.name, avatarUrl: m && m.avatarUrl });\n paintModal(state.modal.draft);\n },\n onRemove: (u) => { state.modal.draft.assignees = state.modal.draft.assignees.filter(a => a.username !== u); paintModal(state.modal.draft); },\n placeholder: 'Assign…',\n });\n\n const ms = document.getElementById('m-milestone');\n ms.innerHTML = '<option value=\"\">None</option>';\n for (const m of state.meta.milestones) {\n const o = document.createElement('option');\n o.value = m.title; o.textContent = m.title + (m.state === 'closed' ? ' (closed)' : '');\n if (m.title === d.milestone) o.selected = true;\n ms.appendChild(o);\n }\n if (d.milestone && !state.meta.milestones.some(m => m.title === d.milestone)) {\n const o = document.createElement('option'); o.value = d.milestone; o.textContent = d.milestone; o.selected = true;\n ms.appendChild(o);\n }\n ms.onchange = () => { state.modal.draft.milestone = ms.value; };\n\n document.getElementById('m-author').textContent = (d.author && (d.author.name || d.author.username)) || '—';\n document.getElementById('m-created').textContent = d.createdAt ? fmtDate(d.createdAt) : '—';\n document.getElementById('m-updated').textContent = d.updatedAt ? fmtDate(d.updatedAt) : '—';\n const link = document.getElementById('m-link');\n if (d.url) { link.href = d.url; link.style.display = ''; } else { link.style.display = 'none'; }\n}\n\nfunction renderChipPicker(elId, selected, options, opts) {\n const el = document.getElementById(elId);\n el.innerHTML = '';\n for (const name of selected) {\n const chip = document.createElement('span');\n chip.className = 'chip';\n chip.textContent = opts.chipLabel ? opts.chipLabel(name) : name;\n if (opts.chipStyle) {\n const st = opts.chipStyle(name);\n if (st.background) chip.style.background = st.background;\n if (st.color) chip.style.color = st.color;\n }\n const x = document.createElement('span');\n x.className = 'remove'; x.textContent = '✕'; x.title = 'Remove';\n x.onclick = (e) => { e.stopPropagation(); opts.onRemove(name); };\n chip.appendChild(x);\n el.appendChild(chip);\n }\n const dd = document.createElement('span');\n dd.className = 'dropdown';\n const btn = document.createElement('button');\n btn.className = 'add-chip'; btn.textContent = '+ ' + (opts.placeholder || 'Add');\n btn.type = 'button';\n btn.onclick = (e) => {\n e.stopPropagation();\n let menu = dd.querySelector('.menu');\n if (menu) { menu.remove(); return; }\n menu = document.createElement('div');\n menu.className = 'menu';\n const search = document.createElement('input');\n search.className = 'search'; search.type = 'search'; search.placeholder = opts.placeholder || 'Search…';\n menu.appendChild(search);\n const list = document.createElement('div');\n menu.appendChild(list);\n const paintList = (q) => {\n list.innerHTML = '';\n const ql = (q || '').toLowerCase();\n const filtered = options.filter(o => o.toLowerCase().includes(ql) && !selected.includes(o));\n if (filtered.length === 0 && !(opts.allowCustom && q)) {\n const empty = document.createElement('div'); empty.className = 'empty'; empty.textContent = 'No matches';\n list.appendChild(empty);\n }\n for (const o of filtered.slice(0, 50)) {\n const opt = document.createElement('div');\n opt.className = 'opt'; opt.textContent = opts.chipLabel ? opts.chipLabel(o) : o;\n opt.onclick = () => { opts.onAdd(o); menu.remove(); };\n list.appendChild(opt);\n }\n if (opts.allowCustom && q && !options.includes(q) && !selected.includes(q)) {\n const opt = document.createElement('div');\n opt.className = 'opt'; opt.textContent = 'Create \"' + q + '\"';\n opt.onclick = () => { opts.onAdd(q); menu.remove(); };\n list.appendChild(opt);\n }\n };\n search.oninput = (ev) => paintList(ev.target.value);\n paintList('');\n dd.appendChild(menu);\n setTimeout(() => search.focus(), 0);\n const away = (ev) => {\n if (!dd.contains(ev.target)) { menu.remove(); document.removeEventListener('click', away); }\n };\n setTimeout(() => document.addEventListener('click', away), 0);\n };\n dd.appendChild(btn);\n el.appendChild(dd);\n}\n\nasync function saveModal() {\n const saveBtn = document.getElementById('m-save');\n const hint = document.getElementById('m-hint');\n saveBtn.disabled = true;\n hint.textContent = 'Saving…';\n const d = state.modal.draft;\n d.title = document.getElementById('m-title').value.trim();\n d.description = document.getElementById('m-desc').value;\n if (!d.title) { hint.textContent = 'Title required'; saveBtn.disabled = false; return; }\n\n try {\n if (state.modal.mode === 'create') {\n const r = await fetch('/api/boards/' + state.current.id + '/items', {\n method: 'POST', headers: csrfHeaders(),\n body: JSON.stringify({\n title: d.title, description: d.description, labels: d.labels,\n assigneeUsernames: (d.assignees || []).map(a => a.username),\n milestoneTitle: d.milestone || undefined,\n }),\n });\n if (!r.ok) throw new Error((await r.json().catch(()=>({}))).error || r.statusText);\n toast('Created ✓', 'ok');\n } else {\n const orig = state.modal.original || {};\n const patch = {};\n if (d.title !== (orig.title || '')) patch.title = d.title;\n if (d.description !== (orig.description || '')) patch.description = d.description;\n const origLabels = (orig.labels || []).slice().sort().join('|');\n const newLabels = (d.labels || []).slice().sort().join('|');\n if (origLabels !== newLabels) patch.labels = d.labels;\n const origA = (orig.assignees || []).map(a => a.username).sort().join('|');\n const newA = (d.assignees || []).map(a => a.username).sort().join('|');\n if (origA !== newA) patch.assigneeUsernames = (d.assignees || []).map(a => a.username);\n if (d.milestone !== (orig.milestone || '')) patch.milestoneTitle = d.milestone || null;\n\n if (Object.keys(patch).length > 0) {\n const r = await fetch('/api/boards/' + state.current.id + '/items/' + encodeURIComponent(state.modal.id), {\n method: 'PATCH', headers: csrfHeaders(), body: JSON.stringify(patch),\n });\n if (!r.ok) throw new Error((await r.json().catch(()=>({}))).error || r.statusText);\n }\n // State change → move to the appropriate column (reuses transition logic on server).\n if (d.state !== (orig.state || 'opened')) {\n const fromCol = state.current.columns.find(c => c.id === orig.stage);\n const toCol = d.state === 'closed'\n ? state.current.columns.find(c => c.kind === 'closed')\n : state.current.columns.find(c => c.kind === 'open-backlog');\n if (toCol) {\n await fetch('/api/boards/' + state.current.id + '/items/' + encodeURIComponent(state.modal.id) + '/move', {\n method: 'PATCH', headers: csrfHeaders(),\n body: JSON.stringify({ from: fromCol && fromCol.id, to: toCol.id }),\n });\n }\n }\n toast('Saved ✓', 'ok');\n }\n closeModal();\n await loadItems();\n state.meta.labels = collectKnownLabels();\n } catch (e) {\n hint.textContent = 'Save failed: ' + e.message;\n } finally {\n saveBtn.disabled = false;\n }\n}\n\ndocument.getElementById('m-close').onclick = closeModal;\ndocument.getElementById('m-cancel').onclick = closeModal;\ndocument.getElementById('m-save').onclick = saveModal;\ndocument.querySelector('#modal .modal-backdrop').onclick = closeModal;\ndocument.getElementById('m-title').addEventListener('keydown', (e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) saveModal();\n});\nconst draftGo = document.getElementById('m-draft-go');\nif (draftGo) draftGo.onclick = draftWithAgent;\nconst roughEl = document.getElementById('m-rough');\nif (roughEl) roughEl.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) draftWithAgent();\n});\n\n// --- Agent activity panel (live polling of the daemon API) ---\n\nconst activity = {\n open: false, timer: null, daemonUrl: null, lastEventAt: 0, eventsRing: [],\n};\n\nfunction resolveDaemonUrl() {\n // Allow ?daemon=http://host:port override; fall back to localhost:18800 (MacBook) then 19900 (clawd).\n const q = new URLSearchParams(window.location.search).get('daemon');\n if (q) return q.replace(/\\\\/+\\$/, '');\n return localStorage.getItem('agentx.daemon') || 'http://localhost:18800';\n}\n\nfunction toggleActivity(force) {\n activity.open = typeof force === 'boolean' ? force : !activity.open;\n const panel = document.getElementById('activity-panel');\n const btn = document.getElementById('activityBtn');\n panel.classList.toggle('hidden', !activity.open);\n panel.setAttribute('aria-hidden', activity.open ? 'false' : 'true');\n btn.classList.toggle('on', activity.open);\n document.body.classList.toggle('panel-open', activity.open);\n if (activity.open) startActivity(); else stopActivity();\n}\n\nasync function startActivity() {\n activity.daemonUrl = resolveDaemonUrl();\n document.getElementById('activity-source').textContent = activity.daemonUrl.replace(/^https?:\\\\/\\\\//, '');\n await tickActivity();\n activity.timer = setInterval(tickActivity, 3000);\n}\nfunction stopActivity() {\n if (activity.timer) { clearInterval(activity.timer); activity.timer = null; }\n}\n\nasync function tickActivity() {\n const body = document.getElementById('activity-body');\n try {\n // Fetch /agents (always present) + /business/status (optional — 404 tolerated)\n const [agentsRes, bizRes] = await Promise.all([\n fetch(activity.daemonUrl + '/agents').catch(() => null),\n fetch(activity.daemonUrl + '/business/status').catch(() => null),\n ]);\n if (!agentsRes || !agentsRes.ok) throw new Error('daemon unreachable at ' + activity.daemonUrl);\n const agents = await agentsRes.json();\n const biz = bizRes && bizRes.ok ? await bizRes.json() : null;\n\n // Merge business data (currentItem, onClock, lastReport) per-agent when available.\n const bizByAgent = {};\n if (biz && Array.isArray(biz.employees)) for (const e of biz.employees) bizByAgent[e.agentId || e.id] = e;\n\n body.innerHTML = '';\n for (const a of agents) {\n const b = bizByAgent[a.id];\n body.appendChild(renderAgentCard(a, b));\n }\n if (agents.length === 0) {\n body.innerHTML = '<div class=\"empty\">No agents on this daemon.</div>';\n }\n // Events ring\n if (biz && biz.recentEvents) appendEvents(biz.recentEvents);\n } catch (e) {\n body.innerHTML = '<div class=\"empty\" style=\"color:var(--red)\">' + escapeHtml(e.message) + '</div>';\n }\n}\n\nfunction renderAgentCard(a, biz) {\n const card = document.createElement('div');\n card.className = 'agent-card';\n const active = (a.active || 0) > 0 || (biz && biz.onClock && biz.busy);\n const dotClass = (a.errors || 0) > 0 ? 'errored' : (active ? 'active' : '');\n const current = biz && biz.currentItem\n ? '<div class=\"current\">Working on <b>' + escapeHtml(biz.currentItem.title || biz.currentItem.id) + '</b></div>'\n : (active ? '<div class=\"current\"><b>' + a.active + ' active task' + (a.active === 1 ? '' : 's') + '</b></div>' : '');\n const modelLabel = a.model ? '<span class=\"model\" title=\"Model\">' + escapeHtml(shortenModelName(a.model)) + '</span>' : '';\n const lastLabel = a.lastActive\n ? '<span class=\"last\" title=\"' + escapeHtml(new Date(a.lastActive).toLocaleString()) + '\">last ' + escapeHtml(fmtAgoBoard(a.lastActive)) + '</span>'\n : '<span class=\"last muted\">never ran</span>';\n card.innerHTML =\n '<div class=\"top\">' +\n '<span class=\"dot ' + dotClass + '\"></span>' +\n '<span class=\"name\">' + escapeHtml(a.name || a.id) + '</span>' +\n '<span class=\"tier\">' + escapeHtml(a.tier || '') + '</span>' +\n '</div>' +\n '<div class=\"meta\">' + modelLabel + lastLabel + '</div>' +\n '<div class=\"stats\">' +\n '<span>Active <b>' + (a.active || 0) + '</b></span>' +\n '<span>Total <b>' + (a.total || 0) + '</b></span>' +\n '<span class=\"errors\">Errors <b>' + (a.errors || 0) + '</b></span>' +\n '</div>' + current;\n return card;\n}\n\nfunction shortenModelName(m) {\n if (!m) return '';\n return String(m)\n .replace(/^claude-/, '')\n .replace(/-\\\\d{8}$/, '')\n .replace(/\\\\[1m\\\\]$/, ' · 1M');\n}\n\nfunction fmtAgoBoard(iso) {\n const t = new Date(iso).getTime();\n if (!t) return '—';\n const s = Math.floor((Date.now() - t) / 1000);\n if (s < 60) return s + 's ago';\n const m = Math.floor(s / 60); if (m < 60) return m + 'm ago';\n const h = Math.floor(m / 60); if (h < 24) return h + 'h ago';\n const d = Math.floor(h / 24); return d + 'd ago';\n}\n\nfunction appendEvents(events) {\n const holder = document.getElementById('activity-events');\n const existingKeys = new Set([...holder.querySelectorAll('.evt')].map(e => e.dataset.key));\n for (const ev of events.slice(-50)) {\n const key = (ev.ts || '') + '|' + (ev.type || '') + '|' + (ev.agentId || '');\n if (existingKeys.has(key)) continue;\n const div = document.createElement('div');\n div.className = 'evt' + (ev.type && ev.type.includes('error') ? ' err' : '');\n div.dataset.key = key;\n div.innerHTML = '<span class=\"ts\">' + (ev.ts ? new Date(ev.ts).toLocaleTimeString() : '') + '</span> ' +\n '<span class=\"who\">' + escapeHtml(ev.agentId || '—') + '</span> ' +\n '<span class=\"what\">' + escapeHtml(ev.summary || ev.type || '') + '</span>';\n holder.appendChild(div);\n }\n // Keep last 50 entries\n while (holder.children.length > 50) holder.removeChild(holder.firstChild);\n holder.scrollTop = holder.scrollHeight;\n}\n\nloadBoards().catch(e => toast('Init failed: ' + e.message));\n`\n","// Vendored SortableJS 1.15.2 — MIT, https://github.com/SortableJS/Sortable\n// Served by board-dashboard at /sortable.min.js. Do not edit by hand.\nexport const SORTABLE_JS = \"/*! Sortable 1.15.2 - MIT | git://github.com/SortableJS/Sortable.git */\\n!function(t,e){\\\"object\\\"==typeof exports&&\\\"undefined\\\"!=typeof module?module.exports=e():\\\"function\\\"==typeof define&&define.amd?define(e):(t=t||self).Sortable=e()}(this,function(){\\\"use strict\\\";function e(e,t){var n,o=Object.keys(e);return Object.getOwnPropertySymbols&&(n=Object.getOwnPropertySymbols(e),t&&(n=n.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),o.push.apply(o,n)),o}function I(o){for(var t=1;t<arguments.length;t++){var i=null!=arguments[t]?arguments[t]:{};t%2?e(Object(i),!0).forEach(function(t){var e,n;e=o,t=i[n=t],n in e?Object.defineProperty(e,n,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[n]=t}):Object.getOwnPropertyDescriptors?Object.defineProperties(o,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach(function(t){Object.defineProperty(o,t,Object.getOwnPropertyDescriptor(i,t))})}return o}function o(t){return(o=\\\"function\\\"==typeof Symbol&&\\\"symbol\\\"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&\\\"function\\\"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?\\\"symbol\\\":typeof t})(t)}function a(){return(a=Object.assign||function(t){for(var e=1;e<arguments.length;e++){var n,o=arguments[e];for(n in o)Object.prototype.hasOwnProperty.call(o,n)&&(t[n]=o[n])}return t}).apply(this,arguments)}function i(t,e){if(null==t)return{};var n,o=function(t,e){if(null==t)return{};for(var n,o={},i=Object.keys(t),r=0;r<i.length;r++)n=i[r],0<=e.indexOf(n)||(o[n]=t[n]);return o}(t,e);if(Object.getOwnPropertySymbols)for(var i=Object.getOwnPropertySymbols(t),r=0;r<i.length;r++)n=i[r],0<=e.indexOf(n)||Object.prototype.propertyIsEnumerable.call(t,n)&&(o[n]=t[n]);return o}function r(t){return function(t){if(Array.isArray(t))return l(t)}(t)||function(t){if(\\\"undefined\\\"!=typeof Symbol&&null!=t[Symbol.iterator]||null!=t[\\\"@@iterator\\\"])return Array.from(t)}(t)||function(t,e){if(t){if(\\\"string\\\"==typeof t)return l(t,e);var n=Object.prototype.toString.call(t).slice(8,-1);return\\\"Map\\\"===(n=\\\"Object\\\"===n&&t.constructor?t.constructor.name:n)||\\\"Set\\\"===n?Array.from(t):\\\"Arguments\\\"===n||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)?l(t,e):void 0}}(t)||function(){throw new TypeError(\\\"Invalid attempt to spread non-iterable instance.\\\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.\\\")}()}function l(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,o=new Array(e);n<e;n++)o[n]=t[n];return o}function t(t){if(\\\"undefined\\\"!=typeof window&&window.navigator)return!!navigator.userAgent.match(t)}var y=t(/(?:Trident.*rv[ :]?11\\\\.|msie|iemobile|Windows Phone)/i),w=t(/Edge/i),s=t(/firefox/i),u=t(/safari/i)&&!t(/chrome/i)&&!t(/android/i),n=t(/iP(ad|od|hone)/i),c=t(/chrome/i)&&t(/android/i),d={capture:!1,passive:!1};function h(t,e,n){t.addEventListener(e,n,!y&&d)}function f(t,e,n){t.removeEventListener(e,n,!y&&d)}function p(t,e){if(e&&(\\\">\\\"===e[0]&&(e=e.substring(1)),t))try{if(t.matches)return t.matches(e);if(t.msMatchesSelector)return t.msMatchesSelector(e);if(t.webkitMatchesSelector)return t.webkitMatchesSelector(e)}catch(t){return}}function P(t,e,n,o){if(t){n=n||document;do{if(null!=e&&(\\\">\\\"!==e[0]||t.parentNode===n)&&p(t,e)||o&&t===n)return t}while(t!==n&&(t=(i=t).host&&i!==document&&i.host.nodeType?i.host:i.parentNode))}var i;return null}var g,m=/\\\\s+/g;function k(t,e,n){var o;t&&e&&(t.classList?t.classList[n?\\\"add\\\":\\\"remove\\\"](e):(o=(\\\" \\\"+t.className+\\\" \\\").replace(m,\\\" \\\").replace(\\\" \\\"+e+\\\" \\\",\\\" \\\"),t.className=(o+(n?\\\" \\\"+e:\\\"\\\")).replace(m,\\\" \\\")))}function R(t,e,n){var o=t&&t.style;if(o){if(void 0===n)return document.defaultView&&document.defaultView.getComputedStyle?n=document.defaultView.getComputedStyle(t,\\\"\\\"):t.currentStyle&&(n=t.currentStyle),void 0===e?n:n[e];o[e=!(e in o||-1!==e.indexOf(\\\"webkit\\\"))?\\\"-webkit-\\\"+e:e]=n+(\\\"string\\\"==typeof n?\\\"\\\":\\\"px\\\")}}function v(t,e){var n=\\\"\\\";if(\\\"string\\\"==typeof t)n=t;else do{var o=R(t,\\\"transform\\\")}while(o&&\\\"none\\\"!==o&&(n=o+\\\" \\\"+n),!e&&(t=t.parentNode));var i=window.DOMMatrix||window.WebKitCSSMatrix||window.CSSMatrix||window.MSCSSMatrix;return i&&new i(n)}function b(t,e,n){if(t){var o=t.getElementsByTagName(e),i=0,r=o.length;if(n)for(;i<r;i++)n(o[i],i);return o}return[]}function O(){var t=document.scrollingElement;return t||document.documentElement}function X(t,e,n,o,i){if(t.getBoundingClientRect||t===window){var r,a,l,s,c,u,d=t!==window&&t.parentNode&&t!==O()?(a=(r=t.getBoundingClientRect()).top,l=r.left,s=r.bottom,c=r.right,u=r.height,r.width):(l=a=0,s=window.innerHeight,c=window.innerWidth,u=window.innerHeight,window.innerWidth);if((e||n)&&t!==window&&(i=i||t.parentNode,!y))do{if(i&&i.getBoundingClientRect&&(\\\"none\\\"!==R(i,\\\"transform\\\")||n&&\\\"static\\\"!==R(i,\\\"position\\\"))){var h=i.getBoundingClientRect();a-=h.top+parseInt(R(i,\\\"border-top-width\\\")),l-=h.left+parseInt(R(i,\\\"border-left-width\\\")),s=a+r.height,c=l+r.width;break}}while(i=i.parentNode);return o&&t!==window&&(o=(e=v(i||t))&&e.a,t=e&&e.d,e&&(s=(a/=t)+(u/=t),c=(l/=o)+(d/=o))),{top:a,left:l,bottom:s,right:c,width:d,height:u}}}function Y(t,e,n){for(var o=M(t,!0),i=X(t)[e];o;){var r=X(o)[n];if(!(\\\"top\\\"===n||\\\"left\\\"===n?r<=i:i<=r))return o;if(o===O())break;o=M(o,!1)}return!1}function B(t,e,n,o){for(var i=0,r=0,a=t.children;r<a.length;){if(\\\"none\\\"!==a[r].style.display&&a[r]!==Ft.ghost&&(o||a[r]!==Ft.dragged)&&P(a[r],n.draggable,t,!1)){if(i===e)return a[r];i++}r++}return null}function F(t,e){for(var n=t.lastElementChild;n&&(n===Ft.ghost||\\\"none\\\"===R(n,\\\"display\\\")||e&&!p(n,e));)n=n.previousElementSibling;return n||null}function j(t,e){var n=0;if(!t||!t.parentNode)return-1;for(;t=t.previousElementSibling;)\\\"TEMPLATE\\\"===t.nodeName.toUpperCase()||t===Ft.clone||e&&!p(t,e)||n++;return n}function E(t){var e=0,n=0,o=O();if(t)do{var i=v(t),r=i.a,i=i.d}while(e+=t.scrollLeft*r,n+=t.scrollTop*i,t!==o&&(t=t.parentNode));return[e,n]}function M(t,e){if(!t||!t.getBoundingClientRect)return O();var n=t,o=!1;do{if(n.clientWidth<n.scrollWidth||n.clientHeight<n.scrollHeight){var i=R(n);if(n.clientWidth<n.scrollWidth&&(\\\"auto\\\"==i.overflowX||\\\"scroll\\\"==i.overflowX)||n.clientHeight<n.scrollHeight&&(\\\"auto\\\"==i.overflowY||\\\"scroll\\\"==i.overflowY)){if(!n.getBoundingClientRect||n===document.body)return O();if(o||e)return n;o=!0}}}while(n=n.parentNode);return O()}function D(t,e){return Math.round(t.top)===Math.round(e.top)&&Math.round(t.left)===Math.round(e.left)&&Math.round(t.height)===Math.round(e.height)&&Math.round(t.width)===Math.round(e.width)}function S(e,n){return function(){var t;g||(1===(t=arguments).length?e.call(this,t[0]):e.apply(this,t),g=setTimeout(function(){g=void 0},n))}}function H(t,e,n){t.scrollLeft+=e,t.scrollTop+=n}function _(t){var e=window.Polymer,n=window.jQuery||window.Zepto;return e&&e.dom?e.dom(t).cloneNode(!0):n?n(t).clone(!0)[0]:t.cloneNode(!0)}function C(t,e){R(t,\\\"position\\\",\\\"absolute\\\"),R(t,\\\"top\\\",e.top),R(t,\\\"left\\\",e.left),R(t,\\\"width\\\",e.width),R(t,\\\"height\\\",e.height)}function T(t){R(t,\\\"position\\\",\\\"\\\"),R(t,\\\"top\\\",\\\"\\\"),R(t,\\\"left\\\",\\\"\\\"),R(t,\\\"width\\\",\\\"\\\"),R(t,\\\"height\\\",\\\"\\\")}function L(n,o,i){var r={};return Array.from(n.children).forEach(function(t){var e;P(t,o.draggable,n,!1)&&!t.animated&&t!==i&&(e=X(t),r.left=Math.min(null!==(t=r.left)&&void 0!==t?t:1/0,e.left),r.top=Math.min(null!==(t=r.top)&&void 0!==t?t:1/0,e.top),r.right=Math.max(null!==(t=r.right)&&void 0!==t?t:-1/0,e.right),r.bottom=Math.max(null!==(t=r.bottom)&&void 0!==t?t:-1/0,e.bottom))}),r.width=r.right-r.left,r.height=r.bottom-r.top,r.x=r.left,r.y=r.top,r}var K=\\\"Sortable\\\"+(new Date).getTime();function x(){var e,o=[];return{captureAnimationState:function(){o=[],this.options.animation&&[].slice.call(this.el.children).forEach(function(t){var e,n;\\\"none\\\"!==R(t,\\\"display\\\")&&t!==Ft.ghost&&(o.push({target:t,rect:X(t)}),e=I({},o[o.length-1].rect),!t.thisAnimationDuration||(n=v(t,!0))&&(e.top-=n.f,e.left-=n.e),t.fromRect=e)})},addAnimationState:function(t){o.push(t)},removeAnimationState:function(t){o.splice(function(t,e){for(var n in t)if(t.hasOwnProperty(n))for(var o in e)if(e.hasOwnProperty(o)&&e[o]===t[n][o])return Number(n);return-1}(o,{target:t}),1)},animateAll:function(t){var c=this;if(!this.options.animation)return clearTimeout(e),void(\\\"function\\\"==typeof t&&t());var u=!1,d=0;o.forEach(function(t){var e=0,n=t.target,o=n.fromRect,i=X(n),r=n.prevFromRect,a=n.prevToRect,l=t.rect,s=v(n,!0);s&&(i.top-=s.f,i.left-=s.e),n.toRect=i,n.thisAnimationDuration&&D(r,i)&&!D(o,i)&&(l.top-i.top)/(l.left-i.left)==(o.top-i.top)/(o.left-i.left)&&(t=l,s=r,r=a,a=c.options,e=Math.sqrt(Math.pow(s.top-t.top,2)+Math.pow(s.left-t.left,2))/Math.sqrt(Math.pow(s.top-r.top,2)+Math.pow(s.left-r.left,2))*a.animation),D(i,o)||(n.prevFromRect=o,n.prevToRect=i,e=e||c.options.animation,c.animate(n,l,i,e)),e&&(u=!0,d=Math.max(d,e),clearTimeout(n.animationResetTimer),n.animationResetTimer=setTimeout(function(){n.animationTime=0,n.prevFromRect=null,n.fromRect=null,n.prevToRect=null,n.thisAnimationDuration=null},e),n.thisAnimationDuration=e)}),clearTimeout(e),u?e=setTimeout(function(){\\\"function\\\"==typeof t&&t()},d):\\\"function\\\"==typeof t&&t(),o=[]},animate:function(t,e,n,o){var i,r;o&&(R(t,\\\"transition\\\",\\\"\\\"),R(t,\\\"transform\\\",\\\"\\\"),i=(r=v(this.el))&&r.a,r=r&&r.d,i=(e.left-n.left)/(i||1),r=(e.top-n.top)/(r||1),t.animatingX=!!i,t.animatingY=!!r,R(t,\\\"transform\\\",\\\"translate3d(\\\"+i+\\\"px,\\\"+r+\\\"px,0)\\\"),this.forRepaintDummy=t.offsetWidth,R(t,\\\"transition\\\",\\\"transform \\\"+o+\\\"ms\\\"+(this.options.easing?\\\" \\\"+this.options.easing:\\\"\\\")),R(t,\\\"transform\\\",\\\"translate3d(0,0,0)\\\"),\\\"number\\\"==typeof t.animated&&clearTimeout(t.animated),t.animated=setTimeout(function(){R(t,\\\"transition\\\",\\\"\\\"),R(t,\\\"transform\\\",\\\"\\\"),t.animated=!1,t.animatingX=!1,t.animatingY=!1},o))}}}var A=[],N={initializeByDefault:!0},W={mount:function(e){for(var t in N)!N.hasOwnProperty(t)||t in e||(e[t]=N[t]);A.forEach(function(t){if(t.pluginName===e.pluginName)throw\\\"Sortable: Cannot mount plugin \\\".concat(e.pluginName,\\\" more than once\\\")}),A.push(e)},pluginEvent:function(e,n,o){var t=this;this.eventCanceled=!1,o.cancel=function(){t.eventCanceled=!0};var i=e+\\\"Global\\\";A.forEach(function(t){n[t.pluginName]&&(n[t.pluginName][i]&&n[t.pluginName][i](I({sortable:n},o)),n.options[t.pluginName]&&n[t.pluginName][e]&&n[t.pluginName][e](I({sortable:n},o)))})},initializePlugins:function(n,o,i,t){for(var e in A.forEach(function(t){var e=t.pluginName;(n.options[e]||t.initializeByDefault)&&((t=new t(n,o,n.options)).sortable=n,t.options=n.options,n[e]=t,a(i,t.defaults))}),n.options){var r;n.options.hasOwnProperty(e)&&(void 0!==(r=this.modifyOption(n,e,n.options[e]))&&(n.options[e]=r))}},getEventProperties:function(e,n){var o={};return A.forEach(function(t){\\\"function\\\"==typeof t.eventProperties&&a(o,t.eventProperties.call(n[t.pluginName],e))}),o},modifyOption:function(e,n,o){var i;return A.forEach(function(t){e[t.pluginName]&&t.optionListeners&&\\\"function\\\"==typeof t.optionListeners[n]&&(i=t.optionListeners[n].call(e[t.pluginName],o))}),i}};function z(t){var e=t.sortable,n=t.rootEl,o=t.name,i=t.targetEl,r=t.cloneEl,a=t.toEl,l=t.fromEl,s=t.oldIndex,c=t.newIndex,u=t.oldDraggableIndex,d=t.newDraggableIndex,h=t.originalEvent,f=t.putSortable,p=t.extraEventProperties;if(e=e||n&&n[K]){var g,m=e.options,t=\\\"on\\\"+o.charAt(0).toUpperCase()+o.substr(1);!window.CustomEvent||y||w?(g=document.createEvent(\\\"Event\\\")).initEvent(o,!0,!0):g=new CustomEvent(o,{bubbles:!0,cancelable:!0}),g.to=a||n,g.from=l||n,g.item=i||n,g.clone=r,g.oldIndex=s,g.newIndex=c,g.oldDraggableIndex=u,g.newDraggableIndex=d,g.originalEvent=h,g.pullMode=f?f.lastPutMode:void 0;var v,b=I(I({},p),W.getEventProperties(o,e));for(v in b)g[v]=b[v];n&&n.dispatchEvent(g),m[t]&&m[t].call(e,g)}}function G(t,e){var n=(o=2<arguments.length&&void 0!==arguments[2]?arguments[2]:{}).evt,o=i(o,U);W.pluginEvent.bind(Ft)(t,e,I({dragEl:V,parentEl:Z,ghostEl:$,rootEl:Q,nextEl:J,lastDownEl:tt,cloneEl:et,cloneHidden:nt,dragStarted:gt,putSortable:st,activeSortable:Ft.active,originalEvent:n,oldIndex:ot,oldDraggableIndex:rt,newIndex:it,newDraggableIndex:at,hideGhostForTarget:Rt,unhideGhostForTarget:Xt,cloneNowHidden:function(){nt=!0},cloneNowShown:function(){nt=!1},dispatchSortableEvent:function(t){q({sortable:e,name:t,originalEvent:n})}},o))}var U=[\\\"evt\\\"];function q(t){z(I({putSortable:st,cloneEl:et,targetEl:V,rootEl:Q,oldIndex:ot,oldDraggableIndex:rt,newIndex:it,newDraggableIndex:at},t))}var V,Z,$,Q,J,tt,et,nt,ot,it,rt,at,lt,st,ct,ut,dt,ht,ft,pt,gt,mt,vt,bt,yt,wt=!1,Et=!1,Dt=[],St=!1,_t=!1,Ct=[],Tt=!1,xt=[],Ot=\\\"undefined\\\"!=typeof document,Mt=n,At=w||y?\\\"cssFloat\\\":\\\"float\\\",Nt=Ot&&!c&&!n&&\\\"draggable\\\"in document.createElement(\\\"div\\\"),It=function(){if(Ot){if(y)return!1;var t=document.createElement(\\\"x\\\");return t.style.cssText=\\\"pointer-events:auto\\\",\\\"auto\\\"===t.style.pointerEvents}}(),Pt=function(t,e){var n=R(t),o=parseInt(n.width)-parseInt(n.paddingLeft)-parseInt(n.paddingRight)-parseInt(n.borderLeftWidth)-parseInt(n.borderRightWidth),i=B(t,0,e),r=B(t,1,e),a=i&&R(i),l=r&&R(r),s=a&&parseInt(a.marginLeft)+parseInt(a.marginRight)+X(i).width,t=l&&parseInt(l.marginLeft)+parseInt(l.marginRight)+X(r).width;if(\\\"flex\\\"===n.display)return\\\"column\\\"===n.flexDirection||\\\"column-reverse\\\"===n.flexDirection?\\\"vertical\\\":\\\"horizontal\\\";if(\\\"grid\\\"===n.display)return n.gridTemplateColumns.split(\\\" \\\").length<=1?\\\"vertical\\\":\\\"horizontal\\\";if(i&&a.float&&\\\"none\\\"!==a.float){e=\\\"left\\\"===a.float?\\\"left\\\":\\\"right\\\";return!r||\\\"both\\\"!==l.clear&&l.clear!==e?\\\"horizontal\\\":\\\"vertical\\\"}return i&&(\\\"block\\\"===a.display||\\\"flex\\\"===a.display||\\\"table\\\"===a.display||\\\"grid\\\"===a.display||o<=s&&\\\"none\\\"===n[At]||r&&\\\"none\\\"===n[At]&&o<s+t)?\\\"vertical\\\":\\\"horizontal\\\"},kt=function(t){function l(r,a){return function(t,e,n,o){var i=t.options.group.name&&e.options.group.name&&t.options.group.name===e.options.group.name;if(null==r&&(a||i))return!0;if(null==r||!1===r)return!1;if(a&&\\\"clone\\\"===r)return r;if(\\\"function\\\"==typeof r)return l(r(t,e,n,o),a)(t,e,n,o);e=(a?t:e).options.group.name;return!0===r||\\\"string\\\"==typeof r&&r===e||r.join&&-1<r.indexOf(e)}}var e={},n=t.group;n&&\\\"object\\\"==o(n)||(n={name:n}),e.name=n.name,e.checkPull=l(n.pull,!0),e.checkPut=l(n.put),e.revertClone=n.revertClone,t.group=e},Rt=function(){!It&&$&&R($,\\\"display\\\",\\\"none\\\")},Xt=function(){!It&&$&&R($,\\\"display\\\",\\\"\\\")};Ot&&!c&&document.addEventListener(\\\"click\\\",function(t){if(Et)return t.preventDefault(),t.stopPropagation&&t.stopPropagation(),t.stopImmediatePropagation&&t.stopImmediatePropagation(),Et=!1},!0);function Yt(t){if(V){t=t.touches?t.touches[0]:t;var e=(i=t.clientX,r=t.clientY,Dt.some(function(t){var e=t[K].options.emptyInsertThreshold;if(e&&!F(t)){var n=X(t),o=i>=n.left-e&&i<=n.right+e,e=r>=n.top-e&&r<=n.bottom+e;return o&&e?a=t:void 0}}),a);if(e){var n,o={};for(n in t)t.hasOwnProperty(n)&&(o[n]=t[n]);o.target=o.rootEl=e,o.preventDefault=void 0,o.stopPropagation=void 0,e[K]._onDragOver(o)}}var i,r,a}function Bt(t){V&&V.parentNode[K]._isOutsideThisEl(t.target)}function Ft(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw\\\"Sortable: `el` must be an HTMLElement, not \\\".concat({}.toString.call(t));this.el=t,this.options=e=a({},e),t[K]=this;var n,o,i={group:null,sort:!0,disabled:!1,store:null,handle:null,draggable:/^[uo]l$/i.test(t.nodeName)?\\\">li\\\":\\\">*\\\",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return Pt(t,this.options)},ghostClass:\\\"sortable-ghost\\\",chosenClass:\\\"sortable-chosen\\\",dragClass:\\\"sortable-drag\\\",ignore:\\\"a, img\\\",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData(\\\"Text\\\",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:\\\"data-id\\\",delay:0,delayOnTouchOnly:!1,touchStartThreshold:(Number.parseInt?Number:window).parseInt(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:\\\"sortable-fallback\\\",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==Ft.supportPointer&&\\\"PointerEvent\\\"in window&&!u,emptyInsertThreshold:5};for(n in W.initializePlugins(this,t,i),i)n in e||(e[n]=i[n]);for(o in kt(e),this)\\\"_\\\"===o.charAt(0)&&\\\"function\\\"==typeof this[o]&&(this[o]=this[o].bind(this));this.nativeDraggable=!e.forceFallback&&Nt,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?h(t,\\\"pointerdown\\\",this._onTapStart):(h(t,\\\"mousedown\\\",this._onTapStart),h(t,\\\"touchstart\\\",this._onTapStart)),this.nativeDraggable&&(h(t,\\\"dragover\\\",this),h(t,\\\"dragenter\\\",this)),Dt.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[]),a(this,x())}function jt(t,e,n,o,i,r,a,l){var s,c,u=t[K],d=u.options.onMove;return!window.CustomEvent||y||w?(s=document.createEvent(\\\"Event\\\")).initEvent(\\\"move\\\",!0,!0):s=new CustomEvent(\\\"move\\\",{bubbles:!0,cancelable:!0}),s.to=e,s.from=t,s.dragged=n,s.draggedRect=o,s.related=i||e,s.relatedRect=r||X(e),s.willInsertAfter=l,s.originalEvent=a,t.dispatchEvent(s),c=d?d.call(u,s,a):c}function Ht(t){t.draggable=!1}function Lt(){Tt=!1}function Kt(t){return setTimeout(t,0)}function Wt(t){return clearTimeout(t)}Ft.prototype={constructor:Ft,_isOutsideThisEl:function(t){this.el.contains(t)||t===this.el||(mt=null)},_getDirection:function(t,e){return\\\"function\\\"==typeof this.options.direction?this.options.direction.call(this,t,e,V):this.options.direction},_onTapStart:function(e){if(e.cancelable){var n=this,o=this.el,t=this.options,i=t.preventOnFilter,r=e.type,a=e.touches&&e.touches[0]||e.pointerType&&\\\"touch\\\"===e.pointerType&&e,l=(a||e).target,s=e.target.shadowRoot&&(e.path&&e.path[0]||e.composedPath&&e.composedPath()[0])||l,c=t.filter;if(!function(t){xt.length=0;var e=t.getElementsByTagName(\\\"input\\\"),n=e.length;for(;n--;){var o=e[n];o.checked&&xt.push(o)}}(o),!V&&!(/mousedown|pointerdown/.test(r)&&0!==e.button||t.disabled)&&!s.isContentEditable&&(this.nativeDraggable||!u||!l||\\\"SELECT\\\"!==l.tagName.toUpperCase())&&!((l=P(l,t.draggable,o,!1))&&l.animated||tt===l)){if(ot=j(l),rt=j(l,t.draggable),\\\"function\\\"==typeof c){if(c.call(this,e,l,this))return q({sortable:n,rootEl:s,name:\\\"filter\\\",targetEl:l,toEl:o,fromEl:o}),G(\\\"filter\\\",n,{evt:e}),void(i&&e.cancelable&&e.preventDefault())}else if(c=c&&c.split(\\\",\\\").some(function(t){if(t=P(s,t.trim(),o,!1))return q({sortable:n,rootEl:t,name:\\\"filter\\\",targetEl:l,fromEl:o,toEl:o}),G(\\\"filter\\\",n,{evt:e}),!0}))return void(i&&e.cancelable&&e.preventDefault());t.handle&&!P(s,t.handle,o,!1)||this._prepareDragStart(e,a,l)}}},_prepareDragStart:function(t,e,n){var o,i=this,r=i.el,a=i.options,l=r.ownerDocument;n&&!V&&n.parentNode===r&&(o=X(n),Q=r,Z=(V=n).parentNode,J=V.nextSibling,tt=n,lt=a.group,ct={target:Ft.dragged=V,clientX:(e||t).clientX,clientY:(e||t).clientY},ft=ct.clientX-o.left,pt=ct.clientY-o.top,this._lastX=(e||t).clientX,this._lastY=(e||t).clientY,V.style[\\\"will-change\\\"]=\\\"all\\\",o=function(){G(\\\"delayEnded\\\",i,{evt:t}),Ft.eventCanceled?i._onDrop():(i._disableDelayedDragEvents(),!s&&i.nativeDraggable&&(V.draggable=!0),i._triggerDragStart(t,e),q({sortable:i,name:\\\"choose\\\",originalEvent:t}),k(V,a.chosenClass,!0))},a.ignore.split(\\\",\\\").forEach(function(t){b(V,t.trim(),Ht)}),h(l,\\\"dragover\\\",Yt),h(l,\\\"mousemove\\\",Yt),h(l,\\\"touchmove\\\",Yt),h(l,\\\"mouseup\\\",i._onDrop),h(l,\\\"touchend\\\",i._onDrop),h(l,\\\"touchcancel\\\",i._onDrop),s&&this.nativeDraggable&&(this.options.touchStartThreshold=4,V.draggable=!0),G(\\\"delayStart\\\",this,{evt:t}),!a.delay||a.delayOnTouchOnly&&!e||this.nativeDraggable&&(w||y)?o():Ft.eventCanceled?this._onDrop():(h(l,\\\"mouseup\\\",i._disableDelayedDrag),h(l,\\\"touchend\\\",i._disableDelayedDrag),h(l,\\\"touchcancel\\\",i._disableDelayedDrag),h(l,\\\"mousemove\\\",i._delayedDragTouchMoveHandler),h(l,\\\"touchmove\\\",i._delayedDragTouchMoveHandler),a.supportPointer&&h(l,\\\"pointermove\\\",i._delayedDragTouchMoveHandler),i._dragStartTimer=setTimeout(o,a.delay)))},_delayedDragTouchMoveHandler:function(t){t=t.touches?t.touches[0]:t;Math.max(Math.abs(t.clientX-this._lastX),Math.abs(t.clientY-this._lastY))>=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){V&&Ht(V),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;f(t,\\\"mouseup\\\",this._disableDelayedDrag),f(t,\\\"touchend\\\",this._disableDelayedDrag),f(t,\\\"touchcancel\\\",this._disableDelayedDrag),f(t,\\\"mousemove\\\",this._delayedDragTouchMoveHandler),f(t,\\\"touchmove\\\",this._delayedDragTouchMoveHandler),f(t,\\\"pointermove\\\",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||\\\"touch\\\"==t.pointerType&&t,!this.nativeDraggable||e?this.options.supportPointer?h(document,\\\"pointermove\\\",this._onTouchMove):h(document,e?\\\"touchmove\\\":\\\"mousemove\\\",this._onTouchMove):(h(V,\\\"dragend\\\",this),h(Q,\\\"dragstart\\\",this._onDragStart));try{document.selection?Kt(function(){document.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){var n;wt=!1,Q&&V?(G(\\\"dragStarted\\\",this,{evt:e}),this.nativeDraggable&&h(document,\\\"dragover\\\",Bt),n=this.options,t||k(V,n.dragClass,!1),k(V,n.ghostClass,!0),Ft.active=this,t&&this._appendGhost(),q({sortable:this,name:\\\"start\\\",originalEvent:e})):this._nulling()},_emulateDragOver:function(){if(ut){this._lastX=ut.clientX,this._lastY=ut.clientY,Rt();for(var t=document.elementFromPoint(ut.clientX,ut.clientY),e=t;t&&t.shadowRoot&&(t=t.shadowRoot.elementFromPoint(ut.clientX,ut.clientY))!==e;)e=t;if(V.parentNode[K]._isOutsideThisEl(t),e)do{if(e[K])if(e[K]._onDragOver({clientX:ut.clientX,clientY:ut.clientY,target:t,rootEl:e})&&!this.options.dragoverBubble)break}while(e=(t=e).parentNode);Xt()}},_onTouchMove:function(t){if(ct){var e=this.options,n=e.fallbackTolerance,o=e.fallbackOffset,i=t.touches?t.touches[0]:t,r=$&&v($,!0),a=$&&r&&r.a,l=$&&r&&r.d,e=Mt&&yt&&E(yt),a=(i.clientX-ct.clientX+o.x)/(a||1)+(e?e[0]-Ct[0]:0)/(a||1),l=(i.clientY-ct.clientY+o.y)/(l||1)+(e?e[1]-Ct[1]:0)/(l||1);if(!Ft.active&&!wt){if(n&&Math.max(Math.abs(i.clientX-this._lastX),Math.abs(i.clientY-this._lastY))<n)return;this._onDragStart(t,!0)}$&&(r?(r.e+=a-(dt||0),r.f+=l-(ht||0)):r={a:1,b:0,c:0,d:1,e:a,f:l},r=\\\"matrix(\\\".concat(r.a,\\\",\\\").concat(r.b,\\\",\\\").concat(r.c,\\\",\\\").concat(r.d,\\\",\\\").concat(r.e,\\\",\\\").concat(r.f,\\\")\\\"),R($,\\\"webkitTransform\\\",r),R($,\\\"mozTransform\\\",r),R($,\\\"msTransform\\\",r),R($,\\\"transform\\\",r),dt=a,ht=l,ut=i),t.cancelable&&t.preventDefault()}},_appendGhost:function(){if(!$){var t=this.options.fallbackOnBody?document.body:Q,e=X(V,!0,Mt,!0,t),n=this.options;if(Mt){for(yt=t;\\\"static\\\"===R(yt,\\\"position\\\")&&\\\"none\\\"===R(yt,\\\"transform\\\")&&yt!==document;)yt=yt.parentNode;yt!==document.body&&yt!==document.documentElement?(yt===document&&(yt=O()),e.top+=yt.scrollTop,e.left+=yt.scrollLeft):yt=O(),Ct=E(yt)}k($=V.cloneNode(!0),n.ghostClass,!1),k($,n.fallbackClass,!0),k($,n.dragClass,!0),R($,\\\"transition\\\",\\\"\\\"),R($,\\\"transform\\\",\\\"\\\"),R($,\\\"box-sizing\\\",\\\"border-box\\\"),R($,\\\"margin\\\",0),R($,\\\"top\\\",e.top),R($,\\\"left\\\",e.left),R($,\\\"width\\\",e.width),R($,\\\"height\\\",e.height),R($,\\\"opacity\\\",\\\"0.8\\\"),R($,\\\"position\\\",Mt?\\\"absolute\\\":\\\"fixed\\\"),R($,\\\"zIndex\\\",\\\"100000\\\"),R($,\\\"pointerEvents\\\",\\\"none\\\"),Ft.ghost=$,t.appendChild($),R($,\\\"transform-origin\\\",ft/parseInt($.style.width)*100+\\\"% \\\"+pt/parseInt($.style.height)*100+\\\"%\\\")}},_onDragStart:function(t,e){var n=this,o=t.dataTransfer,i=n.options;G(\\\"dragStart\\\",this,{evt:t}),Ft.eventCanceled?this._onDrop():(G(\\\"setupClone\\\",this),Ft.eventCanceled||((et=_(V)).removeAttribute(\\\"id\\\"),et.draggable=!1,et.style[\\\"will-change\\\"]=\\\"\\\",this._hideClone(),k(et,this.options.chosenClass,!1),Ft.clone=et),n.cloneId=Kt(function(){G(\\\"clone\\\",n),Ft.eventCanceled||(n.options.removeCloneOnHide||Q.insertBefore(et,V),n._hideClone(),q({sortable:n,name:\\\"clone\\\"}))}),e||k(V,i.dragClass,!0),e?(Et=!0,n._loopId=setInterval(n._emulateDragOver,50)):(f(document,\\\"mouseup\\\",n._onDrop),f(document,\\\"touchend\\\",n._onDrop),f(document,\\\"touchcancel\\\",n._onDrop),o&&(o.effectAllowed=\\\"move\\\",i.setData&&i.setData.call(n,o,V)),h(document,\\\"drop\\\",n),R(V,\\\"transform\\\",\\\"translateZ(0)\\\")),wt=!0,n._dragStartId=Kt(n._dragStarted.bind(n,e,t)),h(document,\\\"selectstart\\\",n),gt=!0,u&&R(document.body,\\\"user-select\\\",\\\"none\\\"))},_onDragOver:function(n){var o,i,r,t,e,a=this.el,l=n.target,s=this.options,c=s.group,u=Ft.active,d=lt===c,h=s.sort,f=st||u,p=this,g=!1;if(!Tt){if(void 0!==n.preventDefault&&n.cancelable&&n.preventDefault(),l=P(l,s.draggable,a,!0),O(\\\"dragOver\\\"),Ft.eventCanceled)return g;if(V.contains(n.target)||l.animated&&l.animatingX&&l.animatingY||p._ignoreWhileAnimating===l)return A(!1);if(Et=!1,u&&!s.disabled&&(d?h||(i=Z!==Q):st===this||(this.lastPutMode=lt.checkPull(this,u,V,n))&&c.checkPut(this,u,V,n))){if(r=\\\"vertical\\\"===this._getDirection(n,l),o=X(V),O(\\\"dragOverValid\\\"),Ft.eventCanceled)return g;if(i)return Z=Q,M(),this._hideClone(),O(\\\"revert\\\"),Ft.eventCanceled||(J?Q.insertBefore(V,J):Q.appendChild(V)),A(!0);var m=F(a,s.draggable);if(m&&(S=n,c=r,x=X(F((D=this).el,D.options.draggable)),D=L(D.el,D.options,$),!(c?S.clientX>D.right+10||S.clientY>x.bottom&&S.clientX>x.left:S.clientY>D.bottom+10||S.clientX>x.right&&S.clientY>x.top)||m.animated)){if(m&&(t=n,e=r,C=X(B((_=this).el,0,_.options,!0)),_=L(_.el,_.options,$),e?t.clientX<_.left-10||t.clientY<C.top&&t.clientX<C.right:t.clientY<_.top-10||t.clientY<C.bottom&&t.clientX<C.left)){var v=B(a,0,s,!0);if(v===V)return A(!1);if(E=X(l=v),!1!==jt(Q,a,V,o,l,E,n,!1))return M(),a.insertBefore(V,v),Z=a,N(),A(!0)}else if(l.parentNode===a){var b,y,w,E=X(l),D=V.parentNode!==a,S=(S=V.animated&&V.toRect||o,x=l.animated&&l.toRect||E,_=(e=r)?S.left:S.top,t=e?S.right:S.bottom,C=e?S.width:S.height,v=e?x.left:x.top,S=e?x.right:x.bottom,x=e?x.width:x.height,!(_===v||t===S||_+C/2===v+x/2)),_=r?\\\"top\\\":\\\"left\\\",C=Y(l,\\\"top\\\",\\\"top\\\")||Y(V,\\\"top\\\",\\\"top\\\"),v=C?C.scrollTop:void 0;if(mt!==l&&(y=E[_],St=!1,_t=!S&&s.invertSwap||D),0!==(b=function(t,e,n,o,i,r,a,l){var s=o?t.clientY:t.clientX,c=o?n.height:n.width,t=o?n.top:n.left,o=o?n.bottom:n.right,n=!1;if(!a)if(l&&bt<c*i){if(St=!St&&(1===vt?t+c*r/2<s:s<o-c*r/2)?!0:St)n=!0;else if(1===vt?s<t+bt:o-bt<s)return-vt}else if(t+c*(1-i)/2<s&&s<o-c*(1-i)/2)return function(t){return j(V)<j(t)?1:-1}(e);if((n=n||a)&&(s<t+c*r/2||o-c*r/2<s))return t+c/2<s?1:-1;return 0}(n,l,E,r,S?1:s.swapThreshold,null==s.invertedSwapThreshold?s.swapThreshold:s.invertedSwapThreshold,_t,mt===l)))for(var T=j(V);(w=Z.children[T-=b])&&(\\\"none\\\"===R(w,\\\"display\\\")||w===$););if(0===b||w===l)return A(!1);vt=b;var x=(mt=l).nextElementSibling,D=!1,S=jt(Q,a,V,o,l,E,n,D=1===b);if(!1!==S)return 1!==S&&-1!==S||(D=1===S),Tt=!0,setTimeout(Lt,30),M(),D&&!x?a.appendChild(V):l.parentNode.insertBefore(V,D?x:l),C&&H(C,0,v-C.scrollTop),Z=V.parentNode,void 0===y||_t||(bt=Math.abs(y-X(l)[_])),N(),A(!0)}}else{if(m===V)return A(!1);if((l=m&&a===n.target?m:l)&&(E=X(l)),!1!==jt(Q,a,V,o,l,E,n,!!l))return M(),m&&m.nextSibling?a.insertBefore(V,m.nextSibling):a.appendChild(V),Z=a,N(),A(!0)}if(a.contains(V))return A(!1)}return!1}function O(t,e){G(t,p,I({evt:n,isOwner:d,axis:r?\\\"vertical\\\":\\\"horizontal\\\",revert:i,dragRect:o,targetRect:E,canSort:h,fromSortable:f,target:l,completed:A,onMove:function(t,e){return jt(Q,a,V,o,t,X(t),n,e)},changed:N},e))}function M(){O(\\\"dragOverAnimationCapture\\\"),p.captureAnimationState(),p!==f&&f.captureAnimationState()}function A(t){return O(\\\"dragOverCompleted\\\",{insertion:t}),t&&(d?u._hideClone():u._showClone(p),p!==f&&(k(V,(st||u).options.ghostClass,!1),k(V,s.ghostClass,!0)),st!==p&&p!==Ft.active?st=p:p===Ft.active&&st&&(st=null),f===p&&(p._ignoreWhileAnimating=l),p.animateAll(function(){O(\\\"dragOverAnimationComplete\\\"),p._ignoreWhileAnimating=null}),p!==f&&(f.animateAll(),f._ignoreWhileAnimating=null)),(l===V&&!V.animated||l===a&&!l.animated)&&(mt=null),s.dragoverBubble||n.rootEl||l===document||(V.parentNode[K]._isOutsideThisEl(n.target),t||Yt(n)),!s.dragoverBubble&&n.stopPropagation&&n.stopPropagation(),g=!0}function N(){it=j(V),at=j(V,s.draggable),q({sortable:p,name:\\\"change\\\",toEl:a,newIndex:it,newDraggableIndex:at,originalEvent:n})}},_ignoreWhileAnimating:null,_offMoveEvents:function(){f(document,\\\"mousemove\\\",this._onTouchMove),f(document,\\\"touchmove\\\",this._onTouchMove),f(document,\\\"pointermove\\\",this._onTouchMove),f(document,\\\"dragover\\\",Yt),f(document,\\\"mousemove\\\",Yt),f(document,\\\"touchmove\\\",Yt)},_offUpEvents:function(){var t=this.el.ownerDocument;f(t,\\\"mouseup\\\",this._onDrop),f(t,\\\"touchend\\\",this._onDrop),f(t,\\\"pointerup\\\",this._onDrop),f(t,\\\"touchcancel\\\",this._onDrop),f(document,\\\"selectstart\\\",this)},_onDrop:function(t){var e=this.el,n=this.options;it=j(V),at=j(V,n.draggable),G(\\\"drop\\\",this,{evt:t}),Z=V&&V.parentNode,it=j(V),at=j(V,n.draggable),Ft.eventCanceled||(St=_t=wt=!1,clearInterval(this._loopId),clearTimeout(this._dragStartTimer),Wt(this.cloneId),Wt(this._dragStartId),this.nativeDraggable&&(f(document,\\\"drop\\\",this),f(e,\\\"dragstart\\\",this._onDragStart)),this._offMoveEvents(),this._offUpEvents(),u&&R(document.body,\\\"user-select\\\",\\\"\\\"),R(V,\\\"transform\\\",\\\"\\\"),t&&(gt&&(t.cancelable&&t.preventDefault(),n.dropBubble||t.stopPropagation()),$&&$.parentNode&&$.parentNode.removeChild($),(Q===Z||st&&\\\"clone\\\"!==st.lastPutMode)&&et&&et.parentNode&&et.parentNode.removeChild(et),V&&(this.nativeDraggable&&f(V,\\\"dragend\\\",this),Ht(V),V.style[\\\"will-change\\\"]=\\\"\\\",gt&&!wt&&k(V,(st||this).options.ghostClass,!1),k(V,this.options.chosenClass,!1),q({sortable:this,name:\\\"unchoose\\\",toEl:Z,newIndex:null,newDraggableIndex:null,originalEvent:t}),Q!==Z?(0<=it&&(q({rootEl:Z,name:\\\"add\\\",toEl:Z,fromEl:Q,originalEvent:t}),q({sortable:this,name:\\\"remove\\\",toEl:Z,originalEvent:t}),q({rootEl:Z,name:\\\"sort\\\",toEl:Z,fromEl:Q,originalEvent:t}),q({sortable:this,name:\\\"sort\\\",toEl:Z,originalEvent:t})),st&&st.save()):it!==ot&&0<=it&&(q({sortable:this,name:\\\"update\\\",toEl:Z,originalEvent:t}),q({sortable:this,name:\\\"sort\\\",toEl:Z,originalEvent:t})),Ft.active&&(null!=it&&-1!==it||(it=ot,at=rt),q({sortable:this,name:\\\"end\\\",toEl:Z,originalEvent:t}),this.save())))),this._nulling()},_nulling:function(){G(\\\"nulling\\\",this),Q=V=Z=$=J=et=tt=nt=ct=ut=gt=it=at=ot=rt=mt=vt=st=lt=Ft.dragged=Ft.ghost=Ft.clone=Ft.active=null,xt.forEach(function(t){t.checked=!0}),xt.length=dt=ht=0},handleEvent:function(t){switch(t.type){case\\\"drop\\\":case\\\"dragend\\\":this._onDrop(t);break;case\\\"dragenter\\\":case\\\"dragover\\\":V&&(this._onDragOver(t),function(t){t.dataTransfer&&(t.dataTransfer.dropEffect=\\\"move\\\");t.cancelable&&t.preventDefault()}(t));break;case\\\"selectstart\\\":t.preventDefault()}},toArray:function(){for(var t,e=[],n=this.el.children,o=0,i=n.length,r=this.options;o<i;o++)P(t=n[o],r.draggable,this.el,!1)&&e.push(t.getAttribute(r.dataIdAttr)||function(t){var e=t.tagName+t.className+t.src+t.href+t.textContent,n=e.length,o=0;for(;n--;)o+=e.charCodeAt(n);return o.toString(36)}(t));return e},sort:function(t,e){var n={},o=this.el;this.toArray().forEach(function(t,e){e=o.children[e];P(e,this.options.draggable,o,!1)&&(n[t]=e)},this),e&&this.captureAnimationState(),t.forEach(function(t){n[t]&&(o.removeChild(n[t]),o.appendChild(n[t]))}),e&&this.animateAll()},save:function(){var t=this.options.store;t&&t.set&&t.set(this)},closest:function(t,e){return P(t,e||this.options.draggable,this.el,!1)},option:function(t,e){var n=this.options;if(void 0===e)return n[t];var o=W.modifyOption(this,t,e);n[t]=void 0!==o?o:e,\\\"group\\\"===t&&kt(n)},destroy:function(){G(\\\"destroy\\\",this);var t=this.el;t[K]=null,f(t,\\\"mousedown\\\",this._onTapStart),f(t,\\\"touchstart\\\",this._onTapStart),f(t,\\\"pointerdown\\\",this._onTapStart),this.nativeDraggable&&(f(t,\\\"dragover\\\",this),f(t,\\\"dragenter\\\",this)),Array.prototype.forEach.call(t.querySelectorAll(\\\"[draggable]\\\"),function(t){t.removeAttribute(\\\"draggable\\\")}),this._onDrop(),this._disableDelayedDragEvents(),Dt.splice(Dt.indexOf(this.el),1),this.el=t=null},_hideClone:function(){nt||(G(\\\"hideClone\\\",this),Ft.eventCanceled||(R(et,\\\"display\\\",\\\"none\\\"),this.options.removeCloneOnHide&&et.parentNode&&et.parentNode.removeChild(et),nt=!0))},_showClone:function(t){\\\"clone\\\"===t.lastPutMode?nt&&(G(\\\"showClone\\\",this),Ft.eventCanceled||(V.parentNode!=Q||this.options.group.revertClone?J?Q.insertBefore(et,J):Q.appendChild(et):Q.insertBefore(et,V),this.options.group.revertClone&&this.animate(V,et),R(et,\\\"display\\\",\\\"\\\"),nt=!1)):this._hideClone()}},Ot&&h(document,\\\"touchmove\\\",function(t){(Ft.active||wt)&&t.cancelable&&t.preventDefault()}),Ft.utils={on:h,off:f,css:R,find:b,is:function(t,e){return!!P(t,e,t,!1)},extend:function(t,e){if(t&&e)for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t},throttle:S,closest:P,toggleClass:k,clone:_,index:j,nextTick:Kt,cancelNextTick:Wt,detectDirection:Pt,getChild:B},Ft.get=function(t){return t[K]},Ft.mount=function(){for(var t=arguments.length,e=new Array(t),n=0;n<t;n++)e[n]=arguments[n];(e=e[0].constructor===Array?e[0]:e).forEach(function(t){if(!t.prototype||!t.prototype.constructor)throw\\\"Sortable: Mounted plugin must be a constructor function, not \\\".concat({}.toString.call(t));t.utils&&(Ft.utils=I(I({},Ft.utils),t.utils)),W.mount(t)})},Ft.create=function(t,e){return new Ft(t,e)};var zt,Gt,Ut,qt,Vt,Zt,$t=[],Qt=!(Ft.version=\\\"1.15.2\\\");function Jt(){$t.forEach(function(t){clearInterval(t.pid)}),$t=[]}function te(){clearInterval(Zt)}var ee,ne=S(function(n,t,e,o){if(t.scroll){var i,r=(n.touches?n.touches[0]:n).clientX,a=(n.touches?n.touches[0]:n).clientY,l=t.scrollSensitivity,s=t.scrollSpeed,c=O(),u=!1;Gt!==e&&(Gt=e,Jt(),zt=t.scroll,i=t.scrollFn,!0===zt&&(zt=M(e,!0)));var d=0,h=zt;do{var f=h,p=X(f),g=p.top,m=p.bottom,v=p.left,b=p.right,y=p.width,w=p.height,E=void 0,D=void 0,S=f.scrollWidth,_=f.scrollHeight,C=R(f),T=f.scrollLeft,p=f.scrollTop,D=f===c?(E=y<S&&(\\\"auto\\\"===C.overflowX||\\\"scroll\\\"===C.overflowX||\\\"visible\\\"===C.overflowX),w<_&&(\\\"auto\\\"===C.overflowY||\\\"scroll\\\"===C.overflowY||\\\"visible\\\"===C.overflowY)):(E=y<S&&(\\\"auto\\\"===C.overflowX||\\\"scroll\\\"===C.overflowX),w<_&&(\\\"auto\\\"===C.overflowY||\\\"scroll\\\"===C.overflowY)),T=E&&(Math.abs(b-r)<=l&&T+y<S)-(Math.abs(v-r)<=l&&!!T),p=D&&(Math.abs(m-a)<=l&&p+w<_)-(Math.abs(g-a)<=l&&!!p);if(!$t[d])for(var x=0;x<=d;x++)$t[x]||($t[x]={});$t[d].vx==T&&$t[d].vy==p&&$t[d].el===f||($t[d].el=f,$t[d].vx=T,$t[d].vy=p,clearInterval($t[d].pid),0==T&&0==p||(u=!0,$t[d].pid=setInterval(function(){o&&0===this.layer&&Ft.active._onTouchMove(Vt);var t=$t[this.layer].vy?$t[this.layer].vy*s:0,e=$t[this.layer].vx?$t[this.layer].vx*s:0;\\\"function\\\"==typeof i&&\\\"continue\\\"!==i.call(Ft.dragged.parentNode[K],e,t,n,Vt,$t[this.layer].el)||H($t[this.layer].el,e,t)}.bind({layer:d}),24))),d++}while(t.bubbleScroll&&h!==c&&(h=M(h,!1)));Qt=u}},30),c=function(t){var e=t.originalEvent,n=t.putSortable,o=t.dragEl,i=t.activeSortable,r=t.dispatchSortableEvent,a=t.hideGhostForTarget,t=t.unhideGhostForTarget;e&&(i=n||i,a(),e=e.changedTouches&&e.changedTouches.length?e.changedTouches[0]:e,e=document.elementFromPoint(e.clientX,e.clientY),t(),i&&!i.el.contains(e)&&(r(\\\"spill\\\"),this.onSpill({dragEl:o,putSortable:n})))};function oe(){}function ie(){}oe.prototype={startIndex:null,dragStart:function(t){t=t.oldDraggableIndex;this.startIndex=t},onSpill:function(t){var e=t.dragEl,n=t.putSortable;this.sortable.captureAnimationState(),n&&n.captureAnimationState();t=B(this.sortable.el,this.startIndex,this.options);t?this.sortable.el.insertBefore(e,t):this.sortable.el.appendChild(e),this.sortable.animateAll(),n&&n.animateAll()},drop:c},a(oe,{pluginName:\\\"revertOnSpill\\\"}),ie.prototype={onSpill:function(t){var e=t.dragEl,t=t.putSortable||this.sortable;t.captureAnimationState(),e.parentNode&&e.parentNode.removeChild(e),t.animateAll()},drop:c},a(ie,{pluginName:\\\"removeOnSpill\\\"});var re,ae,le,se,ce,ue=[],de=[],he=!1,fe=!1,pe=!1;function ge(n,o){de.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)];e?o.insertBefore(t,e):o.appendChild(t)})}function me(){ue.forEach(function(t){t!==le&&t.parentNode&&t.parentNode.removeChild(t)})}return Ft.mount(new function(){function t(){for(var t in this.defaults={scroll:!0,forceAutoScrollFallback:!1,scrollSensitivity:30,scrollSpeed:10,bubbleScroll:!0},this)\\\"_\\\"===t.charAt(0)&&\\\"function\\\"==typeof this[t]&&(this[t]=this[t].bind(this))}return t.prototype={dragStarted:function(t){t=t.originalEvent;this.sortable.nativeDraggable?h(document,\\\"dragover\\\",this._handleAutoScroll):this.options.supportPointer?h(document,\\\"pointermove\\\",this._handleFallbackAutoScroll):t.touches?h(document,\\\"touchmove\\\",this._handleFallbackAutoScroll):h(document,\\\"mousemove\\\",this._handleFallbackAutoScroll)},dragOverCompleted:function(t){t=t.originalEvent;this.options.dragOverBubble||t.rootEl||this._handleAutoScroll(t)},drop:function(){this.sortable.nativeDraggable?f(document,\\\"dragover\\\",this._handleAutoScroll):(f(document,\\\"pointermove\\\",this._handleFallbackAutoScroll),f(document,\\\"touchmove\\\",this._handleFallbackAutoScroll),f(document,\\\"mousemove\\\",this._handleFallbackAutoScroll)),te(),Jt(),clearTimeout(g),g=void 0},nulling:function(){Vt=Gt=zt=Qt=Zt=Ut=qt=null,$t.length=0},_handleFallbackAutoScroll:function(t){this._handleAutoScroll(t,!0)},_handleAutoScroll:function(e,n){var o,i=this,r=(e.touches?e.touches[0]:e).clientX,a=(e.touches?e.touches[0]:e).clientY,t=document.elementFromPoint(r,a);Vt=e,n||this.options.forceAutoScrollFallback||w||y||u?(ne(e,this.options,t,n),o=M(t,!0),!Qt||Zt&&r===Ut&&a===qt||(Zt&&te(),Zt=setInterval(function(){var t=M(document.elementFromPoint(r,a),!0);t!==o&&(o=t,Jt()),ne(e,i.options,t,n)},10),Ut=r,qt=a)):this.options.bubbleScroll&&M(t,!0)!==O()?ne(e,this.options,M(t,!1),!1):Jt()}},a(t,{pluginName:\\\"scroll\\\",initializeByDefault:!0})}),Ft.mount(ie,oe),Ft.mount(new function(){function t(){this.defaults={swapClass:\\\"sortable-swap-highlight\\\"}}return t.prototype={dragStart:function(t){t=t.dragEl;ee=t},dragOverValid:function(t){var e=t.completed,n=t.target,o=t.onMove,i=t.activeSortable,r=t.changed,a=t.cancel;i.options.swap&&(t=this.sortable.el,i=this.options,n&&n!==t&&(t=ee,ee=!1!==o(n)?(k(n,i.swapClass,!0),n):null,t&&t!==ee&&k(t,i.swapClass,!1)),r(),e(!0),a())},drop:function(t){var e,n,o=t.activeSortable,i=t.putSortable,r=t.dragEl,a=i||this.sortable,l=this.options;ee&&k(ee,l.swapClass,!1),ee&&(l.swap||i&&i.options.swap)&&r!==ee&&(a.captureAnimationState(),a!==o&&o.captureAnimationState(),n=ee,t=(e=r).parentNode,l=n.parentNode,t&&l&&!t.isEqualNode(n)&&!l.isEqualNode(e)&&(i=j(e),r=j(n),t.isEqualNode(l)&&i<r&&r++,t.insertBefore(n,t.children[i]),l.insertBefore(e,l.children[r])),a.animateAll(),a!==o&&o.animateAll())},nulling:function(){ee=null}},a(t,{pluginName:\\\"swap\\\",eventProperties:function(){return{swapItem:ee}}})}),Ft.mount(new function(){function t(o){for(var t in this)\\\"_\\\"===t.charAt(0)&&\\\"function\\\"==typeof this[t]&&(this[t]=this[t].bind(this));o.options.avoidImplicitDeselect||(o.options.supportPointer?h(document,\\\"pointerup\\\",this._deselectMultiDrag):(h(document,\\\"mouseup\\\",this._deselectMultiDrag),h(document,\\\"touchend\\\",this._deselectMultiDrag))),h(document,\\\"keydown\\\",this._checkKeyDown),h(document,\\\"keyup\\\",this._checkKeyUp),this.defaults={selectedClass:\\\"sortable-selected\\\",multiDragKey:null,avoidImplicitDeselect:!1,setData:function(t,e){var n=\\\"\\\";ue.length&&ae===o?ue.forEach(function(t,e){n+=(e?\\\", \\\":\\\"\\\")+t.textContent}):n=e.textContent,t.setData(\\\"Text\\\",n)}}}return t.prototype={multiDragKeyDown:!1,isMultiDrag:!1,delayStartGlobal:function(t){t=t.dragEl;le=t},delayEnded:function(){this.isMultiDrag=~ue.indexOf(le)},setupClone:function(t){var e=t.sortable,t=t.cancel;if(this.isMultiDrag){for(var n=0;n<ue.length;n++)de.push(_(ue[n])),de[n].sortableIndex=ue[n].sortableIndex,de[n].draggable=!1,de[n].style[\\\"will-change\\\"]=\\\"\\\",k(de[n],this.options.selectedClass,!1),ue[n]===le&&k(de[n],this.options.chosenClass,!1);e._hideClone(),t()}},clone:function(t){var e=t.sortable,n=t.rootEl,o=t.dispatchSortableEvent,t=t.cancel;this.isMultiDrag&&(this.options.removeCloneOnHide||ue.length&&ae===e&&(ge(!0,n),o(\\\"clone\\\"),t()))},showClone:function(t){var e=t.cloneNowShown,n=t.rootEl,t=t.cancel;this.isMultiDrag&&(ge(!1,n),de.forEach(function(t){R(t,\\\"display\\\",\\\"\\\")}),e(),ce=!1,t())},hideClone:function(t){var e=this,n=(t.sortable,t.cloneNowHidden),t=t.cancel;this.isMultiDrag&&(de.forEach(function(t){R(t,\\\"display\\\",\\\"none\\\"),e.options.removeCloneOnHide&&t.parentNode&&t.parentNode.removeChild(t)}),n(),ce=!0,t())},dragStartGlobal:function(t){t.sortable;!this.isMultiDrag&&ae&&ae.multiDrag._deselectMultiDrag(),ue.forEach(function(t){t.sortableIndex=j(t)}),ue=ue.sort(function(t,e){return t.sortableIndex-e.sortableIndex}),pe=!0},dragStarted:function(t){var e,n=this,t=t.sortable;this.isMultiDrag&&(this.options.sort&&(t.captureAnimationState(),this.options.animation&&(ue.forEach(function(t){t!==le&&R(t,\\\"position\\\",\\\"absolute\\\")}),e=X(le,!1,!0,!0),ue.forEach(function(t){t!==le&&C(t,e)}),he=fe=!0)),t.animateAll(function(){he=fe=!1,n.options.animation&&ue.forEach(function(t){T(t)}),n.options.sort&&me()}))},dragOver:function(t){var e=t.target,n=t.completed,t=t.cancel;fe&&~ue.indexOf(e)&&(n(!1),t())},revert:function(t){var n,o,e=t.fromSortable,i=t.rootEl,r=t.sortable,a=t.dragRect;1<ue.length&&(ue.forEach(function(t){r.addAnimationState({target:t,rect:fe?X(t):a}),T(t),t.fromRect=a,e.removeAnimationState(t)}),fe=!1,n=!this.options.removeCloneOnHide,o=i,ue.forEach(function(t,e){e=o.children[t.sortableIndex+(n?Number(e):0)];e?o.insertBefore(t,e):o.appendChild(t)}))},dragOverCompleted:function(t){var e,n=t.sortable,o=t.isOwner,i=t.insertion,r=t.activeSortable,a=t.parentEl,l=t.putSortable,t=this.options;i&&(o&&r._hideClone(),he=!1,t.animation&&1<ue.length&&(fe||!o&&!r.options.sort&&!l)&&(e=X(le,!1,!0,!0),ue.forEach(function(t){t!==le&&(C(t,e),a.appendChild(t))}),fe=!0),o||(fe||me(),1<ue.length?(o=ce,r._showClone(n),r.options.animation&&!ce&&o&&de.forEach(function(t){r.addAnimationState({target:t,rect:se}),t.fromRect=se,t.thisAnimationDuration=null})):r._showClone(n)))},dragOverAnimationCapture:function(t){var e=t.dragRect,n=t.isOwner,t=t.activeSortable;ue.forEach(function(t){t.thisAnimationDuration=null}),t.options.animation&&!n&&t.multiDrag.isMultiDrag&&(se=a({},e),e=v(le,!0),se.top-=e.f,se.left-=e.e)},dragOverAnimationComplete:function(){fe&&(fe=!1,me())},drop:function(t){var e=t.originalEvent,n=t.rootEl,o=t.parentEl,i=t.sortable,r=t.dispatchSortableEvent,a=t.oldIndex,l=t.putSortable,s=l||this.sortable;if(e){var c,u,d,h=this.options,f=o.children;if(!pe)if(h.multiDragKey&&!this.multiDragKeyDown&&this._deselectMultiDrag(),k(le,h.selectedClass,!~ue.indexOf(le)),~ue.indexOf(le))ue.splice(ue.indexOf(le),1),re=null,z({sortable:i,rootEl:n,name:\\\"deselect\\\",targetEl:le,originalEvent:e});else{if(ue.push(le),z({sortable:i,rootEl:n,name:\\\"select\\\",targetEl:le,originalEvent:e}),e.shiftKey&&re&&i.el.contains(re)){var p=j(re),t=j(le);if(~p&&~t&&p!==t)for(var g,m=p<t?(g=p,t):(g=t,p+1);g<m;g++)~ue.indexOf(f[g])||(k(f[g],h.selectedClass,!0),ue.push(f[g]),z({sortable:i,rootEl:n,name:\\\"select\\\",targetEl:f[g],originalEvent:e}))}else re=le;ae=s}pe&&this.isMultiDrag&&(fe=!1,(o[K].options.sort||o!==n)&&1<ue.length&&(c=X(le),u=j(le,\\\":not(.\\\"+this.options.selectedClass+\\\")\\\"),!he&&h.animation&&(le.thisAnimationDuration=null),s.captureAnimationState(),he||(h.animation&&(le.fromRect=c,ue.forEach(function(t){var e;t.thisAnimationDuration=null,t!==le&&(e=fe?X(t):c,t.fromRect=e,s.addAnimationState({target:t,rect:e}))})),me(),ue.forEach(function(t){f[u]?o.insertBefore(t,f[u]):o.appendChild(t),u++}),a===j(le)&&(d=!1,ue.forEach(function(t){t.sortableIndex!==j(t)&&(d=!0)}),d&&(r(\\\"update\\\"),r(\\\"sort\\\")))),ue.forEach(function(t){T(t)}),s.animateAll()),ae=s),(n===o||l&&\\\"clone\\\"!==l.lastPutMode)&&de.forEach(function(t){t.parentNode&&t.parentNode.removeChild(t)})}},nullingGlobal:function(){this.isMultiDrag=pe=!1,de.length=0},destroyGlobal:function(){this._deselectMultiDrag(),f(document,\\\"pointerup\\\",this._deselectMultiDrag),f(document,\\\"mouseup\\\",this._deselectMultiDrag),f(document,\\\"touchend\\\",this._deselectMultiDrag),f(document,\\\"keydown\\\",this._checkKeyDown),f(document,\\\"keyup\\\",this._checkKeyUp)},_deselectMultiDrag:function(t){if(!(void 0!==pe&&pe||ae!==this.sortable||t&&P(t.target,this.options.draggable,this.sortable.el,!1)||t&&0!==t.button))for(;ue.length;){var e=ue[0];k(e,this.options.selectedClass,!1),ue.shift(),z({sortable:this.sortable,rootEl:this.sortable.el,name:\\\"deselect\\\",targetEl:e,originalEvent:t})}},_checkKeyDown:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!0)},_checkKeyUp:function(t){t.key===this.options.multiDragKey&&(this.multiDragKeyDown=!1)}},a(t,{pluginName:\\\"multiDrag\\\",utils:{select:function(t){var e=t.parentNode[K];e&&e.options.multiDrag&&!~ue.indexOf(t)&&(ae&&ae!==e&&(ae.multiDrag._deselectMultiDrag(),ae=e),k(t,e.options.selectedClass,!0),ue.push(t))},deselect:function(t){var e=t.parentNode[K],n=ue.indexOf(t);e&&e.options.multiDrag&&~n&&(k(t,e.options.selectedClass,!1),ue.splice(n,1))}},eventProperties:function(){var n=this,o=[],i=[];return ue.forEach(function(t){var e;o.push({multiDragElement:t,index:t.sortableIndex}),e=fe&&t!==le?-1:fe?j(t,\\\":not(.\\\"+n.options.selectedClass+\\\")\\\"):j(t),i.push({multiDragElement:t,index:e})}),{items:r(ue),clones:[].concat(de),oldIndicies:o,newIndicies:i}},optionListeners:{multiDragKey:function(t){return\\\"ctrl\\\"===(t=t.toLowerCase())?t=\\\"Control\\\":1<t.length&&(t=t.charAt(0).toUpperCase()+t.substr(1)),t}}})}),Ft});\"\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,EAkEXC,EAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA4IVH,EAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA6OZC,EAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":["createServer","appendFileSync","existsSync","mkdirSync","resolve","SORTABLE_JS","startBoardDashboard","config","dashboard","boards","port","bind","token","sources","board","buildWorkSource","e","createServer","req","res","handleRequest","sendJson","b","ctx","url","path","method","renderBoardHtml","renderLiveHtml","SORTABLE_JS","snap","buildLiveSnapshot","startLiveStream","headers","r","list","body","readJson","drafted","draftIssue","authHeader","membersMatch","source","members","milestonesMatch","ms","moveMatch","itemOpMatch","boardId","itemId","toCol","c","fromCol","applyTransition","audit","auditActor","columns","totals","loadBoardColumns","detail","updated","created","query","empty","baseLabels","extraLabel","search","milestone","assignee","openDays","closedDays","hasOpen","hasClosed","open","closed","closedCol","item","openColumns","stage","deriveStage","k","i","a","p","v","diff","transitionDiff","gl","daemon","agentUsernames","mapping","GitLabWorkSource","args","entry","dir","resolve","existsSync","mkdirSync","line","appendFileSync","fwd","fetchDaemonAgents","signal","base","healthRes","agentsRes","meshRes","agents","h","meshResSafe","_meshJsonCache","j","fetchMeshPeers","primaryUrl","dash","primaryToken","seen","d","key","ac","timeout","meshPeers","nodes","send","ev","data","tick","timer","DRAFT_CONVENTIONS","rough","agentId","boardCtx","userPrompt","content","parsed","extractJson","labels","x","text","fenced","start","depth","status","reject","chunks","raw","BOARD_CSS","BOARD_JS","LIVE_CSS","LIVE_JS"]}
|
package/dist/chunk-4YCH6IZV.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var d=Object.defineProperty;var e=(a,b,c)=>b in a?d(a,b,{enumerable:!0,configurable:!0,writable:!0,value:c}):a[b]=c;var f=(a=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(a,{get:(b,c)=>(typeof require<"u"?require:b)[c]}):a)(function(a){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+a+'" is not supported')});var g=(a,b,c)=>(e(a,typeof b!="symbol"?b+"":b,c),c);export{f as a,g as b};
|
|
2
|
-
//# sourceMappingURL=chunk-4YCH6IZV.js.map
|
package/dist/chunk-DYOBFJLX.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import{readFileSync as A,writeFileSync as E,appendFileSync as K,existsSync as O,mkdirSync as j}from"fs";import{resolve as _}from"path";var w={"claude-opus":{input:15,output:75,cacheRead:1.5,cacheCreate:18.75},"claude-sonnet":{input:3,output:15,cacheRead:.3,cacheCreate:3.75},"claude-haiku":{input:.25,output:1.25,cacheRead:.025,cacheCreate:.3125}},H=2e5,F=1.5;function W(f){let e=f.toLowerCase();return e.includes("opus")?"claude-opus":e.includes("haiku")?"claude-haiku":"claude-sonnet"}var x=500,G=".agentx/pricing/custom.json",I=class{dir;cache=null;pricing;constructor(e=_(process.cwd(),".agentx/usage")){this.dir=e,j(this.dir,{recursive:!0}),this.pricing=J()}getPricing(){return this.pricing}static calculateCost(e,r,s=w){let h=W(r||e.model||"claude-sonnet"),o=s[h]||s["claude-sonnet"],k=e.tier2InputTokens||0,d=e.tier2OutputTokens||0,T=e.tier2CacheReadTokens||0,n=e.tier2CacheCreateTokens||0,i=(e.inputTokens||0)/1e6*o.input+(e.outputTokens||0)/1e6*o.output+(e.cacheReadTokens||0)/1e6*o.cacheRead+(e.cacheCreateTokens||0)/1e6*o.cacheCreate,t=k/1e6*o.input+d/1e6*o.output+T/1e6*o.cacheRead+n/1e6*o.cacheCreate;return i+t*F}cost(e,r){return I.calculateCost(e,r,this.pricing)}record(e,r,s,h,o,k,d,T,n){let i=this.today(),t=i.agents[e]||{tasks:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreateTokens:0,tier2InputTokens:0,tier2OutputTokens:0,tier2CacheReadTokens:0,tier2CacheCreateTokens:0,totalDuration:0,errors:0,byChannel:{},sessionKeys:[]};t.tasks++,t.totalDuration+=r,d&&(t.model=d);let b=0,g=0,C=0,p=0,c=0,m=0,a=0,R=0;s?s.inputTokens+s.cacheReadTokens+s.cacheCreateTokens>H?(c=s.inputTokens,m=s.outputTokens,a=s.cacheReadTokens,R=s.cacheCreateTokens):(b=s.inputTokens,g=s.outputTokens,C=s.cacheReadTokens,p=s.cacheCreateTokens):h!==void 0&&o!==void 0&&(b=Math.ceil(h/4),g=Math.ceil(o/4)),t.inputTokens+=b,t.outputTokens+=g,t.cacheReadTokens+=C,t.cacheCreateTokens+=p,t.tier2InputTokens=(t.tier2InputTokens||0)+c,t.tier2OutputTokens=(t.tier2OutputTokens||0)+m,t.tier2CacheReadTokens=(t.tier2CacheReadTokens||0)+a,t.tier2CacheCreateTokens=(t.tier2CacheCreateTokens||0)+R,k&&t.errors++,t.byChannel||(t.byChannel={});let y=(T||"other").toLowerCase(),l=t.byChannel[y]||{tasks:0,inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreateTokens:0,tier2InputTokens:0,tier2OutputTokens:0,tier2CacheReadTokens:0,tier2CacheCreateTokens:0,totalDuration:0,errors:0};l.tasks++,l.totalDuration+=r,l.inputTokens+=b,l.outputTokens+=g,l.cacheReadTokens+=C,l.cacheCreateTokens+=p,l.tier2InputTokens+=c,l.tier2OutputTokens+=m,l.tier2CacheReadTokens+=a,l.tier2CacheCreateTokens+=R,k&&l.errors++,t.byChannel[y]=l,n&&(t.sessionKeys||(t.sessionKeys=[]),t.sessionKeys.includes(n)||(t.sessionKeys.push(n),t.sessionKeys.length>x&&t.sessionKeys.splice(0,t.sessionKeys.length-x))),i.agents[e]=t,this.save(i)}today(){let e=new Date().toISOString().slice(0,10);if(this.cache?.date===e)return this.cache;let r=this.filePath(e);if(O(r))try{return this.cache=JSON.parse(A(r,"utf-8")),this.cache}catch{}return this.cache={date:e,agents:{}},this.cache}getDate(e){let r=this.filePath(e);if(!O(r))return null;try{return JSON.parse(A(r,"utf-8"))}catch{return null}}generateDailyReport(e){let r=e||new Date().toISOString().slice(0,10),s=this.getDate(r);if(!s)return null;let h=0,o=0,k=0,d=0,T=0,n=0,i="",t=0,b={},g={},C={};for(let[p,c]of Object.entries(s.agents)){let m=this.cost(c);b[p]=m,h+=c.tasks,o+=(c.inputTokens||0)+(c.tier2InputTokens||0),k+=(c.outputTokens||0)+(c.tier2OutputTokens||0),d+=(c.cacheReadTokens||0)+(c.tier2CacheReadTokens||0),T+=(c.cacheCreateTokens||0)+(c.tier2CacheCreateTokens||0),n+=m,m>t&&(t=m,i=p);let a=c.sessionKeys?.length||0;g[p]={sessions:a,avgTasksPerSession:a>0?c.tasks/a:c.tasks};for(let[R,y]of Object.entries(c.byChannel||{})){let l=this.cost(y,c.model);C[R]=(C[R]||0)+l}}return{date:r,totalTasks:h,totalInput:o,totalOutput:k,totalCacheRead:d,totalCacheCreate:T,totalCost:n,topAgent:i,topCost:t,agentCosts:b,agentSessionStats:g,byChannel:C}}appendToTokenCosts(e,r){let s=r||_(process.cwd(),".agentx/TOKEN_COSTS.md"),h=n=>n<.01?n.toFixed(4):n.toFixed(2),o=n=>n>=1e6?`${(n/1e6).toFixed(1)}M`:n>=1e3?`${(n/1e3).toFixed(0)}K`:String(n),k=Object.entries(e.agentCosts).sort(([,n],[,i])=>i-n).map(([n,i])=>{let t=e.agentSessionStats[n],b=t&&t.sessions>0?` (${t.sessions}s, ${t.avgTasksPerSession.toFixed(1)}t/s)`:"";return`${n}: $${h(i)}${b}`}).join(", "),d=Object.entries(e.byChannel).sort(([,n],[,i])=>i-n).map(([n,i])=>`${n}: $${h(i)}`).join(", "),T=`| ${e.date} | ${e.totalTasks} | ${o(e.totalInput)} | ${o(e.totalOutput)} | ${o(e.totalCacheRead)} | ${o(e.totalCacheCreate)} | $${h(e.totalCost)} | ${k} | ${d} |`;O(s)?K(s,T+`
|
|
2
|
-
`):E(s,`# Token Costs
|
|
3
|
-
|
|
4
|
-
Daily token cost tracking. Auto-appended at midnight (Africa/Tunis).
|
|
5
|
-
Agent breakdown: \`agent: $cost (Ns, T.Tt/s)\` \u2014 sessions, tasks/session.
|
|
6
|
-
|
|
7
|
-
| Date | Tasks | Input | Output | Cache R | Cache W | Cost | Per Agent | Per Channel |
|
|
8
|
-
|------|-------|-------|--------|---------|---------|------|-----------|-------------|
|
|
9
|
-
`+T+`
|
|
10
|
-
`)}summary(e=7){let r=0,s=0,h=0,o=0,k=0,d=0,T=0,n={},i={};for(let C=0;C<e;C++){let p=new Date(Date.now()-C*864e5).toISOString().slice(0,10),c=this.getDate(p);if(c)for(let[m,a]of Object.entries(c.agents)){let R=this.cost(a);r+=a.tasks;let y=(a.inputTokens||0)+(a.tier2InputTokens||0),l=(a.outputTokens||0)+(a.tier2OutputTokens||0),S=(a.cacheReadTokens||0)+(a.tier2CacheReadTokens||0),v=(a.cacheCreateTokens||0)+(a.tier2CacheCreateTokens||0);s+=y,h+=l,o+=S,k+=v,d+=a.errors,T+=R;let u=n[m]||{tasks:0,input:0,output:0,cacheRead:0,cacheCreate:0,total:0,avgDuration:0,totalDuration:0,cost:0,sessions:0};u.tasks+=a.tasks,u.input+=y,u.output+=l,u.cacheRead+=S,u.cacheCreate+=v,u.total=u.input+u.output+u.cacheRead+u.cacheCreate,u.totalDuration+=a.totalDuration,u.avgDuration=u.totalDuration/u.tasks,u.cost+=R,u.sessions+=a.sessionKeys?.length||0,a.model&&(u.model=a.model),n[m]=u;for(let[P,$]of Object.entries(a.byChannel||{})){let N=this.cost($,a.model),D=i[P]||{tasks:0,cost:0};D.tasks+=$.tasks,D.cost+=N,i[P]=D}}}let t=s+h+o+k,b=o+k>0?o/(o+k):0,g={};for(let[C,p]of Object.entries(n))g[C]={sessions:p.sessions,avgTasksPerSession:p.sessions>0?p.tasks/p.sessions:p.tasks};return{totalTasks:r,totalTokens:t,totalInput:s,totalOutput:h,totalCacheRead:o,totalCacheCreate:k,cacheHitRatio:b,totalErrors:d,totalCost:T,byAgent:n,byChannel:i,sessionStats:g}}filePath(e){return _(this.dir,`${e}.json`)}save(e){try{E(this.filePath(e.date),JSON.stringify(e,null,2))}catch{}}};function J(){let f=JSON.parse(JSON.stringify(w));try{let e=_(process.cwd(),G);if(!O(e))return f;let r=JSON.parse(A(e,"utf-8"));for(let[s,h]of Object.entries(r))f[s]||(f[s]={input:0,output:0,cacheRead:0,cacheCreate:0}),Object.assign(f[s],h)}catch{}return f}export{I as a};
|
|
11
|
-
//# sourceMappingURL=chunk-DYOBFJLX.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/daemon/token-tracker.ts"],"sourcesContent":["import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// --- Token usage tracker ---\n// Tracks token usage per agent per day with cache-aware, tier-aware cost\n// calculation. Persisted to .agentx/usage/{YYYY-MM-DD}.json.\n//\n// Design notes:\n// - Pricing is per-model-FAMILY (opus/sonnet/haiku). Exact model name\n// variants map via getModelFamily(). Override the table by dropping\n// .agentx/pricing/custom.json (same shape) — merged over defaults at\n// construction, lets operators adjust without a rebuild.\n// - Anthropic bills 1.5× input/output when a single request's total input\n// (input + cacheCreate + cacheRead) exceeds 200K tokens. We split each\n// recorded task into tier1/tier2 buckets and bill them separately so\n// long-context sessions are not undercounted.\n// - Per-channel breakdowns let operators see \"GitLab drove 70% of today's\n// spend\" — the lever for channel-specific session + context tuning.\n// - Session tracking (unique chatId:channel pairs seen today per agent)\n// gives an avg-tasks-per-session proxy — when it spikes, it's usually\n// retry/clarification chains, a signal the context budget or model\n// capability is under-sized for that traffic.\n\n// Anthropic pricing per million tokens (as of April 2026, 5-min ephemeral).\n// Keys here must match getModelFamily() output.\nexport const CACHE_AWARE_PRICING: Record<string, { input: number; output: number; cacheRead: number; cacheCreate: number }> = {\n \"claude-opus\": { input: 15, output: 75, cacheRead: 1.5, cacheCreate: 18.75 },\n \"claude-sonnet\": { input: 3, output: 15, cacheRead: 0.3, cacheCreate: 3.75 },\n \"claude-haiku\": { input: 0.25, output: 1.25, cacheRead: 0.025, cacheCreate: 0.3125 },\n}\n\n/** Per-request input size (input + cacheCreate + cacheRead) above this\n * threshold triggers Anthropic's extended-context tier: all four rates\n * get multiplied. Applies to Opus 4.x and Sonnet 4.x 1M variants. */\nexport const CONTEXT_TIER_THRESHOLD = 200_000\nexport const TIER2_MULTIPLIER = 1.5\n\nexport function getModelFamily(model: string): string {\n const lower = model.toLowerCase()\n if (lower.includes(\"opus\")) return \"claude-opus\"\n if (lower.includes(\"haiku\")) return \"claude-haiku\"\n return \"claude-sonnet\"\n}\n\nexport interface ChannelUsage {\n tasks: number\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n tier2InputTokens: number\n tier2OutputTokens: number\n tier2CacheReadTokens: number\n tier2CacheCreateTokens: number\n totalDuration: number\n errors: number\n}\n\nexport interface AgentUsage {\n tasks: number\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n /** Tokens from requests whose input crossed CONTEXT_TIER_THRESHOLD (billed at\n * TIER2_MULTIPLIER × normal rate). Kept separate from the tier1 buckets so\n * cost math stays honest when a session accumulates past 200K. */\n tier2InputTokens?: number\n tier2OutputTokens?: number\n tier2CacheReadTokens?: number\n tier2CacheCreateTokens?: number\n totalDuration: number\n errors: number\n model?: string\n /** Per-channel breakdown so we can attribute cost to Telegram vs GitLab vs WhatsApp. */\n byChannel?: Record<string, ChannelUsage>\n /** Unique session identifiers (channel:chatId) this agent participated in\n * today. Used to derive avg tasks/session. Capped at 500 entries to keep\n * the daily JSON small. */\n sessionKeys?: string[]\n}\n\nexport interface DailyUsage {\n date: string\n agents: Record<string, AgentUsage>\n}\n\nexport interface DailyReport {\n date: string\n totalTasks: number\n totalInput: number\n totalOutput: number\n totalCacheRead: number\n totalCacheCreate: number\n totalCost: number\n topAgent: string\n topCost: number\n agentCosts: Record<string, number>\n /** Per-agent avg tasks/session — high values suggest retry chains. */\n agentSessionStats: Record<string, { sessions: number; avgTasksPerSession: number }>\n /** Aggregate cost by channel across all agents. */\n byChannel: Record<string, number>\n}\n\n/** Shape of a per-request usage report coming from the runtime. Keeps the\n * record() signature tidy and lets callers opt into tier2 accounting. */\nexport interface TaskUsage {\n inputTokens: number\n outputTokens: number\n cacheReadTokens: number\n cacheCreateTokens: number\n}\n\nconst SESSION_KEYS_MAX = 500\nconst PRICING_OVERRIDE_PATH = \".agentx/pricing/custom.json\"\n\nexport class TokenTracker {\n private dir: string\n private cache: DailyUsage | null = null\n private pricing: typeof CACHE_AWARE_PRICING\n\n constructor(baseDir: string = resolve(process.cwd(), \".agentx/usage\")) {\n this.dir = baseDir\n mkdirSync(this.dir, { recursive: true })\n this.pricing = loadPricingWithOverrides()\n }\n\n /** Returns the active pricing table (hardcoded defaults with any per-model\n * overrides merged in). Useful for UI / debugging. */\n getPricing(): typeof CACHE_AWARE_PRICING {\n return this.pricing\n }\n\n /**\n * Calculate cost for a slice of usage. Accepts either a full AgentUsage or\n * a narrowed ChannelUsage — both expose the same token buckets we price on.\n * Tier2 tokens are billed at TIER2_MULTIPLIER × the normal rate.\n */\n static calculateCost(usage: AgentUsage | ChannelUsage, model?: string, pricingTable: typeof CACHE_AWARE_PRICING = CACHE_AWARE_PRICING): number {\n const family = getModelFamily(model || (usage as AgentUsage).model || \"claude-sonnet\")\n const p = pricingTable[family] || pricingTable[\"claude-sonnet\"]\n const tier2In = (usage as any).tier2InputTokens || 0\n const tier2Out = (usage as any).tier2OutputTokens || 0\n const tier2CR = (usage as any).tier2CacheReadTokens || 0\n const tier2CW = (usage as any).tier2CacheCreateTokens || 0\n\n const tier1 =\n ((usage.inputTokens || 0) / 1_000_000) * p.input +\n ((usage.outputTokens || 0) / 1_000_000) * p.output +\n ((usage.cacheReadTokens || 0) / 1_000_000) * p.cacheRead +\n ((usage.cacheCreateTokens || 0) / 1_000_000) * p.cacheCreate\n\n const tier2 =\n (tier2In / 1_000_000) * p.input +\n (tier2Out / 1_000_000) * p.output +\n (tier2CR / 1_000_000) * p.cacheRead +\n (tier2CW / 1_000_000) * p.cacheCreate\n\n return tier1 + tier2 * TIER2_MULTIPLIER\n }\n\n /**\n * Instance-level cost calculation — uses the tracker's resolved pricing\n * table so any custom override file is honored. Prefer this over the\n * static variant when available.\n */\n cost(usage: AgentUsage | ChannelUsage, model?: string): number {\n return TokenTracker.calculateCost(usage, model, this.pricing)\n }\n\n /**\n * Record a task execution with real or estimated token counts.\n *\n * `channel` attributes the cost to a surface (telegram/gitlab/whatsapp/cron).\n * `sessionKey` is an opaque \"this chat on this day\" identifier — we use it\n * to count unique sessions so an avg-tasks-per-session metric falls out.\n */\n record(\n agentId: string,\n duration: number,\n realUsage?: TaskUsage,\n messageLength?: number,\n responseLength?: number,\n error?: boolean,\n model?: string,\n channel?: string,\n sessionKey?: string,\n ): void {\n const daily = this.today()\n const agent = daily.agents[agentId] || {\n tasks: 0, inputTokens: 0, outputTokens: 0,\n cacheReadTokens: 0, cacheCreateTokens: 0,\n tier2InputTokens: 0, tier2OutputTokens: 0,\n tier2CacheReadTokens: 0, tier2CacheCreateTokens: 0,\n totalDuration: 0, errors: 0, byChannel: {}, sessionKeys: [],\n }\n\n agent.tasks++\n agent.totalDuration += duration\n if (model) agent.model = model\n\n // Split this request into tier1/tier2 based on its TOTAL input size\n // (input + cacheCreate + cacheRead). Output inherits the tier of its\n // request. Estimated paths (no realUsage) are always tier1 — they're\n // too small to reach the threshold.\n let i1 = 0, o1 = 0, cr1 = 0, cw1 = 0, i2 = 0, o2 = 0, cr2 = 0, cw2 = 0\n if (realUsage) {\n const totalInput = realUsage.inputTokens + realUsage.cacheReadTokens + realUsage.cacheCreateTokens\n if (totalInput > CONTEXT_TIER_THRESHOLD) {\n i2 = realUsage.inputTokens\n o2 = realUsage.outputTokens\n cr2 = realUsage.cacheReadTokens\n cw2 = realUsage.cacheCreateTokens\n } else {\n i1 = realUsage.inputTokens\n o1 = realUsage.outputTokens\n cr1 = realUsage.cacheReadTokens\n cw1 = realUsage.cacheCreateTokens\n }\n } else if (messageLength !== undefined && responseLength !== undefined) {\n i1 = Math.ceil(messageLength / 4)\n o1 = Math.ceil(responseLength / 4)\n }\n\n agent.inputTokens += i1\n agent.outputTokens += o1\n agent.cacheReadTokens += cr1\n agent.cacheCreateTokens += cw1\n agent.tier2InputTokens = (agent.tier2InputTokens || 0) + i2\n agent.tier2OutputTokens = (agent.tier2OutputTokens || 0) + o2\n agent.tier2CacheReadTokens = (agent.tier2CacheReadTokens || 0) + cr2\n agent.tier2CacheCreateTokens = (agent.tier2CacheCreateTokens || 0) + cw2\n\n if (error) agent.errors++\n\n // Per-channel attribution. Unknown channel falls into \"other\".\n if (!agent.byChannel) agent.byChannel = {}\n const chKey = (channel || \"other\").toLowerCase()\n const ch = agent.byChannel[chKey] || {\n tasks: 0, inputTokens: 0, outputTokens: 0,\n cacheReadTokens: 0, cacheCreateTokens: 0,\n tier2InputTokens: 0, tier2OutputTokens: 0,\n tier2CacheReadTokens: 0, tier2CacheCreateTokens: 0,\n totalDuration: 0, errors: 0,\n }\n ch.tasks++\n ch.totalDuration += duration\n ch.inputTokens += i1; ch.outputTokens += o1\n ch.cacheReadTokens += cr1; ch.cacheCreateTokens += cw1\n ch.tier2InputTokens += i2; ch.tier2OutputTokens += o2\n ch.tier2CacheReadTokens += cr2; ch.tier2CacheCreateTokens += cw2\n if (error) ch.errors++\n agent.byChannel[chKey] = ch\n\n // Session tracking — dedup sessions by opaque key (normally channel:chatId).\n if (sessionKey) {\n if (!agent.sessionKeys) agent.sessionKeys = []\n if (!agent.sessionKeys.includes(sessionKey)) {\n agent.sessionKeys.push(sessionKey)\n if (agent.sessionKeys.length > SESSION_KEYS_MAX) {\n agent.sessionKeys.splice(0, agent.sessionKeys.length - SESSION_KEYS_MAX)\n }\n }\n }\n\n daily.agents[agentId] = agent\n this.save(daily)\n }\n\n /**\n * Get today's usage summary.\n */\n today(): DailyUsage {\n const date = new Date().toISOString().slice(0, 10)\n\n if (this.cache?.date === date) return this.cache\n\n const path = this.filePath(date)\n if (existsSync(path)) {\n try {\n this.cache = JSON.parse(readFileSync(path, \"utf-8\"))\n return this.cache!\n } catch {}\n }\n\n this.cache = { date, agents: {} }\n return this.cache\n }\n\n /**\n * Get usage for a specific date.\n */\n getDate(date: string): DailyUsage | null {\n const path = this.filePath(date)\n if (!existsSync(path)) return null\n try {\n return JSON.parse(readFileSync(path, \"utf-8\"))\n } catch {\n return null\n }\n }\n\n /**\n * Generate a daily cost report for a given date. Includes per-agent,\n * per-channel costs and session statistics.\n */\n generateDailyReport(date?: string): DailyReport | null {\n const targetDate = date || new Date().toISOString().slice(0, 10)\n const usage = this.getDate(targetDate)\n if (!usage) return null\n\n let totalTasks = 0, totalInput = 0, totalOutput = 0\n let totalCacheRead = 0, totalCacheCreate = 0, totalCost = 0\n let topAgent = \"\", topCost = 0\n const agentCosts: Record<string, number> = {}\n const agentSessionStats: Record<string, { sessions: number; avgTasksPerSession: number }> = {}\n const byChannel: Record<string, number> = {}\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const cost = this.cost(agent)\n agentCosts[id] = cost\n totalTasks += agent.tasks\n totalInput += (agent.inputTokens || 0) + (agent.tier2InputTokens || 0)\n totalOutput += (agent.outputTokens || 0) + (agent.tier2OutputTokens || 0)\n totalCacheRead += (agent.cacheReadTokens || 0) + (agent.tier2CacheReadTokens || 0)\n totalCacheCreate += (agent.cacheCreateTokens || 0) + (agent.tier2CacheCreateTokens || 0)\n totalCost += cost\n if (cost > topCost) { topCost = cost; topAgent = id }\n\n const sessions = agent.sessionKeys?.length || 0\n agentSessionStats[id] = {\n sessions,\n avgTasksPerSession: sessions > 0 ? agent.tasks / sessions : agent.tasks,\n }\n\n for (const [chKey, ch] of Object.entries(agent.byChannel || {})) {\n const chCost = this.cost(ch, agent.model)\n byChannel[chKey] = (byChannel[chKey] || 0) + chCost\n }\n }\n\n return {\n date: targetDate, totalTasks, totalInput, totalOutput,\n totalCacheRead, totalCacheCreate, totalCost, topAgent, topCost, agentCosts,\n agentSessionStats, byChannel,\n }\n }\n\n /**\n * Append a daily report row to TOKEN_COSTS.md. Adds channel + session\n * breakdown inline for at-a-glance spend attribution.\n */\n appendToTokenCosts(report: DailyReport, filePath?: string): void {\n const file = filePath || resolve(process.cwd(), \".agentx/TOKEN_COSTS.md\")\n const fmt = (n: number) => n < 0.01 ? n.toFixed(4) : n.toFixed(2)\n const fmtTok = (n: number) =>\n n >= 1_000_000 ? `${(n / 1_000_000).toFixed(1)}M` :\n n >= 1_000 ? `${(n / 1_000).toFixed(0)}K` :\n String(n)\n\n const agentBreakdown = Object.entries(report.agentCosts)\n .sort(([, a], [, b]) => b - a)\n .map(([id, cost]) => {\n const stats = report.agentSessionStats[id]\n const sessTag = stats && stats.sessions > 0 ? ` (${stats.sessions}s, ${stats.avgTasksPerSession.toFixed(1)}t/s)` : \"\"\n return `${id}: $${fmt(cost)}${sessTag}`\n })\n .join(\", \")\n\n const channelBreakdown = Object.entries(report.byChannel)\n .sort(([, a], [, b]) => b - a)\n .map(([ch, cost]) => `${ch}: $${fmt(cost)}`)\n .join(\", \")\n\n const row = `| ${report.date} | ${report.totalTasks} | ${fmtTok(report.totalInput)} | ${fmtTok(report.totalOutput)} | ${fmtTok(report.totalCacheRead)} | ${fmtTok(report.totalCacheCreate)} | $${fmt(report.totalCost)} | ${agentBreakdown} | ${channelBreakdown} |`\n\n if (!existsSync(file)) {\n const header =\n \"# Token Costs\\n\\n\" +\n \"Daily token cost tracking. Auto-appended at midnight (Africa/Tunis).\\n\" +\n \"Agent breakdown: `agent: $cost (Ns, T.Tt/s)` — sessions, tasks/session.\\n\\n\" +\n \"| Date | Tasks | Input | Output | Cache R | Cache W | Cost | Per Agent | Per Channel |\\n\" +\n \"|------|-------|-------|--------|---------|---------|------|-----------|-------------|\\n\"\n writeFileSync(file, header + row + \"\\n\")\n } else {\n appendFileSync(file, row + \"\\n\")\n }\n }\n\n /**\n * Get summary across last N days.\n */\n summary(days: number = 7): {\n totalTasks: number\n totalTokens: number\n totalInput: number\n totalOutput: number\n totalCacheRead: number\n totalCacheCreate: number\n cacheHitRatio: number\n totalErrors: number\n totalCost: number\n byAgent: Record<string, { tasks: number; input: number; output: number; cacheRead: number; cacheCreate: number; total: number; avgDuration: number; cost: number }>\n byChannel: Record<string, { tasks: number; cost: number }>\n sessionStats: Record<string, { sessions: number; avgTasksPerSession: number }>\n } {\n let totalTasks = 0\n let totalInput = 0\n let totalOutput = 0\n let totalCacheRead = 0\n let totalCacheCreate = 0\n let totalErrors = 0\n let totalCost = 0\n const byAgent: Record<string, { tasks: number; input: number; output: number; cacheRead: number; cacheCreate: number; total: number; avgDuration: number; totalDuration: number; cost: number; model?: string; sessions: number }> = {}\n const byChannel: Record<string, { tasks: number; cost: number }> = {}\n\n for (let i = 0; i < days; i++) {\n const date = new Date(Date.now() - i * 86400_000).toISOString().slice(0, 10)\n const usage = this.getDate(date)\n if (!usage) continue\n\n for (const [id, agent] of Object.entries(usage.agents)) {\n const cost = this.cost(agent)\n totalTasks += agent.tasks\n const agentInput = (agent.inputTokens || 0) + (agent.tier2InputTokens || 0)\n const agentOutput = (agent.outputTokens || 0) + (agent.tier2OutputTokens || 0)\n const agentCR = (agent.cacheReadTokens || 0) + (agent.tier2CacheReadTokens || 0)\n const agentCW = (agent.cacheCreateTokens || 0) + (agent.tier2CacheCreateTokens || 0)\n totalInput += agentInput\n totalOutput += agentOutput\n totalCacheRead += agentCR\n totalCacheCreate += agentCW\n totalErrors += agent.errors\n totalCost += cost\n\n const existing = byAgent[id] || { tasks: 0, input: 0, output: 0, cacheRead: 0, cacheCreate: 0, total: 0, avgDuration: 0, totalDuration: 0, cost: 0, sessions: 0 }\n existing.tasks += agent.tasks\n existing.input += agentInput\n existing.output += agentOutput\n existing.cacheRead += agentCR\n existing.cacheCreate += agentCW\n existing.total = existing.input + existing.output + existing.cacheRead + existing.cacheCreate\n existing.totalDuration += agent.totalDuration\n existing.avgDuration = existing.totalDuration / existing.tasks\n existing.cost += cost\n existing.sessions += agent.sessionKeys?.length || 0\n if (agent.model) existing.model = agent.model\n byAgent[id] = existing\n\n for (const [chKey, ch] of Object.entries(agent.byChannel || {})) {\n const chCost = this.cost(ch, agent.model)\n const acc = byChannel[chKey] || { tasks: 0, cost: 0 }\n acc.tasks += ch.tasks\n acc.cost += chCost\n byChannel[chKey] = acc\n }\n }\n }\n\n const totalTokens = totalInput + totalOutput + totalCacheRead + totalCacheCreate\n const cacheHitRatio = (totalCacheRead + totalCacheCreate) > 0\n ? totalCacheRead / (totalCacheRead + totalCacheCreate)\n : 0\n\n const sessionStats: Record<string, { sessions: number; avgTasksPerSession: number }> = {}\n for (const [id, a] of Object.entries(byAgent)) {\n sessionStats[id] = {\n sessions: a.sessions,\n avgTasksPerSession: a.sessions > 0 ? a.tasks / a.sessions : a.tasks,\n }\n }\n\n return { totalTasks, totalTokens, totalInput, totalOutput, totalCacheRead, totalCacheCreate, cacheHitRatio, totalErrors, totalCost, byAgent, byChannel, sessionStats }\n }\n\n private filePath(date: string): string {\n return resolve(this.dir, `${date}.json`)\n }\n\n private save(usage: DailyUsage): void {\n try {\n writeFileSync(this.filePath(usage.date), JSON.stringify(usage, null, 2))\n } catch {}\n }\n}\n\n/**\n * Load CACHE_AWARE_PRICING and merge any operator-supplied override. The\n * override file is optional; operators tweak rates (e.g. when Anthropic\n * publishes a price change) without rebuilding the daemon.\n */\nfunction loadPricingWithOverrides(): typeof CACHE_AWARE_PRICING {\n const result = JSON.parse(JSON.stringify(CACHE_AWARE_PRICING))\n try {\n const p = resolve(process.cwd(), PRICING_OVERRIDE_PATH)\n if (!existsSync(p)) return result\n const raw = JSON.parse(readFileSync(p, \"utf-8\"))\n for (const [family, rates] of Object.entries(raw)) {\n if (!result[family]) result[family] = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 }\n Object.assign(result[family], rates as object)\n }\n } catch {\n // Malformed override file — stay with defaults rather than crash at boot.\n }\n return result\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","appendFileSync","existsSync","mkdirSync","resolve","CACHE_AWARE_PRICING","CONTEXT_TIER_THRESHOLD","TIER2_MULTIPLIER","getModelFamily","model","lower","SESSION_KEYS_MAX","PRICING_OVERRIDE_PATH","TokenTracker","baseDir","loadPricingWithOverrides","usage","pricingTable","family","p","tier2In","tier2Out","tier2CR","tier2CW","tier1","tier2","agentId","duration","realUsage","messageLength","responseLength","error","channel","sessionKey","daily","agent","i1","o1","cr1","cw1","i2","o2","cr2","cw2","chKey","ch","date","path","targetDate","totalTasks","totalInput","totalOutput","totalCacheRead","totalCacheCreate","totalCost","topAgent","topCost","agentCosts","agentSessionStats","byChannel","id","cost","sessions","chCost","report","filePath","file","fmt","fmtTok","agentBreakdown","a","b","stats","sessTag","channelBreakdown","row","days","totalErrors","byAgent","i","agentInput","agentOutput","agentCR","agentCW","existing","acc","totalTokens","cacheHitRatio","sessionStats","result","raw","rates"]}
|
package/dist/chunk-ITZEVXZ4.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{z as e}from"zod";var r=e.object({name:e.string(),color:e.string().regex(/^#[0-9a-f]{6}$/i).default("#6366f1"),description:e.string().optional()}),l=e.object({id:e.string().regex(/^[a-z0-9_-]+$/,"column id must be lowercase slug"),title:e.string(),kind:e.enum(["open-backlog","scoped-label","closed","label"]).default("label"),mapsToLabel:e.string().optional(),scopedLabel:e.string().optional(),scopedPrefix:e.string().default("Status"),accent:e.string().optional()}),c=e.object({enabled:e.boolean().default(!0),staleDoingMinutes:e.number().int().min(1).default(45),respectLunchBreak:e.boolean().default(!0),respectSchedule:e.boolean().default(!0),action:e.enum(["badge","notify"]).default("badge")}),p=e.discriminatedUnion("type",[e.object({type:e.literal("gitlab"),projects:e.array(e.string()).min(1)})]),b=[{id:"open",title:"Open",kind:"open-backlog",scopedPrefix:"Status",accent:"#6b7280"},{id:"todo",title:"To Do",kind:"scoped-label",scopedLabel:"Status::To Do",scopedPrefix:"Status",accent:"#fb7a35"},{id:"doing",title:"Doing",kind:"scoped-label",scopedLabel:"Status::Doing",scopedPrefix:"Status",accent:"#22c55e"},{id:"onhold",title:"On Hold",kind:"scoped-label",scopedLabel:"Status::On Hold",scopedPrefix:"Status",accent:"#f59e0b"},{id:"review",title:"Review",kind:"scoped-label",scopedLabel:"Status::Review",scopedPrefix:"Status",accent:"#3b82f6"},{id:"closed",title:"Closed",kind:"closed",scopedPrefix:"Status",accent:"#6b7280"}],f=e.object({id:e.string().regex(/^[a-z0-9_-]+$/,"board id must be lowercase slug"),name:e.string(),source:p,primaryToolLabel:e.string().optional(),labels:e.array(r).default([]),columns:e.array(l).min(1).default(b),timeRangeDays:e.number().int().min(1).max(365).default(30),closedWindowDays:e.number().int().min(1).max(365).default(30),reconciliation:c.default({})}),m=e.array(f).default([]),u=e.object({name:e.string(),url:e.string(),token:e.string().optional()}),h=e.object({enabled:e.boolean().default(!1),port:e.number().int().default(4202),bind:e.string().default("127.0.0.1"),token:e.string().optional(),daemonUrl:e.string().default("http://localhost:18800"),daemons:e.array(u).default([]),draftAgent:e.string().optional()}).default({});function S(t,a){let n=new Set(t.labels||[]);if(t.state==="closed"){let o=a.find(i=>i.kind==="closed");if(o)return o.id}for(let o of a)if(o.kind==="scoped-label"&&o.scopedLabel&&n.has(o.scopedLabel))return o.id;for(let o of a)if(o.kind==="label"&&o.mapsToLabel&&n.has(o.mapsToLabel))return o.id;let d=a.find(o=>o.kind==="open-backlog");if(d){let o=d.scopedPrefix+"::";return[...n].some(s=>s.startsWith(o))?null:d.id}return a[0]?.id??null}function x(t,a){let n=[],d=[],o=!1,i=!1;return a.kind==="scoped-label"&&a.scopedLabel?n.push(a.scopedLabel):a.kind==="label"&&a.mapsToLabel?n.push(a.mapsToLabel):a.kind==="closed"&&(o=!0),t&&(t.kind==="scoped-label"&&t.scopedLabel?d.push(t.scopedLabel):t.kind==="label"&&t.mapsToLabel?d.push(t.mapsToLabel):t.kind==="closed"&&a.kind!=="closed"&&(i=!0)),{addLabels:n.length?n:void 0,removeLabels:d.length?d:void 0,closeIssue:o||void 0,reopen:i||void 0}}export{m as a,h as b,S as c,x as d};
|
|
2
|
-
//# sourceMappingURL=chunk-ITZEVXZ4.js.map
|