agentix-cli 0.13.0 → 0.14.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.
Files changed (48) hide show
  1. package/README.md +50 -493
  2. package/dist/{agent-5KIMJI3F.js → agent-DW3NYIIL.js} +2 -2
  3. package/dist/board-dashboard-6TKE6NMV.js +1435 -0
  4. package/dist/board-dashboard-6TKE6NMV.js.map +1 -0
  5. package/dist/chunk-6U2XOQHU.js +7 -0
  6. package/dist/chunk-6U2XOQHU.js.map +1 -0
  7. package/dist/chunk-DYOBFJLX.js +11 -0
  8. package/dist/chunk-DYOBFJLX.js.map +1 -0
  9. package/dist/chunk-ITZEVXZ4.js +2 -0
  10. package/dist/chunk-ITZEVXZ4.js.map +1 -0
  11. package/dist/chunk-M33CB2H4.js +6 -0
  12. package/dist/chunk-M33CB2H4.js.map +1 -0
  13. package/dist/chunk-NJR5WF5E.js +5 -0
  14. package/dist/chunk-NJR5WF5E.js.map +1 -0
  15. package/dist/chunk-O72LPMBR.js +12 -0
  16. package/dist/chunk-O72LPMBR.js.map +1 -0
  17. package/dist/chunk-RB73FZXJ.js +133 -0
  18. package/dist/chunk-RB73FZXJ.js.map +1 -0
  19. package/dist/{chunk-VH23CDHL.js → chunk-UPT2UMF6.js} +42 -40
  20. package/dist/chunk-UPT2UMF6.js.map +1 -0
  21. package/dist/{chunk-D6EDMQRV.js → chunk-XVYMZ4M7.js} +3 -3
  22. package/dist/cli.js +54 -48
  23. package/dist/cli.js.map +1 -1
  24. package/dist/config-7QJ2QJIC.js +2 -0
  25. package/dist/config-mutator-GAEVGHDD.js +2 -0
  26. package/dist/index.d.ts +670 -69
  27. package/dist/index.js +1 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/registry-YVAG4EO5.js +2 -0
  30. package/dist/registry-YVAG4EO5.js.map +1 -0
  31. package/dist/{usage-dashboard-2TJQBFSH.js → usage-dashboard-M7AFIGMJ.js} +2 -2
  32. package/package.json +9 -1
  33. package/dist/chunk-34UAFPK4.js +0 -10
  34. package/dist/chunk-34UAFPK4.js.map +0 -1
  35. package/dist/chunk-CJ45Y2IR.js +0 -12
  36. package/dist/chunk-CJ45Y2IR.js.map +0 -1
  37. package/dist/chunk-T25UEXVK.js +0 -126
  38. package/dist/chunk-T25UEXVK.js.map +0 -1
  39. package/dist/chunk-VH23CDHL.js.map +0 -1
  40. package/dist/chunk-WJ6ZF6EZ.js +0 -6
  41. package/dist/chunk-WJ6ZF6EZ.js.map +0 -1
  42. package/dist/config-HAD5E4YX.js +0 -2
  43. package/dist/registry-CDWY7CLL.js +0 -2
  44. /package/dist/{agent-5KIMJI3F.js.map → agent-DW3NYIIL.js.map} +0 -0
  45. /package/dist/{chunk-D6EDMQRV.js.map → chunk-XVYMZ4M7.js.map} +0 -0
  46. /package/dist/{config-HAD5E4YX.js.map → config-7QJ2QJIC.js.map} +0 -0
  47. /package/dist/{registry-CDWY7CLL.js.map → config-mutator-GAEVGHDD.js.map} +0 -0
  48. /package/dist/{usage-dashboard-2TJQBFSH.js.map → usage-dashboard-M7AFIGMJ.js.map} +0 -0
@@ -0,0 +1 @@
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 => ({ '&':'&amp;', '<':'&lt;', '>':'&gt;', '\"':'&quot;', \"'\":'&#39;' }[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 => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[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"]}
@@ -0,0 +1,7 @@
1
+ import{readFileSync as p,writeFileSync as k,existsSync as u,readdirSync as _,statSync as j}from"fs";import{resolve as f,join as v}from"path";var h=/^(\s*)-\s*\[( |x|X)\]\s*(.*)$/,g=/@([a-z0-9_-]+)/i,w=/\[time:\s*([0-9hm\s]+)\]/i;function U(d){let e=d.match(/(\d+)\s*h/i),s=d.match(/(\d+)\s*m/i);return(e?parseInt(e[1],10)*3600:0)+(s?parseInt(s[1],10)*60:0)}var b=class{constructor(e){this.path=e}type="backlog";capabilities={listAll:!1,create:!1,transition:!1,labels:!1,search:!1};read(){let e=f(process.cwd(),this.path);return u(e)?p(e,"utf-8").split(`
2
+ `):[]}write(e){let s=f(process.cwd(),this.path);k(s,e.join(`
3
+ `))}async listOpen(e){let s=this.read(),n=[];for(let t=0;t<s.length;t++){let i=s[t].match(h);if(!i||i[2].toLowerCase()==="x")continue;let r=i[3],l=r.match(g);if(l&&l[1].toLowerCase()!==e.toLowerCase())continue;let a=r.match(w),c=r.replace(g,"").replace(w,"").trim();n.push({id:`backlog:${t}`,title:c||`Task at line ${t+1}`,assignee:l?l[1]:void 0,estimatedSeconds:a?U(a[1]):void 0,priority:t})}return n}async claim(e,s){}async report(e,s){if(!e.startsWith("backlog:"))throw new Error(`not a backlog id: ${e}`);let n=parseInt(e.slice(8),10),t=this.read(),i=t[n];if(!i)throw new Error(`backlog line ${n} missing`);if(!i.match(h))throw new Error(`backlog line ${n} not a checkbox`);if(s.status==="done"){t[n]=i.replace(/- \[ \]/,"- [x]");let r=s.timeSeconds?` <!-- done by agent, ${Math.round(s.timeSeconds/60)}m -->`:" <!-- done by agent -->";t.splice(n+1,0,r)}else s.status==="blocked"?t.splice(n+1,0,` <!-- BLOCKED: ${s.blocker||s.note||"?"} -->`):s.note&&t.splice(n+1,0,` <!-- ${s.note.replace(/-->/g,"\u2014 >")} -->`);this.write(t)}},y=class{constructor(e,s){this.root=e;this.glob=s}type="wiki";capabilities={listAll:!1,create:!1,transition:!1,labels:!1,search:!1};walk(e,s){if(u(e))for(let n of _(e)){let t=v(e,n);j(t).isDirectory()?this.walk(t,s):n.endsWith(".md")&&s.push(t)}}async listOpen(e){let s=[];this.walk(f(process.cwd(),this.root),s);let n=[];for(let t of s){let i=p(t,"utf-8").split(`
4
+ `);for(let o=0;o<i.length;o++){let r=i[o].match(h);if(!r||r[2].toLowerCase()==="x")continue;let l=r[3].match(g);!l||l[1].toLowerCase()!==e.toLowerCase()||n.push({id:`wiki:${t}:${o}`,title:r[3].replace(g,"").trim(),assignee:l[1],priority:o})}}return n}async claim(){}async report(e,s){let n=e.split(":"),t=parseInt(n[n.length-1],10),i=n.slice(1,-1).join(":");if(!u(i))return;let o=p(i,"utf-8").split(`
5
+ `);s.status==="done"&&o[t]&&(o[t]=o[t].replace(/- \[ \]/,"- [x]"),k(i,o.join(`
6
+ `)))}},$=class{constructor(e,s,n,t,i){this.host=e;this.token=s;this.projects=n;this.agentUsernames=t;this.log=i}type="gitlab";capabilities={listAll:!0,create:!0,transition:!0,labels:!0,search:!0};usernameToAgent(e){for(let[s,n]of Object.entries(this.agentUsernames))if(n.some(t=>t.toLowerCase()===e.toLowerCase()))return s}mapIssue(e,s){let n=e.references?.full?.split("#")[0]||s,t=[],i;Array.isArray(e.labels)&&(e.labels.length&&typeof e.labels[0]=="object"?(i=e.labels.map(a=>({name:a.name,color:a.color,text_color:a.text_color})),t=i.map(a=>a.name)):t=e.labels);let r=(Array.isArray(e.assignees)&&e.assignees.length?e.assignees:e.assignee?[e.assignee]:[]).map(a=>({username:a.username,name:a.name,avatarUrl:a.avatar_url})),l=r[0]?.username;return{id:`gitlab:${n}:issue:${e.iid}`,title:e.title,description:e.description||void 0,assignee:l?this.usernameToAgent(l)||l:void 0,assignees:r,url:e.web_url,priority:e.weight??99,estimatedSeconds:e.time_stats?.time_estimate||void 0,labels:t,labelDetails:i,updatedAt:e.updated_at,state:e.state==="closed"?"closed":"opened",milestone:e.milestone?.title||void 0}}async api(e,s={}){let n=await fetch(`${this.host}/api/v4${e}`,{...s,headers:{"PRIVATE-TOKEN":this.token,"Content-Type":"application/json",...s.headers||{}}});if(!n.ok)throw new Error(`GitLab ${n.status}: ${await n.text().catch(()=>"")}`);return n.json()}async listOpen(e){let s=this.agentUsernames[e]||[];if(!s.length)return[];let n=[],t=this.projects.length?this.projects:["__ALL__"];for(let i of s)for(let o of t)try{let r=`assignee_username=${encodeURIComponent(i)}&state=opened&scope=all&per_page=20`,l=o==="__ALL__"?`/issues?${r}`:`/projects/${encodeURIComponent(o)}/issues?${r}`,a=await this.api(l);for(let c of a){let m=this.mapIssue(c,o);m.assignee=e,n.push(m)}}catch(r){this.log(`[business] GitLab listOpen failed for ${i} on ${o}: ${r.message}`)}return n}async claim(e,s){let{project:n,iid:t,type:i}=this.parseId(s),o=`/projects/${encodeURIComponent(n)}/${i==="issue"?"issues":"merge_requests"}/${t}?add_labels=Doing&remove_labels=To Do`;try{await this.api(o,{method:"PUT"})}catch(r){this.log(`[business] claim label update failed: ${r.message}`)}}async report(e,s){let{project:n,iid:t,type:i}=this.parseId(e),o=i==="issue"?"issues":"merge_requests";if(s.timeSeconds&&s.timeSeconds>0){let r=Math.max(1,Math.round(s.timeSeconds/60)),l=r>=60?`${Math.floor(r/60)}h${r%60?` ${r%60}m`:""}`:`${r}m`;try{await this.api(`/projects/${encodeURIComponent(n)}/${o}/${t}/add_spent_time?duration=${encodeURIComponent(l)}`,{method:"POST"})}catch(a){this.log(`[business] add_spent_time failed: ${a.message}`)}}if(s.status==="done")try{await this.api(`/projects/${encodeURIComponent(n)}/${o}/${t}?add_labels=Done&remove_labels=Doing`,{method:"PUT"})}catch(r){this.log(`[business] done label update failed: ${r.message}`)}else if(s.status==="blocked")try{await this.api(`/projects/${encodeURIComponent(n)}/${o}/${t}?add_labels=Blocked`,{method:"PUT"})}catch(r){this.log(`[business] blocked label update failed: ${r.message}`)}if(s.note)try{await this.api(`/projects/${encodeURIComponent(n)}/${o}/${t}/notes`,{method:"POST",body:JSON.stringify({body:s.note})})}catch(r){this.log(`[business] report note failed: ${r.message}`)}}async listAll(e={}){let s=e.sinceDays??30,n=new Date(Date.now()-s*24*36e5).toISOString(),t=new URLSearchParams,i=e.state??"opened";t.set("state",i),t.set("updated_after",n),t.set("scope","all"),t.set("per_page","100"),t.set("with_labels_details","true"),t.set("order_by","updated_at"),t.set("sort","desc"),e.labels?.length&&t.set("labels",e.labels.join(",")),e.milestone&&t.set("milestone",e.milestone),e.search&&t.set("search",e.search);let o=this.projects.length?this.projects:["__ALL__"],r=[];for(let a of o)try{let c=a==="__ALL__"?`/issues?${t.toString()}`:`/projects/${encodeURIComponent(a)}/issues?${t.toString()}`,m=await this.api(c);for(let I of m)r.push(this.mapIssue(I,a))}catch(c){this.log(`[business] GitLab listAll failed on ${a}: ${c.message}`)}let l=r;if(e.notLabels?.length){let a=new Set(e.notLabels);l=l.filter(c=>!(c.labels||[]).some(m=>a.has(m)))}if(e.withoutScopedPrefix){let a=e.withoutScopedPrefix+"::";l=l.filter(c=>!(c.labels||[]).some(m=>m.startsWith(a)))}return l}async setState(e,s){let{project:n,iid:t,type:i}=this.parseId(e),o=i==="issue"?"issues":"merge_requests";await this.api(`/projects/${encodeURIComponent(n)}/${o}/${t}?state_event=${s}`,{method:"PUT"})}async getItem(e){let{project:s,iid:n}=this.parseId(e);try{let t=await this.api(`/projects/${encodeURIComponent(s)}/issues/${n}?with_labels_details=true`);return{...this.mapIssue(t,s),descriptionHtml:t.description_html||void 0,createdAt:t.created_at,author:t.author?{username:t.author.username,name:t.author.name,avatarUrl:t.author.avatar_url}:void 0,allLabels:Array.isArray(t.labels)?typeof t.labels[0]=="object"?t.labels.map(o=>o.name):t.labels:[]}}catch(t){return this.log(`[board] GitLab getItem failed for ${e}: ${t.message}`),null}}async updateItem(e,s){let{project:n,iid:t}=this.parseId(e),i;s.assigneeUsernames&&(i=(await Promise.all(s.assigneeUsernames.map(async c=>{try{let m=await this.api(`/users?username=${encodeURIComponent(c)}`);return Array.isArray(m)&&m[0]?.id?m[0].id:null}catch{return null}}))).filter(c=>typeof c=="number"));let o;if(s.milestoneTitle!==void 0)if(s.milestoneTitle===null||s.milestoneTitle==="")o=0;else try{let a=await this.api(`/projects/${encodeURIComponent(n)}/milestones?title=${encodeURIComponent(s.milestoneTitle)}`);Array.isArray(a)&&a[0]?.id&&(o=a[0].id)}catch(a){this.log(`[board] milestone lookup failed for "${s.milestoneTitle}": ${a.message}`)}let r={};s.title!==void 0&&(r.title=s.title),s.description!==void 0&&(r.description=s.description),s.labels!==void 0&&(r.labels=s.labels.join(",")),s.addLabels?.length&&(r.add_labels=s.addLabels.join(",")),s.removeLabels?.length&&(r.remove_labels=s.removeLabels.join(",")),i!==void 0&&(r.assignee_ids=i),o!==void 0&&(r.milestone_id=o);let l=await this.api(`/projects/${encodeURIComponent(n)}/issues/${t}`,{method:"PUT",body:JSON.stringify(r)});return this.mapIssue(l,n)}async listMembers(){let e=new Map;for(let s of this.projects)try{let n=await this.api(`/projects/${encodeURIComponent(s)}/members/all?per_page=100`);for(let t of n)t.username&&!e.has(t.username)&&e.set(t.username,{username:t.username,name:t.name,avatarUrl:t.avatar_url})}catch(n){this.log(`[board] listMembers failed for ${s}: ${n.message}`)}return[...e.values()].sort((s,n)=>s.username.localeCompare(n.username))}async listMilestones(){let e=new Map;for(let n of this.projects)try{let t=await this.api(`/projects/${encodeURIComponent(n)}/milestones?per_page=100`);for(let i of t)i.title&&!e.has(i.title)&&e.set(i.title,{id:i.id,title:i.title,state:i.state,dueDate:i.due_date})}catch(t){this.log(`[board] listMilestones failed for ${n}: ${t.message}`)}let s=[...e.values()];return s.sort((n,t)=>n.state!==t.state?n.state==="active"?-1:1:n.title.localeCompare(t.title)),s}async create(e){let s=e.projectHint||this.projects[0];if(!s)throw new Error("GitLabWorkSource.create requires projectHint or configured projects");let n,t=e.assigneeUsernames?.length?e.assigneeUsernames:e.assignee?(this.agentUsernames[e.assignee]||[]).slice(0,1):[];t.length&&(n=(await Promise.all(t.map(async a=>{try{let c=await this.api(`/users?username=${encodeURIComponent(a)}`);return Array.isArray(c)&&c[0]?.id?c[0].id:null}catch(c){return this.log(`[board] user lookup failed for ${a}: ${c.message}`),null}}))).filter(a=>typeof a=="number"));let i;if(e.milestoneTitle)try{let l=await this.api(`/projects/${encodeURIComponent(s)}/milestones?title=${encodeURIComponent(e.milestoneTitle)}`);Array.isArray(l)&&l[0]?.id&&(i=l[0].id)}catch(l){this.log(`[board] milestone lookup failed for "${e.milestoneTitle}": ${l.message}`)}let o={title:e.title,description:e.description,labels:(e.labels||[]).join(",")};n?.length&&(o.assignee_ids=n),i!==void 0&&(o.milestone_id=i);let r=await this.api(`/projects/${encodeURIComponent(s)}/issues`,{method:"POST",body:JSON.stringify(o)});return this.mapIssue(r,s)}async transition(e,s,n){await this.transitionMany(e,s?[s]:[],n?[n]:[])}async transitionMany(e,s,n){let{project:t,iid:i,type:o}=this.parseId(e),r=o==="issue"?"issues":"merge_requests",l=new URLSearchParams;s.length&&l.set("add_labels",s.join(",")),n.length&&l.set("remove_labels",n.join(",")),l.toString()&&await this.api(`/projects/${encodeURIComponent(t)}/${r}/${i}?${l.toString()}`,{method:"PUT"})}parseId(e){let n=e.replace(/^gitlab:/,"").split(":"),t=n.pop(),i=n.pop()==="mr"?"mr":"issue";return{project:n.join(":"),type:i,iid:t}}};function W(d,e,s){let n=d.workSource;switch(n.type){case"backlog":return new b(n.path);case"wiki":return new y(n.path,n.glob);case"gitlab":{let t=e.channels.gitlab;if(!t?.token)throw new Error("business.workSource.type=gitlab requires channels.gitlab.token");let i={};for(let r of t.agentMappings)r.gitlabUsernames.length&&(i[r.agentId]=r.gitlabUsernames);let o=n.projects.length?n.projects:t.routes.map(r=>r.project);return new $(t.host,t.token,o,i,s)}}}export{$ as a,W as b};
7
+ //# sourceMappingURL=chunk-6U2XOQHU.js.map