@usex/mikrotik-mcp 3.2.0 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -20205,6 +20205,107 @@ async function captureRoutes(req, url) {
20205
20205
  }
20206
20206
  return null;
20207
20207
  }
20208
+ var snapStorePromise = null;
20209
+ function snapStore() {
20210
+ if (!snapStorePromise)
20211
+ snapStorePromise = openSnapshotStore(DEFAULT_SNAPSHOT_DB);
20212
+ return snapStorePromise;
20213
+ }
20214
+ async function featureRoutes(req, url) {
20215
+ const p = url.pathname;
20216
+ if (p === "/api/snapshots" && req.method === "GET") {
20217
+ const store2 = await snapStore();
20218
+ const cfg = getConfig();
20219
+ const all = Object.keys(cfg.devices).flatMap((d) => store2.list(d, 200, false));
20220
+ all.sort((a, b) => b.ts - a.ts);
20221
+ return json({ snapshots: all });
20222
+ }
20223
+ const snapMatch = p.match(/^\/api\/snapshot\/(.+)$/);
20224
+ if (snapMatch && req.method === "GET") {
20225
+ const store2 = await snapStore();
20226
+ const s = store2.get(decodeURIComponent(snapMatch[1]));
20227
+ return s ? json(s) : json({ error: "not found" }, 404);
20228
+ }
20229
+ if (p === "/api/snapshots/diff" && req.method === "POST") {
20230
+ const store2 = await snapStore();
20231
+ const b = await readJson(req);
20232
+ const from = b?.from ? store2.get(b.from) : null;
20233
+ const to = b?.to ? store2.get(b.to) : null;
20234
+ if (!from || !to) {
20235
+ return json({ error: "both 'from' and 'to' snapshot ids are required" }, 400);
20236
+ }
20237
+ const diff = diffLines(normalizeExport(from.body), normalizeExport(to.body), {
20238
+ fromLabel: from.label ?? from.id,
20239
+ toLabel: to.label ?? to.id
20240
+ });
20241
+ return json({
20242
+ from: { id: from.id, label: from.label, ts: from.ts, device: from.device },
20243
+ to: { id: to.id, label: to.label, ts: to.ts, device: to.device },
20244
+ summary: diff.summary,
20245
+ unified: diff.unified
20246
+ });
20247
+ }
20248
+ if (p === "/api/plan" && req.method === "POST") {
20249
+ const b = await readJson(req);
20250
+ const commands = [
20251
+ ...(b?.commands ?? []).map((c) => c.trim()).filter(Boolean),
20252
+ ...b?.script ? splitCommands(b.script) : []
20253
+ ];
20254
+ if (commands.length === 0)
20255
+ return json({ error: "no commands provided" }, 400);
20256
+ const plan = buildChangePlan(commands);
20257
+ return json({ plan, text: renderPlan(plan) });
20258
+ }
20259
+ if (p === "/api/s3" && req.method === "GET") {
20260
+ return json({ configured: isS3Configured(), target: isS3Configured() ? s3Target() : null });
20261
+ }
20262
+ if (p === "/api/s3/list" && req.method === "GET") {
20263
+ if (!isS3Configured())
20264
+ return json({ configured: false, objects: [] });
20265
+ const prefix = url.searchParams.get("prefix") ?? "";
20266
+ try {
20267
+ const res = await getS3Client().list({ prefix: prefix || undefined, maxKeys: 1000 });
20268
+ const objects = (res.contents ?? []).map((o) => ({
20269
+ key: o.key,
20270
+ size: o.size ?? 0,
20271
+ lastModified: o.lastModified ?? null
20272
+ }));
20273
+ return json({ configured: true, target: s3Target(), objects, truncated: !!res.isTruncated });
20274
+ } catch (e) {
20275
+ return json({ error: e instanceof Error ? e.message : String(e) }, 502);
20276
+ }
20277
+ }
20278
+ if (p === "/api/s3/presign" && req.method === "GET") {
20279
+ if (!isS3Configured())
20280
+ return json({ error: "S3 not configured" }, 400);
20281
+ const key = url.searchParams.get("key");
20282
+ if (!key)
20283
+ return json({ error: "key required" }, 400);
20284
+ try {
20285
+ const link = getS3Client().presign(key, { expiresIn: presignExpiresIn(), method: "GET" });
20286
+ return json({ url: link });
20287
+ } catch (e) {
20288
+ return json({ error: e instanceof Error ? e.message : String(e) }, 502);
20289
+ }
20290
+ }
20291
+ if (p === "/api/s3/delete" && req.method === "POST") {
20292
+ if (!isS3Configured())
20293
+ return json({ error: "S3 not configured" }, 400);
20294
+ const b = await readJson(req);
20295
+ if (!b?.key)
20296
+ return json({ error: "key required" }, 400);
20297
+ try {
20298
+ const client = getS3Client();
20299
+ if (!await client.exists(b.key))
20300
+ return json({ error: "not found" }, 404);
20301
+ await client.delete(b.key);
20302
+ return json({ ok: true, key: b.key });
20303
+ } catch (e) {
20304
+ return json({ error: e instanceof Error ? e.message : String(e) }, 502);
20305
+ }
20306
+ }
20307
+ return null;
20308
+ }
20208
20309
  async function runDashboard(cfg, transportLabel) {
20209
20310
  const store2 = await openSqliteStore(cfg.dbPath);
20210
20311
  configureRecorder({
@@ -20268,6 +20369,9 @@ async function runDashboard(cfg, transportLabel) {
20268
20369
  const captureResp = await captureRoutes(req, url);
20269
20370
  if (captureResp)
20270
20371
  return captureResp;
20372
+ const featureResp = await featureRoutes(req, url);
20373
+ if (featureResp)
20374
+ return featureResp;
20271
20375
  const db = getEventStore();
20272
20376
  if (!db)
20273
20377
  return json({ error: "recorder not active" }, 503);
@@ -20509,7 +20613,7 @@ function registerPrompts(server) {
20509
20613
  // package.json
20510
20614
  var package_default = {
20511
20615
  name: "@usex/mikrotik-mcp",
20512
- version: "3.2.0",
20616
+ version: "3.3.0",
20513
20617
  description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
20514
20618
  keywords: [
20515
20619
  "ai",
package/dist/index.js CHANGED
@@ -19337,7 +19337,7 @@ var allToolModules = moduleCatalog.map((m) => m.tools);
19337
19337
  // package.json
19338
19338
  var package_default = {
19339
19339
  name: "@usex/mikrotik-mcp",
19340
- version: "3.2.0",
19340
+ version: "3.3.0",
19341
19341
  description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
19342
19342
  keywords: [
19343
19343
  "ai",
@@ -20,11 +20,15 @@ Error generating stack: `+e.message+`
20
20
  `).length;return(0,$.jsxs)(`div`,{className:`cfgstudio`,children:[(0,$.jsxs)(`div`,{className:`cfg-toolbar`,children:[(0,$.jsx)(`span`,{className:`cfg-status ${A?`is-ok`:`is-bad`}`,children:u?`invalid JSON`:s.length?`${s.length} schema issue(s)`:`valid ✓`}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`topo-btn`,onClick:()=>void F(),children:`Test devices`}),(0,$.jsx)(`button`,{className:`topo-btn`,onClick:()=>void I(),disabled:!A,children:`Preview diff`}),(0,$.jsx)(`select`,{className:`cfg-select`,value:S,onChange:e=>C(Number(e.target.value)),title:`Auto-revert window`,children:Fc.map(([e,t])=>(0,$.jsx)(`option`,{value:t,children:e},t))}),(0,$.jsx)(`button`,{className:`topo-btn cfg-save`,onClick:()=>void L(),disabled:!A||!!v,children:`Save`}),(0,$.jsx)(`button`,{className:`topo-btn`,onClick:t,children:`Close`})]}),w&&(0,$.jsx)(`div`,{className:`cfg-msg`,children:w}),v&&(0,$.jsxs)(`div`,{className:`cfg-banner`,children:[(0,$.jsx)(`strong`,{children:`Applied.`}),` `,(v.rollbackMs??0)>0?(0,$.jsxs)($.Fragment,{children:[`Reverting in `,(0,$.jsxs)(`span`,{className:`cfg-count`,children:[b,`s`]}),` unless you keep it.`]}):(0,$.jsx)($.Fragment,{children:`Saved without an auto-revert window.`}),v.devicesChanged&&(0,$.jsxs)(`span`,{className:`cfg-warn`,children:[` `,`· device list changed — reconnect the MCP client to expose it to the model`]}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`topo-btn cfg-save`,onClick:()=>void ee(),children:`Keep changes`}),(0,$.jsx)(`button`,{className:`topo-btn`,onClick:()=>void te(),children:`Revert now`})]}),(0,$.jsxs)(`div`,{className:`cfg-editor`,children:[(0,$.jsx)(`pre`,{className:`cfg-gutter`,"aria-hidden":`true`,ref:k,children:Array.from({length:ne},(e,t)=>t+1).join(`
21
21
  `)}),(0,$.jsxs)(`div`,{className:`cfg-ta-wrap`,children:[(0,$.jsxs)(`pre`,{className:`cfg-hl`,"aria-hidden":`true`,ref:O,children:[Vc(r),`
22
22
  `]}),(0,$.jsx)(`textarea`,{ref:E,className:`cfg-ta`,spellCheck:!1,wrap:`off`,value:r,onChange:e=>{i(e.target.value),j(e.currentTarget)},onKeyDown:N,onClick:()=>p(null),onScroll:e=>{let t=e.currentTarget;O.current&&(O.current.scrollTop=t.scrollTop,O.current.scrollLeft=t.scrollLeft),k.current&&(k.current.scrollTop=t.scrollTop)}}),f&&(0,$.jsx)(`div`,{className:`cfg-ac`,style:{left:f.x,top:f.y},children:f.items.map((e,t)=>(0,$.jsx)(`div`,{className:`cfg-ac-item${t===f.index?` is-sel`:``}`,onMouseDown:t=>{t.preventDefault(),M(e)},children:e},e))})]})]}),Object.keys(m).length>0&&(0,$.jsx)(`div`,{className:`cfg-chips`,children:Object.entries(m).map(([e,t])=>(0,$.jsxs)(`span`,{className:`cfg-chip ${t.ok?`is-ok`:`is-bad`}`,children:[t.ok?`●`:`○`,` `,e,`: `,t.label]},e))}),(u||s.length>0)&&(0,$.jsx)(`div`,{className:`cfg-errors`,children:u?(0,$.jsxs)(`div`,{className:`cfg-err`,children:[`JSON: `,u]}):s.slice(0,12).map((e,t)=>(0,$.jsxs)(`div`,{className:`cfg-err`,children:[(0,$.jsx)(`code`,{children:e.path}),` — `,e.message]},t))}),g&&(0,$.jsxs)(`div`,{className:`cfg-preview`,children:[(0,$.jsxs)(`div`,{className:`cfg-preview__hd`,children:[(0,$.jsx)(`strong`,{children:`Diff vs current`}),(0,$.jsx)(`span`,{className:`muted`,children:g.summary?.changed?`+${g.summary.added} / -${g.summary.removed}`:`no changes`}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`topo-btn`,onClick:()=>_(null),children:`✕`})]}),(0,$.jsx)(`pre`,{className:`cfg-diff`,children:(g.unified||`(identical)`).split(`
23
- `).map((e,t)=>(0,$.jsx)(`div`,{className:e.startsWith(`+`)?`d-add`:e.startsWith(`-`)?`d-del`:e.startsWith(`@@`)?`d-hunk`:``,children:e||` `},t))})]})]})}function Lc(e){return e==null?`#3f3f46`:e>=85?`#f87171`:e>=60?`#a1a1aa`:`#d4d4d8`}function Rc({topo:e,onOnboard:t}){let[n,r]=(0,l.useState)(null),i=e.nodes.filter(e=>e.kind===`device`),a=e.nodes.filter(e=>e.kind===`neighbor`),o=Math.max(1,i.length),s=i.length<=1?0:Math.min(150,76+o*8),c=s+(i.length<=1?180:140),u=Math.max(360,Math.round((c+78)*2)),d=u/2,f=new Map,p=new Map;i.forEach((e,t)=>{if(i.length===1){f.set(e.id,{x:380,y:d}),p.set(e.id,-Math.PI/2);return}let n=t/o*Math.PI*2-Math.PI/2;p.set(e.id,n),f.set(e.id,{x:380+s*Math.cos(n),y:d+s*Math.sin(n)})});let m=new Map,h=new Set(a.map(e=>e.id));for(let t of e.edges)h.has(t.to)&&!m.has(t.to)&&m.set(t.to,t.from);let g=new Map;for(let e of a){let t=m.get(e.id)??i[0]?.id??``;g.set(t,[...g.get(t)??[],e.id])}if(i.length<=1)a.forEach((e,t)=>{let n=t/Math.max(1,a.length)*Math.PI*2-Math.PI/2;f.set(e.id,{x:380+c*Math.cos(n),y:d+c*Math.sin(n)})});else for(let[e,t]of g){let n=p.get(e)??-Math.PI/2,r=Math.min(Math.PI/2.2,.32*t.length);t.forEach((e,i)=>{let a=t.length===1?n:n-r/2+r*i/(t.length-1);f.set(e,{x:380+c*Math.cos(a),y:d+c*Math.sin(a)})})}let _=new Map(e.nodes.map(e=>[e.id,e])),v=n?_.get(n):null;return(0,$.jsxs)(`div`,{className:`topo`,children:[(0,$.jsxs)(`svg`,{viewBox:`0 0 760 ${u}`,width:`100%`,height:Math.min(u,540),preserveAspectRatio:`xMidYMid meet`,children:[e.edges.map((e,t)=>{let n=f.get(e.from),r=f.get(e.to);return!n||!r?null:(0,$.jsx)(`line`,{className:`topo-edge${h.has(e.to)?` is-dashed`:``}`,x1:n.x,y1:n.y,x2:r.x,y2:r.y,children:(0,$.jsxs)(`title`,{children:[e.from,` → `,e.to,e.interface?` (${e.interface})`:``]})},`e-${t}`)}),e.nodes.map(e=>{let t=f.get(e.id);if(!t)return null;let i=e.kind===`device`,a=i?132:108,o=i?50:38,s=t.x-a/2,c=t.y-o/2,l=e.reachable===!0?`#d4d4d8`:e.reachable===!1?`#f87171`:i?`#71717a`:`#e4e4e7`,u=n===e.id;return(0,$.jsxs)(`g`,{className:`topo-node${i?` is-device`:` is-neighbor`}${e.onboardable?` is-onboard`:``}${u?` is-picked`:``}`,transform:`translate(${s},${c})`,onClick:()=>r(t=>t===e.id?null:e.id),children:[(0,$.jsx)(`rect`,{width:a,height:o,rx:9,style:{stroke:l}}),(0,$.jsx)(`text`,{className:`topo-label`,x:9,y:16,children:(e.label||e.id).slice(0,16)}),i?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`text`,{className:`topo-sub`,x:9,y:30,children:(e.board||e.ip||``).slice(0,18)}),(0,$.jsx)(`rect`,{className:`topo-bar-bg`,x:9,y:37,width:a-18,height:4,rx:2}),(0,$.jsx)(`rect`,{x:9,y:37,width:(a-18)*Math.min(100,e.cpuLoad??0)/100,height:4,rx:2,style:{fill:Lc(e.cpuLoad)}}),(0,$.jsx)(`rect`,{className:`topo-bar-bg`,x:9,y:43,width:a-18,height:4,rx:2}),(0,$.jsx)(`rect`,{x:9,y:43,width:(a-18)*Math.min(100,e.memUsedPct??0)/100,height:4,rx:2,style:{fill:Lc(e.memUsedPct)}})]}):(0,$.jsx)(`text`,{className:`topo-sub`,x:9,y:29,children:e.onboardable?`+ onboard`:e.ip||e.mac||``})]},e.id)})]}),(0,$.jsxs)(`div`,{className:`topo-foot`,children:[(0,$.jsxs)(`span`,{className:`legend`,children:[(0,$.jsxs)(`span`,{children:[(0,$.jsx)(`i`,{className:`dot`,style:{background:`#d4d4d8`}}),` online`]}),(0,$.jsxs)(`span`,{children:[(0,$.jsx)(`i`,{className:`dot`,style:{background:`#f87171`}}),` offline`]}),(0,$.jsxs)(`span`,{children:[(0,$.jsx)(`i`,{className:`dot`,style:{background:`#e4e4e7`}}),` neighbour`]}),(0,$.jsxs)(`span`,{className:`muted`,children:[e.stats.devices,` devices · `,e.stats.neighbors,` discovered ·`,` `,e.stats.onboardable,` onboardable`]})]}),v&&(0,$.jsxs)(`div`,{className:`topo-pop`,children:[(0,$.jsxs)(`div`,{className:`topo-pop__hd`,children:[(0,$.jsx)(`strong`,{children:v.label}),(0,$.jsx)(`span`,{className:`muted`,children:[v.board,v.version,v.mac].filter(Boolean).join(` · `)||`no details advertised`}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`topo-btn`,onClick:()=>r(null),children:`✕`})]}),v.suggestedConfig?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`muted`,style:{margin:`2px 0 6px`},children:`Not managed yet — add this to your device config to onboard it:`}),(0,$.jsx)(`pre`,{className:`topo-stub`,children:JSON.stringify({[v.suggestedConfig.name]:zc(v.suggestedConfig)},null,2)}),(0,$.jsx)(`button`,{className:`topo-btn`,onClick:()=>void navigator.clipboard?.writeText(JSON.stringify({[v.suggestedConfig.name]:zc(v.suggestedConfig)},null,2)),children:`Copy config stub`}),t&&(0,$.jsx)(`button`,{className:`topo-btn cfg-save`,onClick:()=>t(v.suggestedConfig.name,zc(v.suggestedConfig)),children:`Add to config →`})]}):(0,$.jsxs)(`div`,{className:`muted`,children:[`Managed device`,v.ip?` · ${v.ip}`:``,v.uptime?` · up ${v.uptime}`:``]})]})]})]})}function zc(e){let t={port:e.port,username:e.username};return e.host&&(t.host=e.host),e.mac&&(t.mac=e.mac),t}var Bc=/("(?:\\.|[^"\\])*"(?:\s*:)?)|\b(true|false|null)\b|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g;function Vc(e){let t=[],n=0,r=0;for(let i of e.matchAll(Bc)){let a=i.index??0;a>n&&t.push(e.slice(n,a));let o=i[0],s;s=i[1]===void 0?i[2]===void 0?`j-num`:o===`null`?`j-null`:`j-bool`:/:\s*$/.test(o)?`j-key`:`j-str`,t.push((0,$.jsx)(`span`,{className:s,children:o},r++)),n=a+o.length}return n<e.length&&t.push(e.slice(n)),t}var Hc={TCP:`#e4e4e7`,UDP:`#d4d4d8`,ICMP:`#a1a1aa`,ICMPv6:`#a1a1aa`,ARP:`#e4e4e7`,IPv6:`#a1a1aa`},Uc=e=>e&&Hc[e]||`#71717a`;function Wc(){let[e,t]=(0,l.useState)(null),[n,r]=(0,l.useState)(!1);(0,l.useEffect)(()=>{let e=()=>void ac(`/api/capture/packets?limit=150`).then(t).catch(()=>{});e();let n=setInterval(e,1500);return()=>clearInterval(n)},[]);let i=e?.stats,a=e?.packets??[],o=async()=>{r(!0),await sc(`/api/capture/stop`,{}).catch(()=>{}),r(!1)};if(!i||!i.running&&i.packets===0)return(0,$.jsxs)(`div`,{className:`cap-idle muted`,children:[`No capture running. Start one with the `,(0,$.jsx)(`code`,{children:`start_packet_capture`}),` tool — point the device's TZSP stream at this host — and decoded packets stream in here live.`]});let s=Math.max(1,...Object.values(i.protocols));return(0,$.jsxs)(`div`,{className:`cap`,children:[(0,$.jsxs)(`div`,{className:`cap-bar`,children:[(0,$.jsx)(`span`,{className:`cap-dot${i.running?` is-on`:``}`}),(0,$.jsx)(`b`,{children:i.running?`capturing`:`stopped`}),(0,$.jsxs)(`span`,{className:`muted`,children:[`UDP `,i.port]}),(0,$.jsxs)(`span`,{className:`muted`,children:[ec(i.packets),` pkts · `,Qs(i.bytes)]}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`a`,{className:`btn`,href:ic(`/api/capture/pcap`),download:`capture.pcap`,children:`⤓ pcap`}),(0,$.jsx)(`button`,{className:`btn btn-danger`,onClick:()=>void o(),disabled:n||!i.running,children:`■ Stop`})]}),(0,$.jsxs)(`div`,{className:`cap-cols`,children:[(0,$.jsxs)(`div`,{className:`cap-side`,children:[(0,$.jsx)(`div`,{className:`cap-h`,children:`Protocols`}),Object.entries(i.protocols).map(([e,t])=>(0,$.jsxs)(`div`,{className:`cap-pbar`,children:[(0,$.jsx)(`span`,{className:`cap-plabel`,style:{color:Uc(e)},children:e}),(0,$.jsx)(`span`,{className:`cap-ptrack`,children:(0,$.jsx)(`i`,{style:{width:`${t/s*100}%`,background:Uc(e)}})}),(0,$.jsx)(`span`,{className:`cap-pn`,children:t})]},e)),(0,$.jsx)(`div`,{className:`cap-h`,style:{marginTop:12},children:`Top talkers`}),i.topTalkers.length===0&&(0,$.jsx)(`div`,{className:`muted`,children:`—`}),i.topTalkers.map(e=>(0,$.jsxs)(`div`,{className:`cap-talker`,children:[(0,$.jsx)(`span`,{children:e.addr}),(0,$.jsx)(`b`,{children:e.count})]},e.addr))]}),(0,$.jsx)(`div`,{className:`cap-list`,children:a.length===0?(0,$.jsx)(`div`,{className:`muted`,style:{padding:10},children:`waiting for packets…`}):a.map((e,t)=>(0,$.jsxs)(`div`,{className:`cap-row`,children:[(0,$.jsx)(`span`,{className:`cap-tt`,children:$s(e.ts)}),(0,$.jsx)(`span`,{className:`cap-proto`,style:{color:Uc(e.protocol)},children:e.protocol??e.ethType}),(0,$.jsx)(`span`,{className:`cap-len`,children:e.len}),(0,$.jsx)(`span`,{className:`cap-info`,children:e.info})]},t))})]})]})}function Gc({value:e,maxHeight:t}){let n=typeof e==`string`?e:JSON.stringify(e,null,2);return(0,$.jsx)(`pre`,{className:`body json`,style:t?{maxHeight:t}:void 0,children:Vc(n)})}function Kc({event:e,onClose:t}){let n=e=>void navigator.clipboard?.writeText(e).catch(()=>{});return(0,$.jsx)(`div`,{className:`overlay`,onClick:e=>e.target===e.currentTarget&&t(),children:(0,$.jsxs)(`div`,{className:`sheet`,children:[(0,$.jsxs)(`div`,{className:`sheet__hd`,children:[(0,$.jsx)(`span`,{className:`risk risk-${e.risk}`,children:e.risk}),(0,$.jsx)(`h3`,{children:e.tool}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`btn`,onClick:t,children:`✕ Close`})]}),(0,$.jsxs)(`div`,{className:`kv__body`,children:[(0,$.jsx)(`div`,{className:`kv__k`,children:`title`}),(0,$.jsx)(`div`,{className:`kv__v`,children:e.title}),(0,$.jsx)(`div`,{className:`kv__k`,children:`time`}),(0,$.jsx)(`div`,{className:`kv__v`,children:new Date(e.ts).toLocaleString()}),(0,$.jsx)(`div`,{className:`kv__k`,children:`device`}),(0,$.jsx)(`div`,{className:`kv__v`,children:e.device??`—`}),(0,$.jsx)(`div`,{className:`kv__k`,children:`transport`}),(0,$.jsx)(`div`,{className:`kv__v`,children:e.transport??`—`}),(0,$.jsx)(`div`,{className:`kv__k`,children:`duration`}),(0,$.jsx)(`div`,{className:`kv__v`,children:Zs(e.durationMs)}),(0,$.jsx)(`div`,{className:`kv__k`,children:`status`}),(0,$.jsx)(`div`,{className:`kv__v`,children:(0,$.jsx)(`span`,{className:e.isError?`status-err`:`status-ok`,children:e.isError?`error`:`ok`})}),(0,$.jsx)(`div`,{className:`kv__k`,children:`output size`}),(0,$.jsxs)(`div`,{className:`kv__v`,children:[Qs(e.outputBytes),e.truncated?` (truncated)`:``]}),(0,$.jsx)(`div`,{className:`kv__k`,children:`structured`}),(0,$.jsx)(`div`,{className:`kv__v`,children:e.hasStructured?`yes (renders an MCP App view)`:`no`})]}),e.error&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`h2`,{className:`muted`,children:`ERROR`}),(0,$.jsx)(`pre`,{className:`body`,style:{color:`var(--mt-bad)`},children:e.error})]}),(0,$.jsxs)(`div`,{className:`sheet__hd`,children:[(0,$.jsx)(`h2`,{className:`muted`,style:{margin:0},children:`INPUT`}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>n(e.input),children:`Copy`})]}),e.input?(0,$.jsx)(Gc,{value:e.input}):(0,$.jsx)(`pre`,{className:`body`,children:`—`}),(0,$.jsxs)(`div`,{className:`sheet__hd`,children:[(0,$.jsx)(`h2`,{className:`muted`,style:{margin:0},children:`OUTPUT`}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>n(e.output),children:`Copy`})]}),(0,$.jsx)(`pre`,{className:`body`,children:e.output||`—`})]})})}function qc(e,t,n){let r=URL.createObjectURL(new Blob([t],{type:`${n};charset=utf-8`})),i=document.createElement(`a`);i.href=r,i.download=e,document.body.append(i),i.click(),i.remove(),URL.revokeObjectURL(r)}var Jc=[{id:`overview`,label:`Overview`,sub:`Calls, latency & risk at a glance`},{id:`devices`,label:`Devices`,sub:`Connectivity radar & system health`},{id:`topology`,label:`Topology`,sub:`Layer-2 neighbours via MNDP / CDP / LLDP`},{id:`packets`,label:`Packets`,sub:`Live TZSP capture & decode`},{id:`config`,label:`Config`,sub:`Effective configuration & safe editor`},{id:`feed`,label:`Live Feed`,sub:`Every tool call, in real time`}];function Yc({name:e}){return(0,$.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.7`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:{overview:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`rect`,{x:`3`,y:`3`,width:`8`,height:`8`,rx:`1.6`}),(0,$.jsx)(`rect`,{x:`13`,y:`3`,width:`8`,height:`5`,rx:`1.6`}),(0,$.jsx)(`rect`,{x:`13`,y:`10`,width:`8`,height:`11`,rx:`1.6`}),(0,$.jsx)(`rect`,{x:`3`,y:`13`,width:`8`,height:`8`,rx:`1.6`})]}),devices:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`rect`,{x:`3`,y:`4`,width:`18`,height:`7`,rx:`2`}),(0,$.jsx)(`rect`,{x:`3`,y:`13`,width:`18`,height:`7`,rx:`2`}),(0,$.jsx)(`path`,{d:`M7 7.5h.01M7 16.5h.01`})]}),topology:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`circle`,{cx:`12`,cy:`5`,r:`2.4`}),(0,$.jsx)(`circle`,{cx:`5`,cy:`19`,r:`2.4`}),(0,$.jsx)(`circle`,{cx:`19`,cy:`19`,r:`2.4`}),(0,$.jsx)(`path`,{d:`M12 7.4 6.4 16.6M12 7.4 17.6 16.6`})]}),packets:(0,$.jsx)(`path`,{d:`M3 12h4l2-7 4 14 2-7h6`}),config:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`path`,{d:`M4 7h8M16 7h4M4 17h4M12 17h8`}),(0,$.jsx)(`circle`,{cx:`14`,cy:`7`,r:`2.2`}),(0,$.jsx)(`circle`,{cx:`10`,cy:`17`,r:`2.2`})]}),feed:(0,$.jsx)(`path`,{d:`M4 6h16M4 12h16M4 18h10`})}[e]})}function Xc(){let[e,t]=(0,l.useState)(null),[n,r]=(0,l.useState)(null),[i,a]=(0,l.useState)(null),[o,s]=(0,l.useState)(null),[c,u]=(0,l.useState)(null),[d,f]=(0,l.useState)(!1),[p,m]=(0,l.useState)(null),[h,g]=(0,l.useState)([]),[_,v]=(0,l.useState)(36e5),[y,b]=(0,l.useState)(!1),[x,S]=(0,l.useState)(`off`),[C,w]=(0,l.useState)(null),[T,E]=(0,l.useState)(()=>new Set),[D,O]=(0,l.useState)({}),[k,A]=(0,l.useState)({tool:``,risk:``,device:``,status:``,q:``}),j=(0,l.useRef)(y);j.current=y;let M=(0,l.useRef)(null);Mc(M);let[N,P]=(0,l.useState)(`overview`),[F,I]=(0,l.useState)(``),[L,ee]=(0,l.useState)(`all`),te=(0,l.useMemo)(()=>{let e=i?.devices??[];return{online:e.filter(e=>e.status.reachable===!0).length,offline:e.filter(e=>e.status.reachable===!1).length,total:e.length}},[i]),ne=(0,l.useMemo)(()=>{let e=i?.devices??[],t=F.trim().toLowerCase();return e.filter(e=>!(L===`online`&&e.status.reachable!==!0||L===`offline`&&e.status.reachable!==!1||t&&!e.name.toLowerCase().includes(t)&&!(e.address??e.host??``).toLowerCase().includes(t)))},[i,F,L]);jc((0,l.useCallback)(e=>{if(j.current)return;g(t=>[e,...t].slice(0,lc));let t=e.device;t&&O(e=>({...e,[t]:(e[t]??0)+1}))},[]),(0,l.useCallback)(e=>S(e),[])),(0,l.useEffect)(()=>{ac(`/api/events?limit=200`).then(e=>g(e.events)).catch(()=>{})},[]);let R=(0,l.useCallback)(()=>{ac(`/api/stats?window=${_}&buckets=60`).then(t).catch(()=>{})},[_]);(0,l.useEffect)(()=>{R();let e=()=>{R(),ac(`/api/meta`).then(r).catch(()=>{}),ac(`/api/devices`).then(a).catch(()=>{}),ac(`/api/topology`).then(s).catch(()=>{})};e();let t=setInterval(()=>{j.current||e()},4e3);return()=>clearInterval(t)},[R]),(0,l.useEffect)(()=>{let e=()=>void ac(`/api/config`).then(u).catch(()=>{});e();let t=setInterval(e,3e4);return()=>clearInterval(t)},[]),(0,l.useEffect)(()=>{let e=e=>{e.key===`Escape`&&w(null)};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]);let z=(0,l.useMemo)(()=>{let e=k.q.trim().toLowerCase();return h.filter(t=>!(k.tool&&t.tool!==k.tool||k.risk&&t.risk!==k.risk||k.device&&t.device!==k.device||k.status===`ok`&&t.isError||k.status===`error`&&!t.isError||e&&!t.tool.toLowerCase().includes(e)&&!t.input.toLowerCase().includes(e)&&!t.output.toLowerCase().includes(e)&&!(t.error??``).toLowerCase().includes(e))).sort((e,t)=>t.ts-e.ts)},[h,k]),B=!!(k.tool||k.risk||k.device||k.status||k.q),re=(0,l.useMemo)(()=>h.filter(e=>e.isError),[h]),V=(0,l.useMemo)(()=>({ok:h.length-re.length,error:re.length}),[h,re]),ie=(0,l.useCallback)(async e=>{try{w(await ac(`/api/event/${encodeURIComponent(e.id)}`))}catch{w(e)}},[]),ae=(0,l.useMemo)(()=>z.slice(0,200),[z]),oe=(0,l.useMemo)(()=>ae.map(e=>e.id),[ae]),se=oe.length>0&&oe.every(e=>T.has(e)),H=!se&&oe.some(e=>T.has(e)),ce=(0,l.useCallback)(e=>{E(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),le=(0,l.useCallback)(()=>{E(e=>{let t=new Set(e);if(oe.length>0&&oe.every(e=>t.has(e)))for(let e of oe)t.delete(e);else for(let e of oe)t.add(e);return t})},[oe]),[ue,de]=(0,l.useState)(!1),fe=(0,l.useCallback)(async()=>{let e=[...T];if(e.length!==0)try{await oc({ids:e});let t=new Set(e);g(e=>e.filter(e=>!t.has(e.id))),E(new Set)}catch{}finally{de(!1)}},[T]),pe=e=>{if(e===`json`){qc(`mcp-events.json`,JSON.stringify(z,null,2),`application/json`);return}let t=[`ts`,`tool`,`risk`,`device`,`durationMs`,`isError`,`error`],n=e=>/[",\n]/.test(e)?`"${e.replace(/"/g,`""`)}"`:e,r=z.map(e=>[new Date(e.ts).toISOString(),e.tool,e.risk,e.device??``,String(e.durationMs),String(e.isError),e.error??``].map(n).join(`,`)).join(`
24
- `);qc(`mcp-events.csv`,`${t.join(`,`)}\n${r}\n`,`text/csv`)},me=e?e.errorRate>=.2?`is-bad`:e.errorRate>=.05?`is-warn`:`is-good`:``,he=c?.mcp??{},ge=c?.dashboard??{},U=(e,t,n)=>(0,$.jsxs)(`select`,{className:`btn`,value:k[e],onChange:t=>A(n=>({...n,[e]:t.target.value})),children:[(0,$.jsx)(`option`,{value:``,children:t}),n.map(e=>(0,$.jsx)(`option`,{value:e,children:e},e))]}),_e=Jc.find(e=>e.id===N)??Jc[0];return(0,$.jsxs)(`div`,{className:`shell`,ref:M,children:[(0,$.jsxs)(`aside`,{className:`nav`,children:[(0,$.jsxs)(`div`,{className:`nav__brand`,children:[(0,$.jsx)(`div`,{className:`nav__mark`,children:(0,$.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,"aria-hidden":`true`,children:(0,$.jsxs)(`g`,{stroke:`#18181b`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,$.jsx)(`path`,{d:`M12 12 L4 5 M12 12 L20 5 M12 12 L12 20`}),(0,$.jsx)(`circle`,{cx:`12`,cy:`12`,r:`3`,fill:`#18181b`,stroke:`none`}),(0,$.jsx)(`circle`,{cx:`4`,cy:`5`,r:`1.9`,fill:`#18181b`,stroke:`none`}),(0,$.jsx)(`circle`,{cx:`20`,cy:`5`,r:`1.9`,fill:`#18181b`,stroke:`none`}),(0,$.jsx)(`circle`,{cx:`12`,cy:`20`,r:`1.9`,fill:`#18181b`,stroke:`none`})]})})}),(0,$.jsxs)(`div`,{className:`nav__brandtext`,children:[(0,$.jsx)(`b`,{children:`MikroTik MCP`}),(0,$.jsx)(`small`,{children:`Observability`})]})]}),(0,$.jsx)(`nav`,{className:`nav__items`,children:Jc.map(e=>(0,$.jsxs)(`button`,{className:`nav__item${N===e.id?` is-active`:``}`,onClick:()=>P(e.id),children:[(0,$.jsx)(Yc,{name:e.id}),(0,$.jsx)(`span`,{children:e.label}),e.id===`feed`&&h.length>0&&(0,$.jsx)(`span`,{className:`nav__badge`,children:h.length>999?`999+`:h.length})]},e.id))}),(0,$.jsxs)(`div`,{className:`nav__foot`,children:[(0,$.jsxs)(`span`,{className:`hero__live${x===`off`?``:` is-on`}`,title:`Live transport: WebSocket (preferred) or SSE fallback`,children:[(0,$.jsx)(`span`,{className:`dot`}),x===`off`?`offline`:`live · ${x}`]}),(0,$.jsx)(`small`,{className:`muted`,children:n?`${ec(n.total)} events · ${n.transport}`:`connecting…`})]})]}),(0,$.jsxs)(`main`,{className:`main`,children:[(0,$.jsxs)(`header`,{className:`topline reveal`,children:[(0,$.jsxs)(`div`,{className:`topline__txt`,children:[(0,$.jsx)(`h1`,{children:_e.label}),(0,$.jsx)(`small`,{children:_e.sub})]}),(0,$.jsx)(`span`,{className:`topline__spacer`}),N===`overview`&&(0,$.jsx)(`select`,{className:`btn`,value:_,onChange:e=>v(Number(e.target.value)),title:`Stats time window`,children:cc.map(([e,t])=>(0,$.jsxs)(`option`,{value:t,children:[`window: `,e]},t))})]}),N===`overview`&&(0,$.jsxs)(`section`,{className:`view`,children:[(0,$.jsx)(`div`,{className:`cards reveal`,children:e?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(dc,{k:`Calls (window)`,v:ec(e.total)}),(0,$.jsx)(dc,{k:`Calls / min`,v:e.callsPerMin.toFixed(1)}),(0,$.jsx)(dc,{k:`Error rate`,v:`${(e.errorRate*100).toFixed(1)}%`,sub:`${e.errors} err`,cls:me}),(0,$.jsx)(dc,{k:`Avg latency`,v:Zs(e.latency.avg)}),(0,$.jsx)(dc,{k:`p95 latency`,v:Zs(e.latency.p95)}),(0,$.jsx)(dc,{k:`p99 latency`,v:Zs(e.latency.p99)}),(0,$.jsx)(dc,{k:`Distinct tools`,v:ec(e.distinctTools)}),(0,$.jsx)(dc,{k:`Output volume`,v:Qs(e.outputBytes)})]}):(0,$.jsxs)(`div`,{className:`stat`,children:[(0,$.jsx)(`p`,{className:`k`,children:`Loading…`}),(0,$.jsx)(`div`,{className:`v`,children:`—`})]})}),(0,$.jsxs)(`div`,{className:`bento reveal`,children:[(0,$.jsxs)(uc,{title:`Calls over time`,className:`b-series`,children:[e?(0,$.jsx)(fc,{series:e.series}):(0,$.jsx)(`div`,{className:`muted`,children:`no data`}),(0,$.jsxs)(`div`,{className:`legend`,children:[(0,$.jsxs)(`span`,{children:[(0,$.jsx)(`i`,{style:{background:`#d4d4d8`}}),`ok`]}),(0,$.jsxs)(`span`,{children:[(0,$.jsx)(`i`,{style:{background:`#f87171`}}),`error`]})]})]}),e&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(uc,{title:`By risk`,className:`b-risk`,children:(0,$.jsx)(pc,{segments:Object.keys(e.byRisk).map(t=>({label:t,value:e.byRisk[t],color:nc[t]}))})}),(0,$.jsx)(uc,{title:`Top tools`,className:`b-tools`,children:(0,$.jsx)(mc,{rows:e.byTool.map(e=>({label:e.tool,value:e.count,sub:`${e.count}× · ${Zs(e.p95Ms)} p95${e.errors?` · ${e.errors} err`:``}`,color:e.errors?`#f87171`:void 0}))})}),(0,$.jsx)(uc,{title:`Status`,className:`b-status`,children:(0,$.jsx)(pc,{segments:[{label:`ok`,value:V.ok,color:`#d4d4d8`},{label:`error`,value:V.error,color:`#f87171`}]})}),(0,$.jsx)(uc,{title:`By device`,className:`b-device`,children:e.byDevice.length?(0,$.jsx)(mc,{rows:e.byDevice.map(e=>({label:e.device,value:e.count}))}):(0,$.jsx)(`div`,{className:`muted`,children:`single device`})}),(0,$.jsx)(uc,{title:`Recent errors`,className:`b-errors`,children:re.length?(0,$.jsx)(`div`,{className:`hbar`,children:re.slice(0,8).map(e=>(0,$.jsxs)(`div`,{className:`hbar__row conn-errrow`,style:{gridTemplateColumns:`auto 1fr`},onClick:()=>void ie(e),children:[(0,$.jsx)(`span`,{className:`muted`,children:$s(e.ts)}),(0,$.jsxs)(`span`,{style:{color:`var(--mt-bad)`,whiteSpace:`nowrap`,overflow:`hidden`,textOverflow:`ellipsis`,minWidth:0},title:e.error??e.output,children:[e.tool,`: `,e.error??e.output??`error`]})]},e.id))}):(0,$.jsx)(`div`,{className:`muted`,children:`no errors 🎉`})})]})]})]}),N===`devices`&&(i&&i.devices.length>0?(0,$.jsxs)(`section`,{className:`view`,children:[(0,$.jsxs)(`div`,{className:`dev-toolbar reveal`,children:[(0,$.jsx)(`input`,{className:`search`,type:`search`,placeholder:`Search devices by name or address…`,value:F,onChange:e=>I(e.target.value),style:{flex:1,minWidth:180}}),(0,$.jsx)(`div`,{className:`dev-filters`,children:[`all`,`online`,`offline`].map(e=>(0,$.jsx)(`button`,{className:`dev-fbtn${L===e?` is-active`:``}`,onClick:()=>ee(e),children:e===`all`?`All ${te.total}`:e===`online`?`Online ${te.online}`:`Offline ${te.offline}`},e))}),(0,$.jsxs)(`span`,{className:`muted`,children:[ne.length,`/`,te.total,` shown`]})]}),(0,$.jsxs)(`details`,{className:`dev-collapse reveal`,open:te.total<=8,children:[(0,$.jsxs)(`summary`,{children:[`Connectivity radar`,(0,$.jsxs)(`span`,{className:`muted`,children:[` `,`· `,te.online,` online · `,te.offline,` offline ·`,` `,te.total,` total`]})]}),(0,$.jsx)(Tc,{payload:i,pulses:D})]}),ne.length===0?(0,$.jsxs)(`div`,{className:`feed-empty reveal`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`🔍`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`No devices match`}),(0,$.jsx)(`p`,{className:`feed-empty__sub`,children:`Try a different search or status filter.`})]}):(0,$.jsx)(`div`,{className:`dev-grid-wide reveal`,children:ne.map(e=>(0,$.jsx)(Ec,{d:e},e.name))}),ne.length>0&&(0,$.jsx)(uc,{title:`Device system health`,className:`reveal`,extra:(0,$.jsx)(`span`,{className:`muted`,children:`CPU · memory · disk · latency · live probe`}),children:(0,$.jsx)(`div`,{className:`health-grid`,children:ne.map(e=>(0,$.jsx)(Ac,{d:e},e.name))})})]}):(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`🖧`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`No devices configured`}),(0,$.jsx)(`p`,{className:`feed-empty__sub`,children:`Add a device to your config to see connectivity and system health here.`})]})),N===`topology`&&(o&&o.nodes.length>0?(0,$.jsx)(`section`,{className:`view`,children:(0,$.jsx)(uc,{title:`Network topology`,className:`reveal`,extra:(0,$.jsx)(`span`,{className:`muted`,children:`Layer-2 neighbours via MNDP/CDP/LLDP · click a neighbour to onboard it`}),children:(0,$.jsx)(Rc,{topo:o,onOnboard:(e,t)=>{m({name:e,body:t}),f(!0),P(`config`)}})})}):(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`🛰️`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`No neighbours discovered yet`}),(0,$.jsx)(`p`,{className:`feed-empty__sub`,children:`Layer-2 neighbours (MNDP / CDP / LLDP) appear here as the device reports them.`})]})),N===`packets`&&(0,$.jsx)(`section`,{className:`view`,children:(0,$.jsx)(uc,{title:`Packet capture`,className:`reveal`,extra:(0,$.jsx)(`span`,{className:`muted`,children:`live TZSP decode · /tool sniffer streaming`}),children:(0,$.jsx)(Wc,{})})}),N===`config`&&(c?(0,$.jsx)(`section`,{className:`view`,children:(0,$.jsx)(uc,{title:`Configuration`,className:`reveal`,extra:(0,$.jsx)(`button`,{className:`btn`,onClick:()=>f(e=>!e),title:`Edit the config JSON with autocomplete, validation and safe-apply`,children:d?`View`:`✎ Edit config`}),children:d?(0,$.jsx)(Ic,{initial:p?{...c,devices:{...c.devices,[p.name]:p.body}}:c,onClose:()=>{f(!1),m(null)},onReload:()=>{m(null),ac(`/api/config`).then(u).catch(()=>{})}},p?`seed-${p.name}`:`config`):(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`div`,{className:`legend`,style:{margin:`0 0 10px`},children:[(0,$.jsxs)(`span`,{children:[`transport: `,tc(he.transport)]}),(0,$.jsxs)(`span`,{children:[`read-only: `,c.readOnly?`yes`:`no`]}),(0,$.jsxs)(`span`,{children:[`dashboard: `,tc(ge.host),`:`,tc(ge.port)]}),(0,$.jsxs)(`span`,{children:[`capture: `,ge.captureBody?`on`:`off`]}),(0,$.jsxs)(`span`,{children:[`s3: `,c.s3?`configured`:`off`]})]}),(0,$.jsxs)(`details`,{className:`cfg`,children:[(0,$.jsx)(`summary`,{children:`Full effective configuration (secrets redacted)`}),(0,$.jsx)(Gc,{value:c,maxHeight:340})]})]})})}):(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`⚙️`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`Loading configuration…`})]})),N===`feed`&&(0,$.jsxs)(`div`,{className:`panel reveal`,children:[(0,$.jsxs)(`div`,{className:`sheet__hd`,style:{marginBottom:12},children:[(0,$.jsx)(`h2`,{style:{margin:0},children:`Live tool calls`}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsxs)(`span`,{className:`muted`,children:[z.length,` shown · `,h.length,` buffered`]})]}),(0,$.jsxs)(`div`,{className:`toolbar`,style:{marginBottom:12},children:[(0,$.jsx)(`div`,{className:`grow`,style:{flex:1,minWidth:180},children:(0,$.jsx)(`input`,{className:`search`,type:`search`,placeholder:`Search tool / input / output / error…`,value:k.q,onChange:e=>A(t=>({...t,q:e.target.value}))})}),U(`tool`,`all tools`,n?.tools??[]),U(`risk`,`all risk`,[`READ`,`WRITE`,`WRITE_IDEMPOTENT`,`DESTRUCTIVE`,`DANGEROUS`]),U(`device`,`all devices`,n?.devices??[]),U(`status`,`all status`,[`ok`,`error`]),(0,$.jsx)(`button`,{className:`btn${y?` is-active`:``}`,onClick:()=>b(e=>!e),children:y?`▶ Resume`:`⏸ Pause`}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>pe(`csv`),children:`CSV`}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>pe(`json`),children:`JSON`}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>A({tool:``,risk:``,device:``,status:``,q:``}),children:`Clear`}),ue&&T.size>0?(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`button`,{className:`btn btn-danger`,onClick:()=>void fe(),children:[`✓ Confirm delete (`,T.size,`)`]}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>de(!1),children:`Cancel`})]}):(0,$.jsxs)(`button`,{className:`btn`,disabled:T.size===0,onClick:()=>de(!0),title:`Delete the selected rows`,children:[`🗑 Delete`,T.size>0?` (${T.size})`:``]})]}),z.length===0?B?(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`🔍`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`No calls match your filters`}),(0,$.jsxs)(`p`,{className:`feed-empty__sub`,children:[h.length,` call`,h.length===1?``:`s`,` buffered — try widening the search or the risk / device / status filters.`]}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>A({tool:``,risk:``,device:``,status:``,q:``}),children:`Clear filters`})]}):(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__pulse${x===`off`?``:` is-on`}`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:x===`off`?`Not connected`:`Listening for tool calls…`}),(0,$.jsx)(`p`,{className:`feed-empty__sub`,children:x===`off`?`The live stream is offline — it will reconnect automatically.`:`Tool calls the LLM makes against this server stream in here in real time.`})]}):(0,$.jsx)(`div`,{className:`feedwrap`,children:(0,$.jsxs)(`table`,{className:`feed`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`th`,{style:{width:28},children:(0,$.jsx)(`input`,{type:`checkbox`,"aria-label":`Select all shown rows`,checked:se,ref:e=>{e&&(e.indeterminate=H)},onChange:le})}),(0,$.jsx)(`th`,{children:`time`}),(0,$.jsx)(`th`,{children:`tool`}),(0,$.jsx)(`th`,{children:`risk`}),(0,$.jsx)(`th`,{children:`device`}),(0,$.jsx)(`th`,{className:`num`,children:`dur`}),(0,$.jsx)(`th`,{children:`status`}),(0,$.jsx)(`th`,{children:`output`})]})}),(0,$.jsx)(`tbody`,{children:ae.map(e=>(0,$.jsxs)(`tr`,{className:`${e.isError?`is-err`:``}${T.has(e.id)?` is-selected`:``}`.trim()||void 0,onClick:()=>void ie(e),children:[(0,$.jsx)(`td`,{onClick:e=>e.stopPropagation(),children:(0,$.jsx)(`input`,{type:`checkbox`,"aria-label":`Select row`,checked:T.has(e.id),onChange:()=>ce(e.id)})}),(0,$.jsx)(`td`,{children:$s(e.ts)}),(0,$.jsx)(`td`,{children:e.tool}),(0,$.jsx)(`td`,{children:(0,$.jsx)(`span`,{className:`risk risk-${e.risk}`,children:e.risk.replace(`WRITE_IDEMPOTENT`,`WRITE·I`)})}),(0,$.jsx)(`td`,{children:e.device??`—`}),(0,$.jsx)(`td`,{className:`num`,children:Zs(e.durationMs)}),(0,$.jsx)(`td`,{children:(0,$.jsx)(`span`,{className:e.isError?`status-err`:`status-ok`,children:e.isError?`error`:`ok`})}),(0,$.jsx)(`td`,{className:`preview`,children:e.isError?e.error??`error`:e.output||`—`})]},e.id))})]})})]})]}),C&&(0,$.jsx)(Kc,{event:C,onClose:()=>w(null)})]})}var Zc=document.getElementById(`root`);Zc&&(0,u.createRoot)(Zc).render((0,$.jsx)(Xc,{}));
23
+ `).map((e,t)=>(0,$.jsx)(`div`,{className:e.startsWith(`+`)?`d-add`:e.startsWith(`-`)?`d-del`:e.startsWith(`@@`)?`d-hunk`:``,children:e||` `},t))})]})]})}function Lc(e){return e==null?`#3f3f46`:e>=85?`#f87171`:e>=60?`#a1a1aa`:`#d4d4d8`}function Rc({topo:e,onOnboard:t}){let[n,r]=(0,l.useState)(null),i=e.nodes.filter(e=>e.kind===`device`),a=e.nodes.filter(e=>e.kind===`neighbor`),o=Math.max(1,i.length),s=i.length<=1?0:Math.min(150,76+o*8),c=s+(i.length<=1?180:140),u=Math.max(360,Math.round((c+78)*2)),d=u/2,f=new Map,p=new Map;i.forEach((e,t)=>{if(i.length===1){f.set(e.id,{x:380,y:d}),p.set(e.id,-Math.PI/2);return}let n=t/o*Math.PI*2-Math.PI/2;p.set(e.id,n),f.set(e.id,{x:380+s*Math.cos(n),y:d+s*Math.sin(n)})});let m=new Map,h=new Set(a.map(e=>e.id));for(let t of e.edges)h.has(t.to)&&!m.has(t.to)&&m.set(t.to,t.from);let g=new Map;for(let e of a){let t=m.get(e.id)??i[0]?.id??``;g.set(t,[...g.get(t)??[],e.id])}if(i.length<=1)a.forEach((e,t)=>{let n=t/Math.max(1,a.length)*Math.PI*2-Math.PI/2;f.set(e.id,{x:380+c*Math.cos(n),y:d+c*Math.sin(n)})});else for(let[e,t]of g){let n=p.get(e)??-Math.PI/2,r=Math.min(Math.PI/2.2,.32*t.length);t.forEach((e,i)=>{let a=t.length===1?n:n-r/2+r*i/(t.length-1);f.set(e,{x:380+c*Math.cos(a),y:d+c*Math.sin(a)})})}let _=new Map(e.nodes.map(e=>[e.id,e])),v=n?_.get(n):null;return(0,$.jsxs)(`div`,{className:`topo`,children:[(0,$.jsxs)(`svg`,{viewBox:`0 0 760 ${u}`,width:`100%`,height:Math.min(u,540),preserveAspectRatio:`xMidYMid meet`,children:[e.edges.map((e,t)=>{let n=f.get(e.from),r=f.get(e.to);return!n||!r?null:(0,$.jsx)(`line`,{className:`topo-edge${h.has(e.to)?` is-dashed`:``}`,x1:n.x,y1:n.y,x2:r.x,y2:r.y,children:(0,$.jsxs)(`title`,{children:[e.from,` → `,e.to,e.interface?` (${e.interface})`:``]})},`e-${t}`)}),e.nodes.map(e=>{let t=f.get(e.id);if(!t)return null;let i=e.kind===`device`,a=i?132:108,o=i?50:38,s=t.x-a/2,c=t.y-o/2,l=e.reachable===!0?`#d4d4d8`:e.reachable===!1?`#f87171`:i?`#71717a`:`#e4e4e7`,u=n===e.id;return(0,$.jsxs)(`g`,{className:`topo-node${i?` is-device`:` is-neighbor`}${e.onboardable?` is-onboard`:``}${u?` is-picked`:``}`,transform:`translate(${s},${c})`,onClick:()=>r(t=>t===e.id?null:e.id),children:[(0,$.jsx)(`rect`,{width:a,height:o,rx:9,style:{stroke:l}}),(0,$.jsx)(`text`,{className:`topo-label`,x:9,y:16,children:(e.label||e.id).slice(0,16)}),i?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`text`,{className:`topo-sub`,x:9,y:30,children:(e.board||e.ip||``).slice(0,18)}),(0,$.jsx)(`rect`,{className:`topo-bar-bg`,x:9,y:37,width:a-18,height:4,rx:2}),(0,$.jsx)(`rect`,{x:9,y:37,width:(a-18)*Math.min(100,e.cpuLoad??0)/100,height:4,rx:2,style:{fill:Lc(e.cpuLoad)}}),(0,$.jsx)(`rect`,{className:`topo-bar-bg`,x:9,y:43,width:a-18,height:4,rx:2}),(0,$.jsx)(`rect`,{x:9,y:43,width:(a-18)*Math.min(100,e.memUsedPct??0)/100,height:4,rx:2,style:{fill:Lc(e.memUsedPct)}})]}):(0,$.jsx)(`text`,{className:`topo-sub`,x:9,y:29,children:e.onboardable?`+ onboard`:e.ip||e.mac||``})]},e.id)})]}),(0,$.jsxs)(`div`,{className:`topo-foot`,children:[(0,$.jsxs)(`span`,{className:`legend`,children:[(0,$.jsxs)(`span`,{children:[(0,$.jsx)(`i`,{className:`dot`,style:{background:`#d4d4d8`}}),` online`]}),(0,$.jsxs)(`span`,{children:[(0,$.jsx)(`i`,{className:`dot`,style:{background:`#f87171`}}),` offline`]}),(0,$.jsxs)(`span`,{children:[(0,$.jsx)(`i`,{className:`dot`,style:{background:`#e4e4e7`}}),` neighbour`]}),(0,$.jsxs)(`span`,{className:`muted`,children:[e.stats.devices,` devices · `,e.stats.neighbors,` discovered ·`,` `,e.stats.onboardable,` onboardable`]})]}),v&&(0,$.jsxs)(`div`,{className:`topo-pop`,children:[(0,$.jsxs)(`div`,{className:`topo-pop__hd`,children:[(0,$.jsx)(`strong`,{children:v.label}),(0,$.jsx)(`span`,{className:`muted`,children:[v.board,v.version,v.mac].filter(Boolean).join(` · `)||`no details advertised`}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`topo-btn`,onClick:()=>r(null),children:`✕`})]}),v.suggestedConfig?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`div`,{className:`muted`,style:{margin:`2px 0 6px`},children:`Not managed yet — add this to your device config to onboard it:`}),(0,$.jsx)(`pre`,{className:`topo-stub`,children:JSON.stringify({[v.suggestedConfig.name]:zc(v.suggestedConfig)},null,2)}),(0,$.jsx)(`button`,{className:`topo-btn`,onClick:()=>void navigator.clipboard?.writeText(JSON.stringify({[v.suggestedConfig.name]:zc(v.suggestedConfig)},null,2)),children:`Copy config stub`}),t&&(0,$.jsx)(`button`,{className:`topo-btn cfg-save`,onClick:()=>t(v.suggestedConfig.name,zc(v.suggestedConfig)),children:`Add to config →`})]}):(0,$.jsxs)(`div`,{className:`muted`,children:[`Managed device`,v.ip?` · ${v.ip}`:``,v.uptime?` · up ${v.uptime}`:``]})]})]})]})}function zc(e){let t={port:e.port,username:e.username};return e.host&&(t.host=e.host),e.mac&&(t.mac=e.mac),t}var Bc=/("(?:\\.|[^"\\])*"(?:\s*:)?)|\b(true|false|null)\b|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g;function Vc(e){let t=[],n=0,r=0;for(let i of e.matchAll(Bc)){let a=i.index??0;a>n&&t.push(e.slice(n,a));let o=i[0],s;s=i[1]===void 0?i[2]===void 0?`j-num`:o===`null`?`j-null`:`j-bool`:/:\s*$/.test(o)?`j-key`:`j-str`,t.push((0,$.jsx)(`span`,{className:s,children:o},r++)),n=a+o.length}return n<e.length&&t.push(e.slice(n)),t}var Hc={TCP:`#e4e4e7`,UDP:`#d4d4d8`,ICMP:`#a1a1aa`,ICMPv6:`#a1a1aa`,ARP:`#e4e4e7`,IPv6:`#a1a1aa`},Uc=e=>e&&Hc[e]||`#71717a`;function Wc(){let[e,t]=(0,l.useState)(null),[n,r]=(0,l.useState)(!1);(0,l.useEffect)(()=>{let e=()=>void ac(`/api/capture/packets?limit=150`).then(t).catch(()=>{});e();let n=setInterval(e,1500);return()=>clearInterval(n)},[]);let i=e?.stats,a=e?.packets??[],o=async()=>{r(!0),await sc(`/api/capture/stop`,{}).catch(()=>{}),r(!1)};if(!i||!i.running&&i.packets===0)return(0,$.jsxs)(`div`,{className:`cap-idle muted`,children:[`No capture running. Start one with the `,(0,$.jsx)(`code`,{children:`start_packet_capture`}),` tool — point the device's TZSP stream at this host — and decoded packets stream in here live.`]});let s=Math.max(1,...Object.values(i.protocols));return(0,$.jsxs)(`div`,{className:`cap`,children:[(0,$.jsxs)(`div`,{className:`cap-bar`,children:[(0,$.jsx)(`span`,{className:`cap-dot${i.running?` is-on`:``}`}),(0,$.jsx)(`b`,{children:i.running?`capturing`:`stopped`}),(0,$.jsxs)(`span`,{className:`muted`,children:[`UDP `,i.port]}),(0,$.jsxs)(`span`,{className:`muted`,children:[ec(i.packets),` pkts · `,Qs(i.bytes)]}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`a`,{className:`btn`,href:ic(`/api/capture/pcap`),download:`capture.pcap`,children:`⤓ pcap`}),(0,$.jsx)(`button`,{className:`btn btn-danger`,onClick:()=>void o(),disabled:n||!i.running,children:`■ Stop`})]}),(0,$.jsxs)(`div`,{className:`cap-cols`,children:[(0,$.jsxs)(`div`,{className:`cap-side`,children:[(0,$.jsx)(`div`,{className:`cap-h`,children:`Protocols`}),Object.entries(i.protocols).map(([e,t])=>(0,$.jsxs)(`div`,{className:`cap-pbar`,children:[(0,$.jsx)(`span`,{className:`cap-plabel`,style:{color:Uc(e)},children:e}),(0,$.jsx)(`span`,{className:`cap-ptrack`,children:(0,$.jsx)(`i`,{style:{width:`${t/s*100}%`,background:Uc(e)}})}),(0,$.jsx)(`span`,{className:`cap-pn`,children:t})]},e)),(0,$.jsx)(`div`,{className:`cap-h`,style:{marginTop:12},children:`Top talkers`}),i.topTalkers.length===0&&(0,$.jsx)(`div`,{className:`muted`,children:`—`}),i.topTalkers.map(e=>(0,$.jsxs)(`div`,{className:`cap-talker`,children:[(0,$.jsx)(`span`,{children:e.addr}),(0,$.jsx)(`b`,{children:e.count})]},e.addr))]}),(0,$.jsx)(`div`,{className:`cap-list`,children:a.length===0?(0,$.jsx)(`div`,{className:`muted`,style:{padding:10},children:`waiting for packets…`}):a.map((e,t)=>(0,$.jsxs)(`div`,{className:`cap-row`,children:[(0,$.jsx)(`span`,{className:`cap-tt`,children:$s(e.ts)}),(0,$.jsx)(`span`,{className:`cap-proto`,style:{color:Uc(e.protocol)},children:e.protocol??e.ethType}),(0,$.jsx)(`span`,{className:`cap-len`,children:e.len}),(0,$.jsx)(`span`,{className:`cap-info`,children:e.info})]},t))})]})]})}function Gc({value:e,maxHeight:t}){let n=typeof e==`string`?e:JSON.stringify(e,null,2);return(0,$.jsx)(`pre`,{className:`body json`,style:t?{maxHeight:t}:void 0,children:Vc(n)})}function Kc(){let[e,t]=(0,l.useState)(null),[n,r]=(0,l.useState)(``),[i,a]=(0,l.useState)(``),[o,s]=(0,l.useState)(null),[c,u]=(0,l.useState)(null),d=(0,l.useCallback)(()=>{ac(`/api/snapshots`).then(e=>t(e.snapshots)).catch(()=>t([]))},[]);(0,l.useEffect)(()=>d(),[d]);let f=e=>{ac(`/api/snapshot/${encodeURIComponent(e)}`).then(s).catch(()=>{})},p=()=>{!n||!i||sc(`/api/snapshots/diff`,{from:n,to:i}).then(u).catch(()=>{})};if(!e)return(0,$.jsx)(`div`,{className:`muted`,children:`loading snapshots…`});if(e.length===0)return(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`🕰️`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`No config snapshots yet`}),(0,$.jsxs)(`p`,{className:`feed-empty__sub`,children:[`Capture one with the `,(0,$.jsx)(`code`,{children:`capture_config_snapshot`}),` tool — then time-travel diff any two here.`]})]});let m=e.map(e=>(0,$.jsxs)(`option`,{value:e.id,children:[e.device,` · `,e.label??e.id,` · `,$s(e.ts)]},e.id));return(0,$.jsxs)(`section`,{className:`view`,children:[(0,$.jsxs)(uc,{title:`Config snapshots`,className:`reveal`,extra:(0,$.jsx)(`button`,{className:`btn`,onClick:d,children:`↻ Refresh`}),children:[(0,$.jsxs)(`div`,{className:`toolbar`,style:{marginBottom:12},children:[(0,$.jsxs)(`select`,{className:`btn`,value:n,onChange:e=>r(e.target.value),children:[(0,$.jsx)(`option`,{value:``,children:`diff from…`}),m]}),(0,$.jsxs)(`select`,{className:`btn`,value:i,onChange:e=>a(e.target.value),children:[(0,$.jsx)(`option`,{value:``,children:`to…`}),m]}),(0,$.jsx)(`button`,{className:`btn is-active`,onClick:p,disabled:!n||!i,children:`Diff →`}),(0,$.jsxs)(`span`,{className:`muted`,children:[e.length,` snapshots`]})]}),(0,$.jsx)(`div`,{className:`feedwrap`,children:(0,$.jsxs)(`table`,{className:`feed`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`th`,{children:`captured`}),(0,$.jsx)(`th`,{children:`device`}),(0,$.jsx)(`th`,{children:`label`}),(0,$.jsx)(`th`,{children:`version`}),(0,$.jsx)(`th`,{className:`num`,children:`lines`}),(0,$.jsx)(`th`,{className:`num`,children:`size`}),(0,$.jsx)(`th`,{children:`output`})]})}),(0,$.jsx)(`tbody`,{children:e.map(e=>(0,$.jsxs)(`tr`,{className:o?.id===e.id?`is-selected`:void 0,onClick:()=>f(e.id),children:[(0,$.jsx)(`td`,{children:$s(e.ts)}),(0,$.jsx)(`td`,{children:e.device}),(0,$.jsx)(`td`,{children:e.label??`—`}),(0,$.jsx)(`td`,{children:e.rosVersion??`—`}),(0,$.jsx)(`td`,{className:`num`,children:e.lines}),(0,$.jsx)(`td`,{className:`num`,children:Qs(e.bytes)}),(0,$.jsx)(`td`,{className:`preview`,children:`view export →`})]},e.id))})]})})]}),c&&(0,$.jsx)(uc,{title:`Time-travel diff`,className:`reveal`,extra:(0,$.jsx)(`span`,{className:`muted`,children:c.summary.changed?`+${c.summary.added} / -${c.summary.removed}`:`identical`}),children:(0,$.jsx)(`pre`,{className:`cfg-diff`,children:(c.unified||`(identical)`).split(`
24
+ `).map((e,t)=>(0,$.jsx)(`div`,{className:e.startsWith(`+`)?`d-add`:e.startsWith(`-`)?`d-del`:e.startsWith(`@@`)?`d-hunk`:``,children:e||` `},t))})}),o&&(0,$.jsx)(uc,{title:`Snapshot · ${o.label??o.id}`,className:`reveal`,extra:(0,$.jsx)(`button`,{className:`btn`,onClick:()=>s(null),children:`✕ Close`}),children:(0,$.jsx)(`pre`,{className:`body`,style:{maxHeight:460},children:o.body||`(empty)`})})]})}var qc={add:`+`,set:`~`,remove:`−`,enable:`▲`,disable:`▽`,move:`⇅`};function Jc(){let[e,t]=(0,l.useState)(``),[n,r]=(0,l.useState)(null),[i,a]=(0,l.useState)(null),[o,s]=(0,l.useState)(!1),c=async()=>{s(!0),a(null);let t=await sc(`/api/plan`,{script:e}).catch(()=>({error:`request failed`}));if(s(!1),t.error||!t.plan){a(t.error??`no plan produced`),r(null);return}r({plan:t.plan,text:t.text??``})},u=n?.plan.grade??``,d=u===`critical`||u===`high`;return(0,$.jsxs)(`section`,{className:`view`,children:[(0,$.jsxs)(uc,{title:`Change plan — dry-run`,className:`reveal`,extra:(0,$.jsx)(`span`,{className:`muted`,children:`terraform-style preview · pure analysis, never touches a device`}),children:[(0,$.jsx)(`textarea`,{className:`plan-input`,spellCheck:!1,placeholder:`Paste intended RouterOS commands, one per line, e.g.
25
+ /ip firewall filter add chain=input action=drop in-interface=WAN
26
+ /ip address add address=10.0.0.1/24 interface=ether1
27
+ /ip address remove [find address=192.168.88.1/24]`,value:e,onChange:e=>t(e.target.value)}),(0,$.jsxs)(`div`,{className:`toolbar`,style:{marginTop:10},children:[(0,$.jsx)(`button`,{className:`btn is-active`,onClick:()=>void c(),disabled:o||!e.trim(),children:o?`Planning…`:`▸ Plan`}),i&&(0,$.jsx)(`span`,{className:`cfg-err`,children:i})]})]}),n&&(0,$.jsxs)(uc,{title:`Plan`,className:`reveal`,extra:(0,$.jsxs)(`span`,{className:`cfg-status ${d?`is-bad`:`is-ok`}`,children:[`risk `,n.plan.riskScore,` · `,u]}),children:[(0,$.jsxs)(`div`,{className:`legend`,style:{marginTop:0},children:[(0,$.jsxs)(`span`,{children:[`+`,n.plan.counts.add,` to add`]}),(0,$.jsxs)(`span`,{children:[`~`,n.plan.counts.modify,` to modify`]}),(0,$.jsxs)(`span`,{children:[`-`,n.plan.counts.remove,` to remove`]}),n.plan.reordered&&(0,$.jsx)(`span`,{children:`· reordered into a safe sequence`})]}),n.plan.warnings.length>0&&(0,$.jsx)(`div`,{className:`cfg-errors`,style:{margin:`10px 0`},children:n.plan.warnings.map((e,t)=>(0,$.jsxs)(`div`,{className:`cfg-err`,style:{color:`var(--mt-warn)`},children:[`⚠ `,e]},t))}),(0,$.jsx)(`div`,{className:`plan-steps`,children:n.plan.steps.map(e=>(0,$.jsxs)(`div`,{className:`plan-step risk-${e.risk}`,children:[(0,$.jsx)(`span`,{className:`plan-op`,children:qc[e.op]??`•`}),(0,$.jsx)(`code`,{children:e.command}),e.lockoutRisk&&(0,$.jsx)(`span`,{className:`plan-lock`,children:`⚠ lock-out`}),(0,$.jsx)(`span`,{className:`plan-risk`,children:e.risk})]},e.index))})]})]})}function Yc(){let[e,t]=(0,l.useState)(null),[n,r]=(0,l.useState)(null),[i,a]=(0,l.useState)(null),[o,s]=(0,l.useState)(null),c=(0,l.useCallback)(()=>{ac(`/api/s3/list`).then(t).catch(()=>t({configured:!1,objects:[]}))},[]);(0,l.useEffect)(()=>c(),[c]);let u=e=>{ac(`/api/s3/presign?key=${encodeURIComponent(e)}`).then(e=>{e.url?window.open(e.url,`_blank`,`noopener`):s(`could not generate download link`)}).catch(()=>s(`could not generate download link`))},d=async e=>{a(e);let t=await sc(`/api/s3/delete`,{key:e}).catch(()=>({error:`request failed`}));a(null),r(null),t.ok?(s(`Deleted ${e}`),c()):s(t.error??`delete failed`)};return e?e.configured?(0,$.jsx)(`section`,{className:`view`,children:(0,$.jsxs)(uc,{title:`S3 backups`,className:`reveal`,extra:(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`span`,{className:`muted`,children:[e.target,` · `,e.objects.length,` object`,e.objects.length===1?``:`s`,e.truncated?` (truncated)`:``]}),(0,$.jsx)(`button`,{className:`btn`,onClick:c,style:{marginLeft:10},children:`↻ Refresh`})]}),children:[o&&(0,$.jsx)(`div`,{className:`cfg-msg`,children:o}),e.objects.length===0?(0,$.jsxs)(`div`,{className:`muted`,style:{padding:12},children:[`No objects in the bucket. Upload one with the `,(0,$.jsx)(`code`,{children:`upload_backup_to_s3`}),` tool.`]}):(0,$.jsx)(`div`,{className:`feedwrap`,children:(0,$.jsxs)(`table`,{className:`feed`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`th`,{children:`key`}),(0,$.jsx)(`th`,{className:`num`,children:`size`}),(0,$.jsx)(`th`,{children:`modified`}),(0,$.jsx)(`th`,{style:{width:200},children:`actions`})]})}),(0,$.jsx)(`tbody`,{children:e.objects.map(e=>(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`td`,{children:e.key}),(0,$.jsx)(`td`,{className:`num`,children:Qs(e.size)}),(0,$.jsx)(`td`,{children:e.lastModified?new Date(e.lastModified).toLocaleString():`—`}),(0,$.jsxs)(`td`,{onClick:e=>e.stopPropagation(),children:[(0,$.jsx)(`button`,{className:`btn`,onClick:()=>u(e.key),children:`⤓ Download`}),` `,n===e.key?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`button`,{className:`btn btn-danger`,disabled:i===e.key,onClick:()=>void d(e.key),children:`✓ Confirm`}),` `,(0,$.jsx)(`button`,{className:`btn`,onClick:()=>r(null),children:`Cancel`})]}):(0,$.jsx)(`button`,{className:`btn`,onClick:()=>r(e.key),children:`🗑 Delete`})]})]},e.key))})]})})]})}):(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`☁️`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`S3 is not configured`}),(0,$.jsxs)(`p`,{className:`feed-empty__sub`,children:[`Add an `,(0,$.jsx)(`code`,{children:`s3`}),` block (bucket + credentials) to your config to manage backup objects here.`]})]}):(0,$.jsx)(`div`,{className:`muted`,children:`loading S3 objects…`})}function Xc({event:e,onClose:t}){let n=e=>void navigator.clipboard?.writeText(e).catch(()=>{});return(0,$.jsx)(`div`,{className:`overlay`,onClick:e=>e.target===e.currentTarget&&t(),children:(0,$.jsxs)(`div`,{className:`sheet`,children:[(0,$.jsxs)(`div`,{className:`sheet__hd`,children:[(0,$.jsx)(`span`,{className:`risk risk-${e.risk}`,children:e.risk}),(0,$.jsx)(`h3`,{children:e.tool}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`btn`,onClick:t,children:`✕ Close`})]}),(0,$.jsxs)(`div`,{className:`kv__body`,children:[(0,$.jsx)(`div`,{className:`kv__k`,children:`title`}),(0,$.jsx)(`div`,{className:`kv__v`,children:e.title}),(0,$.jsx)(`div`,{className:`kv__k`,children:`time`}),(0,$.jsx)(`div`,{className:`kv__v`,children:new Date(e.ts).toLocaleString()}),(0,$.jsx)(`div`,{className:`kv__k`,children:`device`}),(0,$.jsx)(`div`,{className:`kv__v`,children:e.device??`—`}),(0,$.jsx)(`div`,{className:`kv__k`,children:`transport`}),(0,$.jsx)(`div`,{className:`kv__v`,children:e.transport??`—`}),(0,$.jsx)(`div`,{className:`kv__k`,children:`duration`}),(0,$.jsx)(`div`,{className:`kv__v`,children:Zs(e.durationMs)}),(0,$.jsx)(`div`,{className:`kv__k`,children:`status`}),(0,$.jsx)(`div`,{className:`kv__v`,children:(0,$.jsx)(`span`,{className:e.isError?`status-err`:`status-ok`,children:e.isError?`error`:`ok`})}),(0,$.jsx)(`div`,{className:`kv__k`,children:`output size`}),(0,$.jsxs)(`div`,{className:`kv__v`,children:[Qs(e.outputBytes),e.truncated?` (truncated)`:``]}),(0,$.jsx)(`div`,{className:`kv__k`,children:`structured`}),(0,$.jsx)(`div`,{className:`kv__v`,children:e.hasStructured?`yes (renders an MCP App view)`:`no`})]}),e.error&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`h2`,{className:`muted`,children:`ERROR`}),(0,$.jsx)(`pre`,{className:`body`,style:{color:`var(--mt-bad)`},children:e.error})]}),(0,$.jsxs)(`div`,{className:`sheet__hd`,children:[(0,$.jsx)(`h2`,{className:`muted`,style:{margin:0},children:`INPUT`}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>n(e.input),children:`Copy`})]}),e.input?(0,$.jsx)(Gc,{value:e.input}):(0,$.jsx)(`pre`,{className:`body`,children:`—`}),(0,$.jsxs)(`div`,{className:`sheet__hd`,children:[(0,$.jsx)(`h2`,{className:`muted`,style:{margin:0},children:`OUTPUT`}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>n(e.output),children:`Copy`})]}),(0,$.jsx)(`pre`,{className:`body`,children:e.output||`—`})]})})}function Zc(e,t,n){let r=URL.createObjectURL(new Blob([t],{type:`${n};charset=utf-8`})),i=document.createElement(`a`);i.href=r,i.download=e,document.body.append(i),i.click(),i.remove(),URL.revokeObjectURL(r)}var Qc=[{id:`overview`,label:`Overview`,sub:`Calls, latency & risk at a glance`},{id:`devices`,label:`Devices`,sub:`Connectivity radar & system health`},{id:`topology`,label:`Topology`,sub:`Layer-2 neighbours via MNDP / CDP / LLDP`},{id:`packets`,label:`Packets`,sub:`Live TZSP capture & decode`},{id:`snapshots`,label:`Snapshots`,sub:`Config history & time-travel diff`},{id:`plan`,label:`Change Plan`,sub:`Dry-run intended RouterOS commands`},{id:`s3`,label:`S3 Backups`,sub:`List, download & delete S3 backup objects`},{id:`config`,label:`Config`,sub:`Effective configuration & safe editor`},{id:`feed`,label:`Live Feed`,sub:`Every tool call, in real time`}];function $c({name:e}){return(0,$.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.7`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:{overview:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`rect`,{x:`3`,y:`3`,width:`8`,height:`8`,rx:`1.6`}),(0,$.jsx)(`rect`,{x:`13`,y:`3`,width:`8`,height:`5`,rx:`1.6`}),(0,$.jsx)(`rect`,{x:`13`,y:`10`,width:`8`,height:`11`,rx:`1.6`}),(0,$.jsx)(`rect`,{x:`3`,y:`13`,width:`8`,height:`8`,rx:`1.6`})]}),devices:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`rect`,{x:`3`,y:`4`,width:`18`,height:`7`,rx:`2`}),(0,$.jsx)(`rect`,{x:`3`,y:`13`,width:`18`,height:`7`,rx:`2`}),(0,$.jsx)(`path`,{d:`M7 7.5h.01M7 16.5h.01`})]}),topology:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`circle`,{cx:`12`,cy:`5`,r:`2.4`}),(0,$.jsx)(`circle`,{cx:`5`,cy:`19`,r:`2.4`}),(0,$.jsx)(`circle`,{cx:`19`,cy:`19`,r:`2.4`}),(0,$.jsx)(`path`,{d:`M12 7.4 6.4 16.6M12 7.4 17.6 16.6`})]}),packets:(0,$.jsx)(`path`,{d:`M3 12h4l2-7 4 14 2-7h6`}),snapshots:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`path`,{d:`M12 3 3 7.5 12 12 21 7.5 12 3Z`}),(0,$.jsx)(`path`,{d:`M3 12 12 16.5 21 12`}),(0,$.jsx)(`path`,{d:`M3 16.5 12 21 21 16.5`})]}),plan:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`circle`,{cx:`6`,cy:`6`,r:`2.3`}),(0,$.jsx)(`circle`,{cx:`6`,cy:`18`,r:`2.3`}),(0,$.jsx)(`circle`,{cx:`18`,cy:`8`,r:`2.3`}),(0,$.jsx)(`path`,{d:`M6 8.3v7.4M8.3 6H13a3 3 0 0 1 3 3v0`})]}),s3:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`ellipse`,{cx:`12`,cy:`6`,rx:`7`,ry:`2.6`}),(0,$.jsx)(`path`,{d:`M5 6v12c0 1.5 3.1 2.6 7 2.6s7-1.1 7-2.6V6`}),(0,$.jsx)(`path`,{d:`M5 12c0 1.5 3.1 2.6 7 2.6s7-1.1 7-2.6`})]}),config:(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(`path`,{d:`M4 7h8M16 7h4M4 17h4M12 17h8`}),(0,$.jsx)(`circle`,{cx:`14`,cy:`7`,r:`2.2`}),(0,$.jsx)(`circle`,{cx:`10`,cy:`17`,r:`2.2`})]}),feed:(0,$.jsx)(`path`,{d:`M4 6h16M4 12h16M4 18h10`})}[e]})}function el(){let[e,t]=(0,l.useState)(null),[n,r]=(0,l.useState)(null),[i,a]=(0,l.useState)(null),[o,s]=(0,l.useState)(null),[c,u]=(0,l.useState)(null),[d,f]=(0,l.useState)(!1),[p,m]=(0,l.useState)(null),[h,g]=(0,l.useState)([]),[_,v]=(0,l.useState)(36e5),[y,b]=(0,l.useState)(!1),[x,S]=(0,l.useState)(`off`),[C,w]=(0,l.useState)(null),[T,E]=(0,l.useState)(()=>new Set),[D,O]=(0,l.useState)({}),[k,A]=(0,l.useState)({tool:``,risk:``,device:``,status:``,q:``}),j=(0,l.useRef)(y);j.current=y;let M=(0,l.useRef)(null);Mc(M);let[N,P]=(0,l.useState)(`overview`),[F,I]=(0,l.useState)(``),[L,ee]=(0,l.useState)(`all`),te=(0,l.useMemo)(()=>{let e=i?.devices??[];return{online:e.filter(e=>e.status.reachable===!0).length,offline:e.filter(e=>e.status.reachable===!1).length,total:e.length}},[i]),ne=(0,l.useMemo)(()=>{let e=i?.devices??[],t=F.trim().toLowerCase();return e.filter(e=>!(L===`online`&&e.status.reachable!==!0||L===`offline`&&e.status.reachable!==!1||t&&!e.name.toLowerCase().includes(t)&&!(e.address??e.host??``).toLowerCase().includes(t)))},[i,F,L]);jc((0,l.useCallback)(e=>{if(j.current)return;g(t=>[e,...t].slice(0,lc));let t=e.device;t&&O(e=>({...e,[t]:(e[t]??0)+1}))},[]),(0,l.useCallback)(e=>S(e),[])),(0,l.useEffect)(()=>{ac(`/api/events?limit=200`).then(e=>g(e.events)).catch(()=>{})},[]);let R=(0,l.useCallback)(()=>{ac(`/api/stats?window=${_}&buckets=60`).then(t).catch(()=>{})},[_]);(0,l.useEffect)(()=>{R();let e=()=>{R(),ac(`/api/meta`).then(r).catch(()=>{}),ac(`/api/devices`).then(a).catch(()=>{}),ac(`/api/topology`).then(s).catch(()=>{})};e();let t=setInterval(()=>{j.current||e()},4e3);return()=>clearInterval(t)},[R]),(0,l.useEffect)(()=>{let e=()=>void ac(`/api/config`).then(u).catch(()=>{});e();let t=setInterval(e,3e4);return()=>clearInterval(t)},[]),(0,l.useEffect)(()=>{let e=e=>{e.key===`Escape`&&w(null)};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]);let z=(0,l.useMemo)(()=>{let e=k.q.trim().toLowerCase();return h.filter(t=>!(k.tool&&t.tool!==k.tool||k.risk&&t.risk!==k.risk||k.device&&t.device!==k.device||k.status===`ok`&&t.isError||k.status===`error`&&!t.isError||e&&!t.tool.toLowerCase().includes(e)&&!t.input.toLowerCase().includes(e)&&!t.output.toLowerCase().includes(e)&&!(t.error??``).toLowerCase().includes(e))).sort((e,t)=>t.ts-e.ts)},[h,k]),B=!!(k.tool||k.risk||k.device||k.status||k.q),re=(0,l.useMemo)(()=>h.filter(e=>e.isError),[h]),V=(0,l.useMemo)(()=>({ok:h.length-re.length,error:re.length}),[h,re]),ie=(0,l.useCallback)(async e=>{try{w(await ac(`/api/event/${encodeURIComponent(e.id)}`))}catch{w(e)}},[]),ae=(0,l.useMemo)(()=>z.slice(0,200),[z]),oe=(0,l.useMemo)(()=>ae.map(e=>e.id),[ae]),se=oe.length>0&&oe.every(e=>T.has(e)),H=!se&&oe.some(e=>T.has(e)),ce=(0,l.useCallback)(e=>{E(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),le=(0,l.useCallback)(()=>{E(e=>{let t=new Set(e);if(oe.length>0&&oe.every(e=>t.has(e)))for(let e of oe)t.delete(e);else for(let e of oe)t.add(e);return t})},[oe]),[ue,de]=(0,l.useState)(!1),fe=(0,l.useCallback)(async()=>{let e=[...T];if(e.length!==0)try{await oc({ids:e});let t=new Set(e);g(e=>e.filter(e=>!t.has(e.id))),E(new Set)}catch{}finally{de(!1)}},[T]),pe=e=>{if(e===`json`){Zc(`mcp-events.json`,JSON.stringify(z,null,2),`application/json`);return}let t=[`ts`,`tool`,`risk`,`device`,`durationMs`,`isError`,`error`],n=e=>/[",\n]/.test(e)?`"${e.replace(/"/g,`""`)}"`:e,r=z.map(e=>[new Date(e.ts).toISOString(),e.tool,e.risk,e.device??``,String(e.durationMs),String(e.isError),e.error??``].map(n).join(`,`)).join(`
28
+ `);Zc(`mcp-events.csv`,`${t.join(`,`)}\n${r}\n`,`text/csv`)},me=e?e.errorRate>=.2?`is-bad`:e.errorRate>=.05?`is-warn`:`is-good`:``,he=c?.mcp??{},ge=c?.dashboard??{},U=(e,t,n)=>(0,$.jsxs)(`select`,{className:`btn`,value:k[e],onChange:t=>A(n=>({...n,[e]:t.target.value})),children:[(0,$.jsx)(`option`,{value:``,children:t}),n.map(e=>(0,$.jsx)(`option`,{value:e,children:e},e))]}),_e=Qc.find(e=>e.id===N)??Qc[0];return(0,$.jsxs)(`div`,{className:`shell`,ref:M,children:[(0,$.jsxs)(`aside`,{className:`nav`,children:[(0,$.jsxs)(`div`,{className:`nav__brand`,children:[(0,$.jsx)(`div`,{className:`nav__mark`,children:(0,$.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,"aria-hidden":`true`,children:(0,$.jsxs)(`g`,{stroke:`#18181b`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,$.jsx)(`path`,{d:`M12 12 L4 5 M12 12 L20 5 M12 12 L12 20`}),(0,$.jsx)(`circle`,{cx:`12`,cy:`12`,r:`3`,fill:`#18181b`,stroke:`none`}),(0,$.jsx)(`circle`,{cx:`4`,cy:`5`,r:`1.9`,fill:`#18181b`,stroke:`none`}),(0,$.jsx)(`circle`,{cx:`20`,cy:`5`,r:`1.9`,fill:`#18181b`,stroke:`none`}),(0,$.jsx)(`circle`,{cx:`12`,cy:`20`,r:`1.9`,fill:`#18181b`,stroke:`none`})]})})}),(0,$.jsxs)(`div`,{className:`nav__brandtext`,children:[(0,$.jsx)(`b`,{children:`MikroTik MCP`}),(0,$.jsx)(`small`,{children:`Observability`})]})]}),(0,$.jsx)(`nav`,{className:`nav__items`,children:Qc.map(e=>(0,$.jsxs)(`button`,{className:`nav__item${N===e.id?` is-active`:``}`,onClick:()=>P(e.id),children:[(0,$.jsx)($c,{name:e.id}),(0,$.jsx)(`span`,{children:e.label}),e.id===`feed`&&h.length>0&&(0,$.jsx)(`span`,{className:`nav__badge`,children:h.length>999?`999+`:h.length})]},e.id))}),(0,$.jsxs)(`div`,{className:`nav__foot`,children:[(0,$.jsxs)(`span`,{className:`hero__live${x===`off`?``:` is-on`}`,title:`Live transport: WebSocket (preferred) or SSE fallback`,children:[(0,$.jsx)(`span`,{className:`dot`}),x===`off`?`offline`:`live · ${x}`]}),(0,$.jsx)(`small`,{className:`muted`,children:n?`${ec(n.total)} events · ${n.transport}`:`connecting…`})]})]}),(0,$.jsxs)(`main`,{className:`main`,children:[(0,$.jsxs)(`header`,{className:`topline reveal`,children:[(0,$.jsxs)(`div`,{className:`topline__txt`,children:[(0,$.jsx)(`h1`,{children:_e.label}),(0,$.jsx)(`small`,{children:_e.sub})]}),(0,$.jsx)(`span`,{className:`topline__spacer`}),N===`overview`&&(0,$.jsx)(`select`,{className:`btn`,value:_,onChange:e=>v(Number(e.target.value)),title:`Stats time window`,children:cc.map(([e,t])=>(0,$.jsxs)(`option`,{value:t,children:[`window: `,e]},t))})]}),N===`overview`&&(0,$.jsxs)(`section`,{className:`view`,children:[(0,$.jsx)(`div`,{className:`cards reveal`,children:e?(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(dc,{k:`Calls (window)`,v:ec(e.total)}),(0,$.jsx)(dc,{k:`Calls / min`,v:e.callsPerMin.toFixed(1)}),(0,$.jsx)(dc,{k:`Error rate`,v:`${(e.errorRate*100).toFixed(1)}%`,sub:`${e.errors} err`,cls:me}),(0,$.jsx)(dc,{k:`Avg latency`,v:Zs(e.latency.avg)}),(0,$.jsx)(dc,{k:`p95 latency`,v:Zs(e.latency.p95)}),(0,$.jsx)(dc,{k:`p99 latency`,v:Zs(e.latency.p99)}),(0,$.jsx)(dc,{k:`Distinct tools`,v:ec(e.distinctTools)}),(0,$.jsx)(dc,{k:`Output volume`,v:Qs(e.outputBytes)})]}):(0,$.jsxs)(`div`,{className:`stat`,children:[(0,$.jsx)(`p`,{className:`k`,children:`Loading…`}),(0,$.jsx)(`div`,{className:`v`,children:`—`})]})}),(0,$.jsxs)(`div`,{className:`bento reveal`,children:[(0,$.jsxs)(uc,{title:`Calls over time`,className:`b-series`,children:[e?(0,$.jsx)(fc,{series:e.series}):(0,$.jsx)(`div`,{className:`muted`,children:`no data`}),(0,$.jsxs)(`div`,{className:`legend`,children:[(0,$.jsxs)(`span`,{children:[(0,$.jsx)(`i`,{style:{background:`#d4d4d8`}}),`ok`]}),(0,$.jsxs)(`span`,{children:[(0,$.jsx)(`i`,{style:{background:`#f87171`}}),`error`]})]})]}),e&&(0,$.jsxs)($.Fragment,{children:[(0,$.jsx)(uc,{title:`By risk`,className:`b-risk`,children:(0,$.jsx)(pc,{segments:Object.keys(e.byRisk).map(t=>({label:t,value:e.byRisk[t],color:nc[t]}))})}),(0,$.jsx)(uc,{title:`Top tools`,className:`b-tools`,children:(0,$.jsx)(mc,{rows:e.byTool.map(e=>({label:e.tool,value:e.count,sub:`${e.count}× · ${Zs(e.p95Ms)} p95${e.errors?` · ${e.errors} err`:``}`,color:e.errors?`#f87171`:void 0}))})}),(0,$.jsx)(uc,{title:`Status`,className:`b-status`,children:(0,$.jsx)(pc,{segments:[{label:`ok`,value:V.ok,color:`#d4d4d8`},{label:`error`,value:V.error,color:`#f87171`}]})}),(0,$.jsx)(uc,{title:`By device`,className:`b-device`,children:e.byDevice.length?(0,$.jsx)(mc,{rows:e.byDevice.map(e=>({label:e.device,value:e.count}))}):(0,$.jsx)(`div`,{className:`muted`,children:`single device`})}),(0,$.jsx)(uc,{title:`Recent errors`,className:`b-errors`,children:re.length?(0,$.jsx)(`div`,{className:`hbar`,children:re.slice(0,8).map(e=>(0,$.jsxs)(`div`,{className:`hbar__row conn-errrow`,style:{gridTemplateColumns:`auto 1fr`},onClick:()=>void ie(e),children:[(0,$.jsx)(`span`,{className:`muted`,children:$s(e.ts)}),(0,$.jsxs)(`span`,{style:{color:`var(--mt-bad)`,whiteSpace:`nowrap`,overflow:`hidden`,textOverflow:`ellipsis`,minWidth:0},title:e.error??e.output,children:[e.tool,`: `,e.error??e.output??`error`]})]},e.id))}):(0,$.jsx)(`div`,{className:`muted`,children:`no errors 🎉`})})]})]})]}),N===`devices`&&(i&&i.devices.length>0?(0,$.jsxs)(`section`,{className:`view`,children:[(0,$.jsxs)(`div`,{className:`dev-toolbar reveal`,children:[(0,$.jsx)(`input`,{className:`search`,type:`search`,placeholder:`Search devices by name or address…`,value:F,onChange:e=>I(e.target.value),style:{flex:1,minWidth:180}}),(0,$.jsx)(`div`,{className:`dev-filters`,children:[`all`,`online`,`offline`].map(e=>(0,$.jsx)(`button`,{className:`dev-fbtn${L===e?` is-active`:``}`,onClick:()=>ee(e),children:e===`all`?`All ${te.total}`:e===`online`?`Online ${te.online}`:`Offline ${te.offline}`},e))}),(0,$.jsxs)(`span`,{className:`muted`,children:[ne.length,`/`,te.total,` shown`]})]}),(0,$.jsxs)(`details`,{className:`dev-collapse reveal`,open:te.total<=8,children:[(0,$.jsxs)(`summary`,{children:[`Connectivity radar`,(0,$.jsxs)(`span`,{className:`muted`,children:[` `,`· `,te.online,` online · `,te.offline,` offline ·`,` `,te.total,` total`]})]}),(0,$.jsx)(Tc,{payload:i,pulses:D})]}),ne.length===0?(0,$.jsxs)(`div`,{className:`feed-empty reveal`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`🔍`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`No devices match`}),(0,$.jsx)(`p`,{className:`feed-empty__sub`,children:`Try a different search or status filter.`})]}):(0,$.jsx)(`div`,{className:`dev-grid-wide reveal`,children:ne.map(e=>(0,$.jsx)(Ec,{d:e},e.name))}),ne.length>0&&(0,$.jsx)(uc,{title:`Device system health`,className:`reveal`,extra:(0,$.jsx)(`span`,{className:`muted`,children:`CPU · memory · disk · latency · live probe`}),children:(0,$.jsx)(`div`,{className:`health-grid`,children:ne.map(e=>(0,$.jsx)(Ac,{d:e},e.name))})})]}):(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`🖧`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`No devices configured`}),(0,$.jsx)(`p`,{className:`feed-empty__sub`,children:`Add a device to your config to see connectivity and system health here.`})]})),N===`topology`&&(o&&o.nodes.length>0?(0,$.jsx)(`section`,{className:`view`,children:(0,$.jsx)(uc,{title:`Network topology`,className:`reveal`,extra:(0,$.jsx)(`span`,{className:`muted`,children:`Layer-2 neighbours via MNDP/CDP/LLDP · click a neighbour to onboard it`}),children:(0,$.jsx)(Rc,{topo:o,onOnboard:(e,t)=>{m({name:e,body:t}),f(!0),P(`config`)}})})}):(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`🛰️`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`No neighbours discovered yet`}),(0,$.jsx)(`p`,{className:`feed-empty__sub`,children:`Layer-2 neighbours (MNDP / CDP / LLDP) appear here as the device reports them.`})]})),N===`packets`&&(0,$.jsx)(`section`,{className:`view`,children:(0,$.jsx)(uc,{title:`Packet capture`,className:`reveal`,extra:(0,$.jsx)(`span`,{className:`muted`,children:`live TZSP decode · /tool sniffer streaming`}),children:(0,$.jsx)(Wc,{})})}),N===`snapshots`&&(0,$.jsx)(Kc,{}),N===`plan`&&(0,$.jsx)(Jc,{}),N===`s3`&&(0,$.jsx)(Yc,{}),N===`config`&&(c?(0,$.jsx)(`section`,{className:`view`,children:(0,$.jsx)(uc,{title:`Configuration`,className:`reveal`,extra:(0,$.jsx)(`button`,{className:`btn`,onClick:()=>f(e=>!e),title:`Edit the config JSON with autocomplete, validation and safe-apply`,children:d?`View`:`✎ Edit config`}),children:d?(0,$.jsx)(Ic,{initial:p?{...c,devices:{...c.devices,[p.name]:p.body}}:c,onClose:()=>{f(!1),m(null)},onReload:()=>{m(null),ac(`/api/config`).then(u).catch(()=>{})}},p?`seed-${p.name}`:`config`):(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`div`,{className:`legend`,style:{margin:`0 0 10px`},children:[(0,$.jsxs)(`span`,{children:[`transport: `,tc(he.transport)]}),(0,$.jsxs)(`span`,{children:[`read-only: `,c.readOnly?`yes`:`no`]}),(0,$.jsxs)(`span`,{children:[`dashboard: `,tc(ge.host),`:`,tc(ge.port)]}),(0,$.jsxs)(`span`,{children:[`capture: `,ge.captureBody?`on`:`off`]}),(0,$.jsxs)(`span`,{children:[`s3: `,c.s3?`configured`:`off`]})]}),(0,$.jsxs)(`details`,{className:`cfg`,children:[(0,$.jsx)(`summary`,{children:`Full effective configuration (secrets redacted)`}),(0,$.jsx)(Gc,{value:c,maxHeight:340})]})]})})}):(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`⚙️`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`Loading configuration…`})]})),N===`feed`&&(0,$.jsxs)(`div`,{className:`panel reveal`,children:[(0,$.jsxs)(`div`,{className:`sheet__hd`,style:{marginBottom:12},children:[(0,$.jsx)(`h2`,{style:{margin:0},children:`Live tool calls`}),(0,$.jsx)(`span`,{style:{flex:1}}),(0,$.jsxs)(`span`,{className:`muted`,children:[z.length,` shown · `,h.length,` buffered`]})]}),(0,$.jsxs)(`div`,{className:`toolbar`,style:{marginBottom:12},children:[(0,$.jsx)(`div`,{className:`grow`,style:{flex:1,minWidth:180},children:(0,$.jsx)(`input`,{className:`search`,type:`search`,placeholder:`Search tool / input / output / error…`,value:k.q,onChange:e=>A(t=>({...t,q:e.target.value}))})}),U(`tool`,`all tools`,n?.tools??[]),U(`risk`,`all risk`,[`READ`,`WRITE`,`WRITE_IDEMPOTENT`,`DESTRUCTIVE`,`DANGEROUS`]),U(`device`,`all devices`,n?.devices??[]),U(`status`,`all status`,[`ok`,`error`]),(0,$.jsx)(`button`,{className:`btn${y?` is-active`:``}`,onClick:()=>b(e=>!e),children:y?`▶ Resume`:`⏸ Pause`}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>pe(`csv`),children:`CSV`}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>pe(`json`),children:`JSON`}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>A({tool:``,risk:``,device:``,status:``,q:``}),children:`Clear`}),ue&&T.size>0?(0,$.jsxs)($.Fragment,{children:[(0,$.jsxs)(`button`,{className:`btn btn-danger`,onClick:()=>void fe(),children:[`✓ Confirm delete (`,T.size,`)`]}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>de(!1),children:`Cancel`})]}):(0,$.jsxs)(`button`,{className:`btn`,disabled:T.size===0,onClick:()=>de(!0),title:`Delete the selected rows`,children:[`🗑 Delete`,T.size>0?` (${T.size})`:``]})]}),z.length===0?B?(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__icon`,children:`🔍`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:`No calls match your filters`}),(0,$.jsxs)(`p`,{className:`feed-empty__sub`,children:[h.length,` call`,h.length===1?``:`s`,` buffered — try widening the search or the risk / device / status filters.`]}),(0,$.jsx)(`button`,{className:`btn`,onClick:()=>A({tool:``,risk:``,device:``,status:``,q:``}),children:`Clear filters`})]}):(0,$.jsxs)(`div`,{className:`feed-empty`,children:[(0,$.jsx)(`div`,{className:`feed-empty__pulse${x===`off`?``:` is-on`}`}),(0,$.jsx)(`p`,{className:`feed-empty__title`,children:x===`off`?`Not connected`:`Listening for tool calls…`}),(0,$.jsx)(`p`,{className:`feed-empty__sub`,children:x===`off`?`The live stream is offline — it will reconnect automatically.`:`Tool calls the LLM makes against this server stream in here in real time.`})]}):(0,$.jsx)(`div`,{className:`feedwrap`,children:(0,$.jsxs)(`table`,{className:`feed`,children:[(0,$.jsx)(`thead`,{children:(0,$.jsxs)(`tr`,{children:[(0,$.jsx)(`th`,{style:{width:28},children:(0,$.jsx)(`input`,{type:`checkbox`,"aria-label":`Select all shown rows`,checked:se,ref:e=>{e&&(e.indeterminate=H)},onChange:le})}),(0,$.jsx)(`th`,{children:`time`}),(0,$.jsx)(`th`,{children:`tool`}),(0,$.jsx)(`th`,{children:`risk`}),(0,$.jsx)(`th`,{children:`device`}),(0,$.jsx)(`th`,{className:`num`,children:`dur`}),(0,$.jsx)(`th`,{children:`status`}),(0,$.jsx)(`th`,{children:`output`})]})}),(0,$.jsx)(`tbody`,{children:ae.map(e=>(0,$.jsxs)(`tr`,{className:`${e.isError?`is-err`:``}${T.has(e.id)?` is-selected`:``}`.trim()||void 0,onClick:()=>void ie(e),children:[(0,$.jsx)(`td`,{onClick:e=>e.stopPropagation(),children:(0,$.jsx)(`input`,{type:`checkbox`,"aria-label":`Select row`,checked:T.has(e.id),onChange:()=>ce(e.id)})}),(0,$.jsx)(`td`,{children:$s(e.ts)}),(0,$.jsx)(`td`,{children:e.tool}),(0,$.jsx)(`td`,{children:(0,$.jsx)(`span`,{className:`risk risk-${e.risk}`,children:e.risk.replace(`WRITE_IDEMPOTENT`,`WRITE·I`)})}),(0,$.jsx)(`td`,{children:e.device??`—`}),(0,$.jsx)(`td`,{className:`num`,children:Zs(e.durationMs)}),(0,$.jsx)(`td`,{children:(0,$.jsx)(`span`,{className:e.isError?`status-err`:`status-ok`,children:e.isError?`error`:`ok`})}),(0,$.jsx)(`td`,{className:`preview`,children:e.isError?e.error??`error`:e.output||`—`})]},e.id))})]})})]})]}),C&&(0,$.jsx)(Xc,{event:C,onClose:()=>w(null)})]})}var tl=document.getElementById(`root`);tl&&(0,u.createRoot)(tl).render((0,$.jsx)(el,{}));
25
29
  </script>
26
30
  <style>
27
- @import "https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap";:root{--mt-bg:var(--color-background-primary,#0b0d10);--mt-surface:var(--color-background-secondary,#14171c);--mt-surface-2:var(--color-background-tertiary,#1b1f26);--mt-border:var(--color-border-primary,#262b33);--mt-text:var(--color-text-primary,#e8eaed);--mt-text-dim:var(--color-text-secondary,#9aa3af);--mt-text-faint:var(--color-text-tertiary,#6b7280);--mt-accent:var(--color-accent-primary,#6ea8fe);--mt-good:#34d399;--mt-warn:#fbbf24;--mt-bad:#f87171;--mt-radius:var(--border-radius-md,14px);--mt-radius-sm:var(--border-radius-sm,9px);--mt-mono:var(--font-mono,ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace);--mt-sans:var(--font-sans,system-ui, -apple-system, "Segoe UI", sans-serif);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}*{box-sizing:border-box}body{background:var(--mt-bg);color:var(--mt-text);font-family:var(--mt-sans);-webkit-font-smoothing:antialiased;margin:0;padding:16px;font-size:13px;line-height:1.5}.app{gap:14px;max-width:960px;margin:0 auto;display:grid}.hd{flex-wrap:wrap;align-items:center;gap:12px;display:flex}.hd__dot{background:var(--mt-good);width:9px;height:9px;box-shadow:0 0 0 4px color-mix(in srgb, var(--mt-good) 22%, transparent);border-radius:50%}.hd__title{letter-spacing:-.01em;margin:0;font-size:17px;font-weight:650}.hd__sub{color:var(--mt-text-dim);font-family:var(--mt-mono);margin:0;font-size:12px}.hd__spacer{flex:1}.pill{border:1px solid var(--mt-border);background:var(--mt-surface);color:var(--mt-text-dim);font-family:var(--mt-mono);border-radius:999px;align-items:center;gap:6px;padding:3px 9px;font-size:11px;display:inline-flex}.pill b{color:var(--mt-text);font-weight:600}.grid{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;display:grid}.card{background:linear-gradient(180deg, var(--mt-surface), var(--mt-surface-2));border:1px solid var(--mt-border);border-radius:var(--mt-radius);padding:14px}.card__label{color:var(--mt-text-dim);text-transform:uppercase;letter-spacing:.06em;margin:0 0 8px;font-size:11px}.card__value{font-family:var(--mt-mono);letter-spacing:-.01em;font-size:20px;font-weight:600}.toolbar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.toolbar .grow{flex:1;min-width:140px}.search{appearance:none;border:1px solid var(--mt-border);background:var(--mt-surface);width:100%;color:var(--mt-text);font:inherit;border-radius:var(--mt-radius-sm);padding:7px 11px;font-size:13px}.search:focus{border-color:var(--mt-accent);outline:none}.search::placeholder{color:var(--mt-text-faint)}.btn{appearance:none;border:1px solid var(--mt-border);background:var(--mt-surface);color:var(--mt-text);font:inherit;border-radius:var(--mt-radius-sm);cursor:pointer;white-space:nowrap;padding:6px 11px;font-size:12px;transition:border-color .15s,background .15s}.btn:disabled{opacity:.5;cursor:default}.chip{border:1px solid var(--mt-border);background:var(--mt-surface);color:var(--mt-text-dim);font-family:var(--mt-mono);border-radius:999px;align-items:center;gap:4px;padding:1px 7px;font-size:11px;display:inline-flex}.chip.is-good{color:var(--mt-good);border-color:color-mix(in srgb, var(--mt-good) 45%, var(--mt-border))}.chip.is-warn{color:var(--mt-warn);border-color:color-mix(in srgb, var(--mt-warn) 45%, var(--mt-border))}.chip.is-bad{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border))}.tablewrap{border:1px solid var(--mt-border);border-radius:var(--mt-radius);max-height:70vh;overflow:auto}table.tbl{border-collapse:collapse;width:100%;font-size:12px}table.tbl th,table.tbl td{text-align:left;border-bottom:1px solid color-mix(in srgb, var(--mt-border) 55%, transparent);white-space:nowrap;font-family:var(--mt-mono);padding:7px 12px}table.tbl thead th{z-index:1;background:var(--mt-surface-2);color:var(--mt-text-dim);cursor:pointer;-webkit-user-select:none;user-select:none;font-weight:600;position:sticky;top:0}table.tbl thead th:hover{color:var(--mt-text)}table.tbl thead th .arrow{color:var(--mt-accent);margin-left:4px}table.tbl tbody tr{cursor:pointer}table.tbl tbody tr:hover{background:color-mix(in srgb, var(--mt-accent) 8%, transparent)}table.tbl tbody tr.is-disabled td,table.tbl td.col-num{color:var(--mt-text-faint)}.kv__body{border:1px solid var(--mt-border);border-radius:var(--mt-radius);grid-template-columns:minmax(120px,.4fr) 1fr;display:grid;overflow:hidden}.kv__body>div{border-bottom:1px solid color-mix(in srgb, var(--mt-border) 55%, transparent);overflow-wrap:anywhere;min-width:0;font-size:12px;font-family:var(--mt-mono);padding:7px 14px}.kv__k{color:var(--mt-text-dim);background:color-mix(in srgb, var(--mt-surface) 60%, transparent)}.kv__v{color:var(--mt-text)}.drawer{border:1px solid var(--mt-accent);border-radius:var(--mt-radius);background:var(--mt-surface);gap:10px;padding:12px;display:grid}.drawer__hd{align-items:center;gap:8px;display:flex}.drawer__hd b{font-family:var(--mt-mono)}pre.raw{border:1px solid var(--mt-border);border-radius:var(--mt-radius);background:var(--mt-surface);color:var(--mt-text);font-family:var(--mt-mono);white-space:pre;max-height:70vh;margin:0;padding:14px;font-size:12px;overflow:auto}.foot{color:var(--mt-text-faint);font-size:11px;font-family:var(--mt-mono);flex-wrap:wrap;align-items:center;gap:10px;display:flex}.foot .grow{flex:1}.skeleton,.empty{color:var(--mt-text-faint);text-align:center;padding:40px 0}:root{--mt-sans:"Space Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;--mt-mono:"DM Mono", ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;--mt-bg:#09090b;--mt-surface:#18181b;--mt-surface-2:#27272a;--mt-border:#27272a;--mt-text:#fafafa;--mt-text-dim:#a1a1aa;--mt-text-faint:#71717a;--mt-accent:#e4e4e7;--mt-good:#d4d4d8;--mt-warn:#a1a1aa;--mt-bad:#f87171;--mt-radius:12px;--mt-radius-sm:8px;--sky:#d4d4d8;--teal:#e4e4e7;--cyan:#a1a1aa;--lime:#71717a;--coral:#f87171;--amber:#a1a1aa;--grad:linear-gradient(115deg, #e4e4e7, #a1a1aa 50%, #52525b);--ease:cubic-bezier(.22, 1, .36, 1);--glass:#18181b8c}html,body{background:var(--mt-bg);color:var(--mt-text);font-family:var(--mt-sans);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;margin:0;padding:0}body:before{content:"";z-index:-2;background:radial-gradient(58% 42% at 12% -6%, color-mix(in srgb, var(--teal) 16%, transparent), transparent 70%), radial-gradient(50% 40% at 100% 0%, color-mix(in srgb, var(--sky) 13%, transparent), transparent 68%), radial-gradient(60% 50% at 84% 108%, color-mix(in srgb, var(--lime) 8%, transparent), transparent 70%);pointer-events:none;position:fixed;inset:0}body:after{content:"";z-index:-2;background-image:linear-gradient(color-mix(in srgb, var(--mt-border) 70%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, var(--mt-border) 70%, transparent) 1px, transparent 1px);opacity:.5;pointer-events:none;background-size:46px 46px;position:fixed;inset:0;-webkit-mask-image:radial-gradient(circle at 50% 0,#000,#0000 75%);mask-image:radial-gradient(circle at 50% 0,#000,#0000 75%)}.shell{grid-template-columns:250px minmax(0,1fr);align-items:start;min-height:100vh;display:grid}.nav{border-right:1px solid var(--mt-border);background:color-mix(in srgb, var(--mt-surface) 55%, transparent);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);flex-direction:column;gap:10px;height:100vh;padding:18px 14px;display:flex;position:sticky;top:0}.nav__brand{align-items:center;gap:11px;padding:6px 8px 14px;display:flex}.nav__mark{background:var(--grad);width:38px;height:38px;box-shadow:0 8px 22px color-mix(in srgb, var(--teal) 38%, transparent);border-radius:11px;flex:none;place-items:center;display:grid}.nav__mark svg{width:22px;height:22px;display:block}.nav__brandtext{flex-direction:column;min-width:0;line-height:1.1;display:flex}.nav__brandtext b{letter-spacing:-.02em;font-size:15px;font-weight:700}.nav__brandtext small{font-family:var(--mt-mono);letter-spacing:.16em;text-transform:uppercase;color:var(--mt-text-dim);margin-top:3px;font-size:10px}.nav__items{flex-direction:column;flex:1;gap:3px;display:flex}.nav__item{color:var(--mt-text-dim);font-family:var(--mt-sans);cursor:pointer;text-align:left;transition:background .18s var(--ease), color .18s, border-color .18s, transform .18s var(--ease);background:0 0;border:1px solid #0000;border-radius:11px;align-items:center;gap:11px;padding:10px 12px;font-size:13px;font-weight:500;display:flex}.nav__item svg{opacity:.85;flex:none;width:18px;height:18px}.nav__item:hover{color:var(--mt-text);background:color-mix(in srgb, var(--teal) 9%, transparent)}.nav__item.is-active{color:var(--mt-text);border-color:color-mix(in srgb, var(--teal) 32%, var(--mt-border));background:linear-gradient(100deg, color-mix(in srgb, var(--teal) 18%, transparent), color-mix(in srgb, var(--sky) 7%, transparent))}.nav__item.is-active svg{opacity:1;color:var(--teal)}.nav__badge{font-family:var(--mt-mono);background:color-mix(in srgb, var(--teal) 22%, transparent);color:var(--mt-text);border-radius:999px;margin-left:auto;padding:1px 7px;font-size:10px}.nav__foot{border-top:1px solid var(--mt-border);flex-direction:column;gap:8px;padding:10px 8px 2px;display:flex}.main{align-content:start;gap:18px;min-width:0;padding:22px clamp(16px,3vw,44px) 64px;display:grid}.topline{flex-wrap:wrap;align-items:center;gap:14px;display:flex}.topline__txt h1{letter-spacing:-.025em;background:linear-gradient(100deg, var(--mt-text), color-mix(in srgb, var(--teal) 65%, var(--mt-text)));-webkit-text-fill-color:transparent;-webkit-background-clip:text;background-clip:text;margin:0;font-size:clamp(19px,2.4vw,26px);font-weight:700}.topline__txt small{color:var(--mt-text-dim);font-family:var(--mt-mono);margin-top:3px;font-size:12px;display:block}.topline__spacer{flex:1;min-width:8px}.view{align-content:start;gap:18px;display:grid}.bento{grid-template-columns:repeat(6,1fr);gap:18px;display:grid}.bento .panel{margin:0}.b-series{grid-column:span 4}.b-risk{grid-column:span 2}.b-tools,.b-status,.b-device,.b-errors{grid-column:span 3}.b-risk,.b-status{flex-direction:column;display:flex}@media (width<=1040px){.bento{grid-template-columns:repeat(2,1fr)}.b-series,.b-risk,.b-tools,.b-status,.b-device,.b-errors{grid-column:span 2}}@media (width<=900px){.shell{grid-template-columns:1fr}.nav{border-right:0;border-bottom:1px solid var(--mt-border);z-index:5;flex-direction:row;align-items:center;gap:8px;height:auto;padding:10px 12px;position:sticky;top:0;overflow-x:auto}.nav__brand{padding:0 6px 0 2px}.nav__brandtext{display:none}.nav__items{flex-direction:row;flex:1;gap:4px}.nav__item span{display:none}.nav__item{padding:9px 11px}.nav__foot{border-top:0;flex-direction:row;align-items:center;padding:0}.nav__foot .muted{display:none}}@media (width<=560px){.nav__brand{display:none}}@media (prefers-reduced-motion:no-preference){.js-motion .reveal{opacity:0}}.hero__live{border:1px solid var(--mt-border);background:var(--glass);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);font-family:var(--mt-mono);letter-spacing:.04em;text-transform:uppercase;color:var(--mt-text-dim);border-radius:999px;align-items:center;gap:7px;padding:7px 13px;font-size:11px;display:inline-flex}.hero__live .dot{background:var(--mt-bad);border-radius:50%;width:8px;height:8px;transition:background .3s}.hero__live.is-on{color:var(--mt-text);border-color:color-mix(in srgb, var(--mt-good) 40%, var(--mt-border))}.hero__live.is-on .dot{background:var(--mt-good);box-shadow:0 0 0 4px color-mix(in srgb, var(--mt-good) 22%, transparent);animation:1.8s ease-out infinite hero-pulse}@keyframes hero-pulse{0%{box-shadow:0 0 0 0 color-mix(in srgb, var(--mt-good) 55%, transparent)}70%{box-shadow:0 0 0 9px color-mix(in srgb, var(--mt-good) 0%, transparent)}to{box-shadow:0 0 0 0 color-mix(in srgb, var(--mt-good) 0%, transparent)}}.panel>h2,.sheet__hd h2{font-family:var(--mt-sans)}.cards{grid-template-columns:repeat(2,1fr);gap:12px;display:grid}@media (width>=560px){.cards{grid-template-columns:repeat(4,1fr)}}@media (width>=1040px){.cards{grid-template-columns:repeat(8,1fr)}}.stat{background:linear-gradient(180deg, var(--mt-surface), color-mix(in srgb, var(--mt-surface-2) 80%, transparent));border:1px solid var(--mt-border);border-radius:var(--mt-radius);transition:transform .3s var(--ease), border-color .3s var(--ease);padding:14px 15px;position:relative;overflow:hidden}.stat:before{content:"";background:var(--grad);opacity:0;height:2px;transition:opacity .3s var(--ease);position:absolute;inset:0 0 auto}.stat:hover{border-color:color-mix(in srgb, var(--teal) 38%, var(--mt-border));transform:translateY(-3px)}.stat:hover:before{opacity:1}.stat .k{color:var(--mt-text-dim);text-transform:uppercase;letter-spacing:.08em;margin:0 0 7px;font-size:10px}.stat .v{font-family:var(--mt-mono);letter-spacing:-.02em;font-variant-numeric:tabular-nums;font-size:23px;font-weight:500}.stat .v small{color:var(--mt-text-dim);font-size:12px;font-weight:400}.stat.is-bad .v{color:var(--mt-bad)}.stat.is-warn .v{color:var(--mt-warn)}.stat.is-good .v{color:var(--mt-good)}.panel{background:var(--glass);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid var(--mt-border);border-radius:var(--mt-radius);padding:18px;position:relative;box-shadow:inset 0 1px #ffffff0a}.panel>h2{color:var(--mt-text-dim);text-transform:uppercase;letter-spacing:.07em;align-items:center;gap:9px;margin:0 0 14px;font-size:12px;font-weight:600;display:inline-flex}.panel>h2:before,.sheet__hd h2:first-child:before{content:"";background:var(--grad);border-radius:2px;flex:none;width:3px;height:13px}.cols{grid-template-columns:1.4fr 1fr;gap:18px;display:grid}@media (width<=820px){.cols{grid-template-columns:1fr}}.cols-3{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:18px;display:grid}.cols-3 .panel{flex-direction:column;display:flex}.hbar{gap:9px;display:grid}.hbar__row{grid-template-columns:minmax(90px,.9fr) 2fr auto;align-items:center;gap:10px;font-size:12px;display:grid}.hbar__label{font-family:var(--mt-mono);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.hbar__track{background:color-mix(in srgb, var(--mt-surface-2) 90%, transparent);border-radius:999px;height:8px;overflow:hidden}.hbar__fill{background:var(--grad);height:100%;box-shadow:0 0 10px color-mix(in srgb, var(--teal) 45%, transparent);border-radius:999px}.hbar__val{font-family:var(--mt-mono);color:var(--mt-text-dim);text-align:right;font-variant-numeric:tabular-nums;font-size:11px}.legend{font-family:var(--mt-mono);color:var(--mt-text-dim);flex-wrap:wrap;gap:12px;margin-top:12px;font-size:11px;display:flex}.legend span{align-items:center;gap:5px;display:inline-flex}.legend i{border-radius:2px;width:9px;height:9px;display:inline-block}.risk{font-family:var(--mt-mono);border:1px solid var(--mt-border);text-transform:uppercase;letter-spacing:.04em;border-radius:999px;padding:1px 8px;font-size:10px;display:inline-block}.risk-READ{color:var(--mt-good);border-color:color-mix(in srgb, var(--mt-good) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-good) 10%, transparent)}.risk-WRITE,.risk-WRITE_IDEMPOTENT{color:var(--mt-warn);border-color:color-mix(in srgb, var(--mt-warn) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-warn) 10%, transparent)}.risk-DESTRUCTIVE,.risk-DANGEROUS{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-bad) 10%, transparent)}.feed{border-collapse:collapse;width:100%;font-size:12px}.feed th,.feed td{text-align:left;border-bottom:1px solid color-mix(in srgb, var(--mt-border) 55%, transparent);white-space:nowrap;font-family:var(--mt-mono);padding:7px 11px}.feed thead th{background:color-mix(in srgb, var(--mt-surface-2) 96%, transparent);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);color:var(--mt-text-dim);text-transform:uppercase;letter-spacing:.05em;z-index:1;font-size:10px;font-weight:600;position:sticky;top:0}.feed tbody tr{cursor:pointer;transition:background .12s}.feed tbody tr:hover{background:color-mix(in srgb, var(--mt-accent) 9%, transparent)}.feed tbody tr.is-err{background:color-mix(in srgb, var(--mt-bad) 9%, transparent)}.feed tbody tr.is-selected{background:color-mix(in srgb, var(--mt-accent) 16%, transparent)}.feed td.preview{white-space:nowrap;text-overflow:ellipsis;max-width:320px;color:var(--mt-text-dim);overflow:hidden}.feed td.num{text-align:right;color:var(--mt-text-dim);font-variant-numeric:tabular-nums}.status-ok{color:var(--mt-good)}.status-err{color:var(--mt-bad)}.feedwrap{border:1px solid var(--mt-border);border-radius:var(--mt-radius);max-height:60vh;overflow:auto}.feed-empty{border:1px dashed var(--mt-border);border-radius:var(--mt-radius);text-align:center;justify-items:center;gap:8px;padding:54px 16px;display:grid}.feed-empty__icon{opacity:.8;font-size:28px}.feed-empty__title{color:var(--mt-text);margin:0;font-weight:600}.feed-empty__sub{max-width:460px;color:var(--mt-text-faint);margin:0;font-size:12px}.feed-empty .btn{margin-top:6px}.feed-empty__pulse{background:var(--mt-text-faint);border-radius:50%;width:12px;height:12px}.feed-empty__pulse.is-on{background:var(--mt-good);animation:1.6s ease-out infinite feed-pulse}@keyframes feed-pulse{0%{box-shadow:0 0 0 0 color-mix(in srgb, var(--mt-good) 55%, transparent)}70%{box-shadow:0 0 0 12px color-mix(in srgb, var(--mt-good) 0%, transparent)}to{box-shadow:0 0 0 0 color-mix(in srgb, var(--mt-good) 0%, transparent)}}.conn{display:block;overflow:visible}.conn-grid{fill:none;stroke:var(--mt-border);stroke-opacity:.7;stroke-dasharray:2 7}.conn-sonar{fill:none;stroke:var(--mt-accent);stroke-width:1.5px;opacity:0;animation:3.3s ease-out infinite conn-sonar}@keyframes conn-sonar{0%{r:30px;opacity:.5}to{r:150px;opacity:0}}.conn-link{fill:none;stroke-width:1.6px;opacity:.38}.conn-flow{fill:none;stroke-width:2.4px;stroke-linecap:round;stroke-dasharray:2 13;opacity:.9;filter:drop-shadow(0 0 4px);animation:1.05s linear infinite conn-flow}@keyframes conn-flow{to{stroke-dashoffset:-60px}}.conn-packet{filter:drop-shadow(0 0 6px var(--teal))}.conn-hub-glow{fill:var(--mt-accent);opacity:.22;filter:blur(11px)}.conn-hub-ring{fill:none;stroke:var(--mt-accent);stroke-width:1.4px;stroke-dasharray:4 9;opacity:.7;transform-box:fill-box;transform-origin:50%;animation:15s linear infinite conn-rot}@keyframes conn-rot{to{transform:rotate(360deg)}}.conn-node-halo{fill:none;stroke-width:2px;transform-box:fill-box;transform-origin:50%;animation:2.4s ease-in-out infinite conn-halo}@keyframes conn-halo{0%,to{opacity:.45;transform:scale(1)}50%{opacity:0;transform:scale(1.55)}}.conn-blink{animation:1.6s ease-in-out infinite conn-blink}@keyframes conn-blink{0%,to{opacity:1}50%{opacity:.35}}.conn-node text{pointer-events:none}@media (prefers-reduced-motion:reduce){.conn-sonar,.conn-flow,.conn-hub-ring,.conn-node-halo,.conn-blink,.conn-packet animateMotion{animation:none}.conn-sonar{display:none}}.dev-grid{grid-template-columns:1fr;align-content:start;gap:12px;display:grid}@media (width>=600px) and (width<=819px){.dev-grid{grid-template-columns:1fr 1fr}}.dev-toolbar{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.dev-filters{border:1px solid var(--mt-border);background:color-mix(in srgb, var(--mt-surface-2) 70%, transparent);border-radius:999px;gap:4px;padding:3px;display:inline-flex}.dev-fbtn{appearance:none;color:var(--mt-text-dim);font:11px var(--mt-mono);cursor:pointer;white-space:nowrap;background:0 0;border:0;border-radius:999px;padding:5px 12px;transition:background .18s,color .18s}.dev-fbtn:hover{color:var(--mt-text)}.dev-fbtn.is-active{background:var(--grad);color:var(--mt-bg);font-weight:600}.dev-grid-wide{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));align-content:start;gap:14px;display:grid}.dev-collapse{border:1px solid var(--mt-border);border-radius:var(--mt-radius);background:var(--glass);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);padding:6px 18px 18px}.dev-collapse>summary{cursor:pointer;text-transform:uppercase;letter-spacing:.07em;color:var(--mt-text-dim);align-items:center;gap:8px;padding:12px 0;font-size:12px;font-weight:600;list-style:none;display:flex}.dev-collapse>summary::-webkit-details-marker{display:none}.dev-collapse>summary:before{content:"▸";transition:transform .2s var(--ease);color:var(--teal);display:inline-block}.dev-collapse[open]>summary:before{transform:rotate(90deg)}.dev-card .dot--status{width:7px;height:7px}.dev-card{transition:transform .3s var(--ease), border-color .3s var(--ease);gap:8px;padding:14px 15px;display:grid}.dev-card:hover{border-color:color-mix(in srgb, var(--teal) 35%, var(--mt-border));transform:translateY(-2px)}.dev-card__top{align-items:center;gap:8px;display:flex}.dev-card__name{font-family:var(--mt-mono);font-size:13px;font-weight:500}.dev-card .dot{border-radius:50%;flex:none;width:9px;height:9px}.dev-card__meta{font-family:var(--mt-mono);color:var(--mt-text-dim);grid-template-columns:auto 1fr;gap:3px 12px;font-size:11px;display:grid}.dev-card__meta b{color:var(--mt-text);overflow-wrap:anywhere;font-weight:500}details.cfg>summary{cursor:pointer;color:var(--mt-text-dim);font-family:var(--mt-mono);-webkit-user-select:none;user-select:none;padding:4px 0;font-size:12px}.badge{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.06em;color:var(--mt-bg);background:var(--grad);border-radius:999px;padding:1px 7px;font-size:9px;font-weight:600}.overlay{-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);z-index:10;background:#0000009e;justify-content:flex-end;display:flex;position:fixed;inset:0}.sheet{background:color-mix(in srgb, var(--mt-surface) 96%, var(--mt-bg));border-left:1px solid color-mix(in srgb, var(--teal) 28%, var(--mt-border));align-content:start;gap:12px;width:min(580px,100%);height:100%;padding:18px;display:grid;overflow:auto;box-shadow:-24px 0 60px #00000080}.sheet__hd{align-items:center;gap:10px;display:flex}.sheet__hd h3{font-family:var(--mt-mono);margin:0;font-size:15px}pre.body{background:var(--mt-bg);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);font-family:var(--mt-mono);white-space:pre-wrap;word-break:break-word;max-height:40vh;margin:0;padding:12px;font-size:12px;overflow:auto}pre.body.json{color:#a1a1aa}pre.body.json .j-key{color:#7dd3fc}pre.body.json .j-str{color:#86efac}pre.body.json .j-num{color:#fcd34d}pre.body.json .j-bool{color:#d8b4fe}pre.body.json .j-null{color:#71717a}.muted{color:var(--mt-text-faint);font-family:var(--mt-mono);font-size:11px}.btn-danger{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-bad) 12%, transparent)}.btn-danger:hover{background:color-mix(in srgb, var(--mt-bad) 22%, transparent);border-color:var(--mt-bad)}.btn:hover{border-color:color-mix(in srgb, var(--teal) 55%, var(--mt-border));background:color-mix(in srgb, var(--teal) 10%, var(--mt-surface-2))}.btn.is-active{border-color:var(--teal);color:var(--teal)}.conn-legend{font-family:var(--mt-mono);color:var(--mt-text-dim);flex-wrap:wrap;justify-content:center;gap:16px;margin-top:8px;font-size:11px;display:flex}.conn-legend span{align-items:center;gap:6px;display:inline-flex}.conn-legend i{border-radius:3px;width:10px;height:10px;display:inline-block}.conn-chevron{fill:none;stroke-width:1.6px;stroke-linecap:round;stroke-linejoin:round;opacity:.85}.conn-errrow{cursor:pointer;border-radius:6px}.conn-errrow:hover{background:color-mix(in srgb, var(--mt-bad) 12%, transparent)}@media (prefers-reduced-motion:reduce){.conn-chevron{opacity:.6}}.health-grid{grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:14px;display:grid}.health-card{flex-direction:column;gap:10px;padding:16px;display:flex}.health-card--na{justify-content:center;gap:6px;min-height:120px}.health-card__hd{align-items:center;gap:8px;display:flex}.health-card__sub{font-family:var(--mt-mono);margin-top:-4px;font-size:11px}.health-card__gauges{justify-content:space-around;gap:14px;display:flex}.gauge{flex-direction:column;align-items:center;gap:2px;display:flex}.gauge__label{font-family:var(--mt-mono);letter-spacing:.06em;color:var(--mt-text-dim);font-size:10px}.health-card__charts{gap:8px;display:grid}.health-chart{gap:2px;display:grid}.health-chart__k{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.05em;color:var(--mt-text-dim);font-size:10px}.spark{background:color-mix(in srgb, var(--mt-surface-2) 60%, transparent);border-radius:6px;display:block}.spark--empty{font-family:var(--mt-mono);color:var(--mt-text-faint);text-align:center;padding:12px 6px;font-size:11px}.health-card__foot{font-family:var(--mt-mono);font-size:11px}.topo{flex-direction:column;gap:10px;display:flex}.topo svg{background:radial-gradient(circle at 50% 45%, color-mix(in srgb, var(--mt-accent) 8%, transparent), transparent 60%);border-radius:var(--mt-radius-sm);width:100%;display:block}.topo-edge{stroke:color-mix(in srgb, var(--mt-text-faint) 55%, transparent);stroke-width:1.4px}.topo-edge.is-dashed{stroke:color-mix(in srgb, var(--mt-accent) 60%, transparent);stroke-dasharray:4 4}.topo-node{cursor:pointer}.topo-node rect{fill:var(--mt-surface-2);stroke-width:1.5px;transition:filter .15s}.topo-node.is-neighbor rect{fill:color-mix(in srgb, var(--mt-surface) 70%, transparent);stroke-dasharray:4 3}.topo-node.is-onboard rect{stroke-dasharray:4 3}.topo-node:hover rect,.topo-node.is-picked rect{filter:drop-shadow(0 0 6px color-mix(in srgb, var(--mt-accent) 55%, transparent))}.topo-node.is-picked rect{stroke-width:2.2px}.topo-label{fill:var(--mt-text);font:600 12px var(--mt-mono)}.topo-sub{fill:var(--mt-text-dim);font:10px var(--mt-mono)}.topo-bar-bg{fill:color-mix(in srgb, var(--mt-text-faint) 35%, transparent)}.topo-foot{flex-wrap:wrap;justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.topo-foot .legend{margin-top:0}.topo-pop{background:var(--mt-surface);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);flex:1;min-width:280px;max-width:460px;padding:12px}.topo-pop__hd{align-items:center;gap:8px;margin-bottom:6px;display:flex}.topo-pop__hd .muted{font-size:11px}.topo-stub{background:var(--mt-bg);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);font:11px/1.5 var(--mt-mono);color:var(--mt-text);white-space:pre;margin:0 0 8px;padding:8px 10px;overflow-x:auto}.topo-btn{font:11px var(--mt-mono);color:var(--mt-text);background:var(--mt-surface-2);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);cursor:pointer;padding:5px 10px;transition:border-color .15s,color .15s}.topo-btn:hover{border-color:var(--mt-accent);color:var(--mt-accent)}.cfgstudio{flex-direction:column;gap:10px;display:flex}.cfg-toolbar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.cfg-status{font:11px var(--mt-mono);border:1px solid var(--mt-border);border-radius:999px;padding:4px 10px}.cfg-status.is-ok{color:var(--mt-good);border-color:color-mix(in srgb, var(--mt-good) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-good) 10%, transparent)}.cfg-status.is-bad{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-bad) 10%, transparent)}.cfg-select{font:11px var(--mt-mono);color:var(--mt-text);background:var(--mt-surface-2);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);padding:5px 9px}.cfg-save{border-color:color-mix(in srgb, var(--mt-accent) 55%, var(--mt-border));color:var(--mt-accent)}.cfg-msg{font:12px var(--mt-mono);color:var(--mt-text-dim)}.cfg-banner{border-radius:var(--mt-radius-sm);background:color-mix(in srgb, var(--mt-warn) 12%, var(--mt-surface));border:1px solid color-mix(in srgb, var(--mt-warn) 45%, var(--mt-border));flex-wrap:wrap;align-items:center;gap:8px;padding:10px 13px;font-size:12px;display:flex}.cfg-count{font-family:var(--mt-mono);color:var(--mt-warn);font-weight:700}.cfg-warn{color:var(--mt-warn);font-size:11px}.cfg-editor{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:var(--mt-bg);max-height:420px;display:flex;overflow:hidden}.cfg-gutter{text-align:right;color:var(--mt-text-faint);background:var(--mt-surface-2);font:12px/1.5 var(--mt-mono);-webkit-user-select:none;user-select:none;min-width:38px;margin:0;padding:10px 8px;overflow:hidden}.cfg-ta-wrap{flex:1;position:relative}.cfg-hl,.cfg-ta{box-sizing:border-box;font:12px/1.5 var(--mt-mono);white-space:pre;tab-size:2;letter-spacing:normal;border:0;margin:0;padding:10px 12px}.cfg-hl{z-index:0;pointer-events:none;color:var(--mt-text-dim);position:absolute;inset:0;overflow:hidden}.cfg-ta{z-index:1;resize:vertical;color:#0000;width:100%;height:400px;caret-color:var(--mt-accent);background:0 0;outline:none;position:relative}.cfg-ta::selection{background:color-mix(in srgb, var(--mt-accent) 35%, transparent)}.cfg-hl .j-key{color:#7dd3fc}.cfg-hl .j-str{color:#86efac}.cfg-hl .j-num{color:#fcd34d}.cfg-hl .j-bool{color:#d8b4fe}.cfg-hl .j-null{color:#71717a}.cfg-ac{z-index:5;background:var(--mt-surface);border:1px solid var(--mt-accent);border-radius:var(--mt-radius-sm);min-width:150px;max-height:184px;position:absolute;overflow-y:auto;box-shadow:0 8px 24px #00000080}.cfg-ac-item{font:12px var(--mt-mono);color:var(--mt-text-dim);cursor:pointer;padding:5px 11px}.cfg-ac-item.is-sel{background:color-mix(in srgb, var(--mt-accent) 22%, transparent);color:var(--mt-text)}.cfg-chips{flex-wrap:wrap;gap:6px;display:flex}.cfg-chip{font:11px var(--mt-mono);border:1px solid var(--mt-border);border-radius:999px;padding:4px 10px}.cfg-chip.is-ok{color:var(--mt-good);border-color:color-mix(in srgb, var(--mt-good) 45%, var(--mt-border))}.cfg-chip.is-bad{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border))}.cfg-errors{flex-direction:column;gap:3px;display:flex}.cfg-err{font:11px var(--mt-mono);color:var(--mt-bad)}.cfg-err code{color:var(--mt-warn)}.cfg-preview{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);overflow:hidden}.cfg-preview__hd{background:var(--mt-surface-2);align-items:center;gap:8px;padding:9px 13px;font-size:12px;display:flex}.cfg-diff{max-height:320px;font:11px/1.5 var(--mt-mono);color:var(--mt-text-dim);background:var(--mt-bg);margin:0;padding:10px 12px;overflow:auto}.cfg-diff .d-add{color:var(--mt-good)}.cfg-diff .d-del{color:var(--mt-bad)}.cfg-diff .d-hunk{color:var(--mt-accent)}.cap-idle{font-size:13px;line-height:1.5}.cap-idle code{color:var(--mt-accent);font-family:var(--mt-mono)}.cap{flex-direction:column;gap:12px;display:flex}.cap-bar{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.cap-dot{background:var(--mt-text-faint);border-radius:999px;width:9px;height:9px}.cap-dot.is-on{background:var(--mt-good);box-shadow:0 0 0 4px color-mix(in srgb, var(--mt-good) 22%, transparent);animation:1.6s ease-out infinite feed-pulse}.cap-cols{grid-template-columns:220px 1fr;gap:12px;display:grid}@media (width<=720px){.cap-cols{grid-template-columns:1fr}}.cap-side{flex-direction:column;gap:5px;display:flex}.cap-h{text-transform:uppercase;letter-spacing:.05em;color:var(--mt-text-dim);font-size:11px}.cap-pbar{font:11px var(--mt-mono);grid-template-columns:52px 1fr 34px;align-items:center;gap:6px;display:grid}.cap-ptrack{background:var(--mt-surface-2);border-radius:999px;height:8px;overflow:hidden}.cap-ptrack i{border-radius:999px;height:100%;display:block}.cap-pn{text-align:right;color:var(--mt-text-dim)}.cap-talker{font:11px var(--mt-mono);color:var(--mt-text-dim);justify-content:space-between;display:flex}.cap-talker b{color:var(--mt-text)}.cap-list{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:var(--mt-bg);height:360px;font:11px/1.6 var(--mt-mono);overflow-y:auto}.cap-row{white-space:nowrap;grid-template-columns:90px 64px 48px 1fr;gap:8px;padding:1px 10px;display:grid;overflow:hidden}.cap-row:nth-child(odd){background:color-mix(in srgb, var(--mt-surface) 40%, transparent)}.cap-tt{color:var(--mt-text-faint)}.cap-proto{font-weight:600}.cap-len{color:var(--mt-text-dim);text-align:right}.cap-info{color:var(--mt-text);text-overflow:ellipsis;overflow:hidden}
31
+ @import "https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=DM+Mono:wght@400;500&display=swap";:root{--mt-bg:var(--color-background-primary,#0b0d10);--mt-surface:var(--color-background-secondary,#14171c);--mt-surface-2:var(--color-background-tertiary,#1b1f26);--mt-border:var(--color-border-primary,#262b33);--mt-text:var(--color-text-primary,#e8eaed);--mt-text-dim:var(--color-text-secondary,#9aa3af);--mt-text-faint:var(--color-text-tertiary,#6b7280);--mt-accent:var(--color-accent-primary,#6ea8fe);--mt-good:#34d399;--mt-warn:#fbbf24;--mt-bad:#f87171;--mt-radius:var(--border-radius-md,14px);--mt-radius-sm:var(--border-radius-sm,9px);--mt-mono:var(--font-mono,ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace);--mt-sans:var(--font-sans,system-ui, -apple-system, "Segoe UI", sans-serif);--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}*{box-sizing:border-box}body{background:var(--mt-bg);color:var(--mt-text);font-family:var(--mt-sans);-webkit-font-smoothing:antialiased;margin:0;padding:16px;font-size:13px;line-height:1.5}.app{gap:14px;max-width:960px;margin:0 auto;display:grid}.hd{flex-wrap:wrap;align-items:center;gap:12px;display:flex}.hd__dot{background:var(--mt-good);width:9px;height:9px;box-shadow:0 0 0 4px color-mix(in srgb, var(--mt-good) 22%, transparent);border-radius:50%}.hd__title{letter-spacing:-.01em;margin:0;font-size:17px;font-weight:650}.hd__sub{color:var(--mt-text-dim);font-family:var(--mt-mono);margin:0;font-size:12px}.hd__spacer{flex:1}.pill{border:1px solid var(--mt-border);background:var(--mt-surface);color:var(--mt-text-dim);font-family:var(--mt-mono);border-radius:999px;align-items:center;gap:6px;padding:3px 9px;font-size:11px;display:inline-flex}.pill b{color:var(--mt-text);font-weight:600}.grid{grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:12px;display:grid}.card{background:linear-gradient(180deg, var(--mt-surface), var(--mt-surface-2));border:1px solid var(--mt-border);border-radius:var(--mt-radius);padding:14px}.card__label{color:var(--mt-text-dim);text-transform:uppercase;letter-spacing:.06em;margin:0 0 8px;font-size:11px}.card__value{font-family:var(--mt-mono);letter-spacing:-.01em;font-size:20px;font-weight:600}.toolbar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.toolbar .grow{flex:1;min-width:140px}.search{appearance:none;border:1px solid var(--mt-border);background:var(--mt-surface);width:100%;color:var(--mt-text);font:inherit;border-radius:var(--mt-radius-sm);padding:7px 11px;font-size:13px}.search:focus{border-color:var(--mt-accent);outline:none}.search::placeholder{color:var(--mt-text-faint)}.btn{appearance:none;border:1px solid var(--mt-border);background:var(--mt-surface);color:var(--mt-text);font:inherit;border-radius:var(--mt-radius-sm);cursor:pointer;white-space:nowrap;padding:6px 11px;font-size:12px;transition:border-color .15s,background .15s}.btn:disabled{opacity:.5;cursor:default}.chip{border:1px solid var(--mt-border);background:var(--mt-surface);color:var(--mt-text-dim);font-family:var(--mt-mono);border-radius:999px;align-items:center;gap:4px;padding:1px 7px;font-size:11px;display:inline-flex}.chip.is-good{color:var(--mt-good);border-color:color-mix(in srgb, var(--mt-good) 45%, var(--mt-border))}.chip.is-warn{color:var(--mt-warn);border-color:color-mix(in srgb, var(--mt-warn) 45%, var(--mt-border))}.chip.is-bad{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border))}.tablewrap{border:1px solid var(--mt-border);border-radius:var(--mt-radius);max-height:70vh;overflow:auto}table.tbl{border-collapse:collapse;width:100%;font-size:12px}table.tbl th,table.tbl td{text-align:left;border-bottom:1px solid color-mix(in srgb, var(--mt-border) 55%, transparent);white-space:nowrap;font-family:var(--mt-mono);padding:7px 12px}table.tbl thead th{z-index:1;background:var(--mt-surface-2);color:var(--mt-text-dim);cursor:pointer;-webkit-user-select:none;user-select:none;font-weight:600;position:sticky;top:0}table.tbl thead th:hover{color:var(--mt-text)}table.tbl thead th .arrow{color:var(--mt-accent);margin-left:4px}table.tbl tbody tr{cursor:pointer}table.tbl tbody tr:hover{background:color-mix(in srgb, var(--mt-accent) 8%, transparent)}table.tbl tbody tr.is-disabled td,table.tbl td.col-num{color:var(--mt-text-faint)}.kv__body{border:1px solid var(--mt-border);border-radius:var(--mt-radius);grid-template-columns:minmax(120px,.4fr) 1fr;display:grid;overflow:hidden}.kv__body>div{border-bottom:1px solid color-mix(in srgb, var(--mt-border) 55%, transparent);overflow-wrap:anywhere;min-width:0;font-size:12px;font-family:var(--mt-mono);padding:7px 14px}.kv__k{color:var(--mt-text-dim);background:color-mix(in srgb, var(--mt-surface) 60%, transparent)}.kv__v{color:var(--mt-text)}.drawer{border:1px solid var(--mt-accent);border-radius:var(--mt-radius);background:var(--mt-surface);gap:10px;padding:12px;display:grid}.drawer__hd{align-items:center;gap:8px;display:flex}.drawer__hd b{font-family:var(--mt-mono)}pre.raw{border:1px solid var(--mt-border);border-radius:var(--mt-radius);background:var(--mt-surface);color:var(--mt-text);font-family:var(--mt-mono);white-space:pre;max-height:70vh;margin:0;padding:14px;font-size:12px;overflow:auto}.foot{color:var(--mt-text-faint);font-size:11px;font-family:var(--mt-mono);flex-wrap:wrap;align-items:center;gap:10px;display:flex}.foot .grow{flex:1}.skeleton,.empty{color:var(--mt-text-faint);text-align:center;padding:40px 0}:root{--mt-sans:"Space Grotesk", system-ui, -apple-system, "Segoe UI", sans-serif;--mt-mono:"DM Mono", ui-monospace, "SF Mono", "JetBrains Mono", Menlo, monospace;--mt-bg:#09090b;--mt-surface:#18181b;--mt-surface-2:#27272a;--mt-border:#27272a;--mt-text:#fafafa;--mt-text-dim:#a1a1aa;--mt-text-faint:#71717a;--mt-accent:#e4e4e7;--mt-good:#d4d4d8;--mt-warn:#a1a1aa;--mt-bad:#f87171;--mt-radius:12px;--mt-radius-sm:8px;--sky:#d4d4d8;--teal:#e4e4e7;--cyan:#a1a1aa;--lime:#71717a;--coral:#f87171;--amber:#a1a1aa;--grad:linear-gradient(115deg, #e4e4e7, #a1a1aa 50%, #52525b);--ease:cubic-bezier(.22, 1, .36, 1);--glass:#18181b8c}html,body{background:var(--mt-bg);color:var(--mt-text);font-family:var(--mt-sans);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;margin:0;padding:0}body:before{content:"";z-index:-2;background:radial-gradient(58% 42% at 12% -6%, color-mix(in srgb, var(--teal) 16%, transparent), transparent 70%), radial-gradient(50% 40% at 100% 0%, color-mix(in srgb, var(--sky) 13%, transparent), transparent 68%), radial-gradient(60% 50% at 84% 108%, color-mix(in srgb, var(--lime) 8%, transparent), transparent 70%);pointer-events:none;position:fixed;inset:0}body:after{content:"";z-index:-2;background-image:linear-gradient(color-mix(in srgb, var(--mt-border) 70%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, var(--mt-border) 70%, transparent) 1px, transparent 1px);opacity:.5;pointer-events:none;background-size:46px 46px;position:fixed;inset:0;-webkit-mask-image:radial-gradient(circle at 50% 0,#000,#0000 75%);mask-image:radial-gradient(circle at 50% 0,#000,#0000 75%)}.shell{grid-template-columns:250px minmax(0,1fr);align-items:start;min-height:100vh;display:grid}.nav{border-right:1px solid var(--mt-border);background:color-mix(in srgb, var(--mt-surface) 55%, transparent);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);flex-direction:column;gap:10px;height:100vh;padding:18px 14px;display:flex;position:sticky;top:0}.nav__brand{align-items:center;gap:11px;padding:6px 8px 14px;display:flex}.nav__mark{background:var(--grad);width:38px;height:38px;box-shadow:0 8px 22px color-mix(in srgb, var(--teal) 38%, transparent);border-radius:11px;flex:none;place-items:center;display:grid}.nav__mark svg{width:22px;height:22px;display:block}.nav__brandtext{flex-direction:column;min-width:0;line-height:1.1;display:flex}.nav__brandtext b{letter-spacing:-.02em;font-size:15px;font-weight:700}.nav__brandtext small{font-family:var(--mt-mono);letter-spacing:.16em;text-transform:uppercase;color:var(--mt-text-dim);margin-top:3px;font-size:10px}.nav__items{flex-direction:column;flex:1;gap:3px;display:flex}.nav__item{color:var(--mt-text-dim);font-family:var(--mt-sans);cursor:pointer;text-align:left;transition:background .18s var(--ease), color .18s, border-color .18s, transform .18s var(--ease);background:0 0;border:1px solid #0000;border-radius:11px;align-items:center;gap:11px;padding:10px 12px;font-size:13px;font-weight:500;display:flex}.nav__item svg{opacity:.85;flex:none;width:18px;height:18px}.nav__item:hover{color:var(--mt-text);background:color-mix(in srgb, var(--teal) 9%, transparent)}.nav__item.is-active{color:var(--mt-text);border-color:color-mix(in srgb, var(--teal) 32%, var(--mt-border));background:linear-gradient(100deg, color-mix(in srgb, var(--teal) 18%, transparent), color-mix(in srgb, var(--sky) 7%, transparent))}.nav__item.is-active svg{opacity:1;color:var(--teal)}.nav__badge{font-family:var(--mt-mono);background:color-mix(in srgb, var(--teal) 22%, transparent);color:var(--mt-text);border-radius:999px;margin-left:auto;padding:1px 7px;font-size:10px}.nav__foot{border-top:1px solid var(--mt-border);flex-direction:column;gap:8px;padding:10px 8px 2px;display:flex}.main{align-content:start;gap:18px;min-width:0;padding:22px clamp(16px,3vw,44px) 64px;display:grid}.topline{flex-wrap:wrap;align-items:center;gap:14px;display:flex}.topline__txt h1{letter-spacing:-.025em;background:linear-gradient(100deg, var(--mt-text), color-mix(in srgb, var(--teal) 65%, var(--mt-text)));-webkit-text-fill-color:transparent;-webkit-background-clip:text;background-clip:text;margin:0;font-size:clamp(19px,2.4vw,26px);font-weight:700}.topline__txt small{color:var(--mt-text-dim);font-family:var(--mt-mono);margin-top:3px;font-size:12px;display:block}.topline__spacer{flex:1;min-width:8px}.view{align-content:start;gap:18px;display:grid}.bento{grid-template-columns:repeat(6,1fr);gap:18px;display:grid}.bento .panel{margin:0}.b-series{grid-column:span 4}.b-risk{grid-column:span 2}.b-tools,.b-status,.b-device,.b-errors{grid-column:span 3}.b-risk,.b-status{flex-direction:column;display:flex}@media (width<=1040px){.bento{grid-template-columns:repeat(2,1fr)}.b-series,.b-risk,.b-tools,.b-status,.b-device,.b-errors{grid-column:span 2}}@media (width<=900px){.shell{grid-template-columns:1fr}.nav{border-right:0;border-bottom:1px solid var(--mt-border);z-index:5;flex-direction:row;align-items:center;gap:8px;height:auto;padding:10px 12px;position:sticky;top:0;overflow-x:auto}.nav__brand{padding:0 6px 0 2px}.nav__brandtext{display:none}.nav__items{flex-direction:row;flex:1;gap:4px}.nav__item span{display:none}.nav__item{padding:9px 11px}.nav__foot{border-top:0;flex-direction:row;align-items:center;padding:0}.nav__foot .muted{display:none}}@media (width<=560px){.nav__brand{display:none}}@media (prefers-reduced-motion:no-preference){.js-motion .reveal{opacity:0}}.hero__live{border:1px solid var(--mt-border);background:var(--glass);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);font-family:var(--mt-mono);letter-spacing:.04em;text-transform:uppercase;color:var(--mt-text-dim);border-radius:999px;align-items:center;gap:7px;padding:7px 13px;font-size:11px;display:inline-flex}.hero__live .dot{background:var(--mt-bad);border-radius:50%;width:8px;height:8px;transition:background .3s}.hero__live.is-on{color:var(--mt-text);border-color:color-mix(in srgb, var(--mt-good) 40%, var(--mt-border))}.hero__live.is-on .dot{background:var(--mt-good);box-shadow:0 0 0 4px color-mix(in srgb, var(--mt-good) 22%, transparent);animation:1.8s ease-out infinite hero-pulse}@keyframes hero-pulse{0%{box-shadow:0 0 0 0 color-mix(in srgb, var(--mt-good) 55%, transparent)}70%{box-shadow:0 0 0 9px color-mix(in srgb, var(--mt-good) 0%, transparent)}to{box-shadow:0 0 0 0 color-mix(in srgb, var(--mt-good) 0%, transparent)}}.panel>h2,.sheet__hd h2{font-family:var(--mt-sans)}.cards{grid-template-columns:repeat(2,1fr);gap:12px;display:grid}@media (width>=560px){.cards{grid-template-columns:repeat(4,1fr)}}@media (width>=1040px){.cards{grid-template-columns:repeat(8,1fr)}}.stat{background:linear-gradient(180deg, var(--mt-surface), color-mix(in srgb, var(--mt-surface-2) 80%, transparent));border:1px solid var(--mt-border);border-radius:var(--mt-radius);transition:transform .3s var(--ease), border-color .3s var(--ease);padding:14px 15px;position:relative;overflow:hidden}.stat:before{content:"";background:var(--grad);opacity:0;height:2px;transition:opacity .3s var(--ease);position:absolute;inset:0 0 auto}.stat:hover{border-color:color-mix(in srgb, var(--teal) 38%, var(--mt-border));transform:translateY(-3px)}.stat:hover:before{opacity:1}.stat .k{color:var(--mt-text-dim);text-transform:uppercase;letter-spacing:.08em;margin:0 0 7px;font-size:10px}.stat .v{font-family:var(--mt-mono);letter-spacing:-.02em;font-variant-numeric:tabular-nums;font-size:23px;font-weight:500}.stat .v small{color:var(--mt-text-dim);font-size:12px;font-weight:400}.stat.is-bad .v{color:var(--mt-bad)}.stat.is-warn .v{color:var(--mt-warn)}.stat.is-good .v{color:var(--mt-good)}.panel{background:var(--glass);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid var(--mt-border);border-radius:var(--mt-radius);padding:18px;position:relative;box-shadow:inset 0 1px #ffffff0a}.panel>h2{color:var(--mt-text-dim);text-transform:uppercase;letter-spacing:.07em;align-items:center;gap:9px;margin:0 0 14px;font-size:12px;font-weight:600;display:inline-flex}.panel>h2:before,.sheet__hd h2:first-child:before{content:"";background:var(--grad);border-radius:2px;flex:none;width:3px;height:13px}.cols{grid-template-columns:1.4fr 1fr;gap:18px;display:grid}@media (width<=820px){.cols{grid-template-columns:1fr}}.cols-3{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:18px;display:grid}.cols-3 .panel{flex-direction:column;display:flex}.hbar{gap:9px;display:grid}.hbar__row{grid-template-columns:minmax(90px,.9fr) 2fr auto;align-items:center;gap:10px;font-size:12px;display:grid}.hbar__label{font-family:var(--mt-mono);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.hbar__track{background:color-mix(in srgb, var(--mt-surface-2) 90%, transparent);border-radius:999px;height:8px;overflow:hidden}.hbar__fill{background:var(--grad);height:100%;box-shadow:0 0 10px color-mix(in srgb, var(--teal) 45%, transparent);border-radius:999px}.hbar__val{font-family:var(--mt-mono);color:var(--mt-text-dim);text-align:right;font-variant-numeric:tabular-nums;font-size:11px}.legend{font-family:var(--mt-mono);color:var(--mt-text-dim);flex-wrap:wrap;gap:12px;margin-top:12px;font-size:11px;display:flex}.legend span{align-items:center;gap:5px;display:inline-flex}.legend i{border-radius:2px;width:9px;height:9px;display:inline-block}.risk{font-family:var(--mt-mono);border:1px solid var(--mt-border);text-transform:uppercase;letter-spacing:.04em;border-radius:999px;padding:1px 8px;font-size:10px;display:inline-block}.risk-READ{color:var(--mt-good);border-color:color-mix(in srgb, var(--mt-good) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-good) 10%, transparent)}.risk-WRITE,.risk-WRITE_IDEMPOTENT{color:var(--mt-warn);border-color:color-mix(in srgb, var(--mt-warn) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-warn) 10%, transparent)}.risk-DESTRUCTIVE,.risk-DANGEROUS{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-bad) 10%, transparent)}.feed{border-collapse:collapse;width:100%;font-size:12px}.feed th,.feed td{text-align:left;border-bottom:1px solid color-mix(in srgb, var(--mt-border) 55%, transparent);white-space:nowrap;font-family:var(--mt-mono);padding:7px 11px}.feed thead th{background:color-mix(in srgb, var(--mt-surface-2) 96%, transparent);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);color:var(--mt-text-dim);text-transform:uppercase;letter-spacing:.05em;z-index:1;font-size:10px;font-weight:600;position:sticky;top:0}.feed tbody tr{cursor:pointer;transition:background .12s}.feed tbody tr:hover{background:color-mix(in srgb, var(--mt-accent) 9%, transparent)}.feed tbody tr.is-err{background:color-mix(in srgb, var(--mt-bad) 9%, transparent)}.feed tbody tr.is-selected{background:color-mix(in srgb, var(--mt-accent) 16%, transparent)}.feed td.preview{white-space:nowrap;text-overflow:ellipsis;max-width:320px;color:var(--mt-text-dim);overflow:hidden}.feed td.num{text-align:right;color:var(--mt-text-dim);font-variant-numeric:tabular-nums}.status-ok{color:var(--mt-good)}.status-err{color:var(--mt-bad)}.feedwrap{border:1px solid var(--mt-border);border-radius:var(--mt-radius);max-height:60vh;overflow:auto}.feed-empty{border:1px dashed var(--mt-border);border-radius:var(--mt-radius);text-align:center;justify-items:center;gap:8px;padding:54px 16px;display:grid}.feed-empty__icon{opacity:.8;font-size:28px}.feed-empty__title{color:var(--mt-text);margin:0;font-weight:600}.feed-empty__sub{max-width:460px;color:var(--mt-text-faint);margin:0;font-size:12px}.feed-empty .btn{margin-top:6px}.feed-empty__pulse{background:var(--mt-text-faint);border-radius:50%;width:12px;height:12px}.feed-empty__pulse.is-on{background:var(--mt-good);animation:1.6s ease-out infinite feed-pulse}@keyframes feed-pulse{0%{box-shadow:0 0 0 0 color-mix(in srgb, var(--mt-good) 55%, transparent)}70%{box-shadow:0 0 0 12px color-mix(in srgb, var(--mt-good) 0%, transparent)}to{box-shadow:0 0 0 0 color-mix(in srgb, var(--mt-good) 0%, transparent)}}.conn{display:block;overflow:visible}.conn-grid{fill:none;stroke:var(--mt-border);stroke-opacity:.7;stroke-dasharray:2 7}.conn-sonar{fill:none;stroke:var(--mt-accent);stroke-width:1.5px;opacity:0;animation:3.3s ease-out infinite conn-sonar}@keyframes conn-sonar{0%{r:30px;opacity:.5}to{r:150px;opacity:0}}.conn-link{fill:none;stroke-width:1.6px;opacity:.38}.conn-flow{fill:none;stroke-width:2.4px;stroke-linecap:round;stroke-dasharray:2 13;opacity:.9;filter:drop-shadow(0 0 4px);animation:1.05s linear infinite conn-flow}@keyframes conn-flow{to{stroke-dashoffset:-60px}}.conn-packet{filter:drop-shadow(0 0 6px var(--teal))}.conn-hub-glow{fill:var(--mt-accent);opacity:.22;filter:blur(11px)}.conn-hub-ring{fill:none;stroke:var(--mt-accent);stroke-width:1.4px;stroke-dasharray:4 9;opacity:.7;transform-box:fill-box;transform-origin:50%;animation:15s linear infinite conn-rot}@keyframes conn-rot{to{transform:rotate(360deg)}}.conn-node-halo{fill:none;stroke-width:2px;transform-box:fill-box;transform-origin:50%;animation:2.4s ease-in-out infinite conn-halo}@keyframes conn-halo{0%,to{opacity:.45;transform:scale(1)}50%{opacity:0;transform:scale(1.55)}}.conn-blink{animation:1.6s ease-in-out infinite conn-blink}@keyframes conn-blink{0%,to{opacity:1}50%{opacity:.35}}.conn-node text{pointer-events:none}@media (prefers-reduced-motion:reduce){.conn-sonar,.conn-flow,.conn-hub-ring,.conn-node-halo,.conn-blink,.conn-packet animateMotion{animation:none}.conn-sonar{display:none}}.dev-grid{grid-template-columns:1fr;align-content:start;gap:12px;display:grid}@media (width>=600px) and (width<=819px){.dev-grid{grid-template-columns:1fr 1fr}}.dev-toolbar{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.dev-filters{border:1px solid var(--mt-border);background:color-mix(in srgb, var(--mt-surface-2) 70%, transparent);border-radius:999px;gap:4px;padding:3px;display:inline-flex}.dev-fbtn{appearance:none;color:var(--mt-text-dim);font:11px var(--mt-mono);cursor:pointer;white-space:nowrap;background:0 0;border:0;border-radius:999px;padding:5px 12px;transition:background .18s,color .18s}.dev-fbtn:hover{color:var(--mt-text)}.dev-fbtn.is-active{background:var(--grad);color:var(--mt-bg);font-weight:600}.dev-grid-wide{grid-template-columns:repeat(auto-fill,minmax(280px,1fr));align-content:start;gap:14px;display:grid}.dev-collapse{border:1px solid var(--mt-border);border-radius:var(--mt-radius);background:var(--glass);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);padding:6px 18px 18px}.dev-collapse>summary{cursor:pointer;text-transform:uppercase;letter-spacing:.07em;color:var(--mt-text-dim);align-items:center;gap:8px;padding:12px 0;font-size:12px;font-weight:600;list-style:none;display:flex}.dev-collapse>summary::-webkit-details-marker{display:none}.dev-collapse>summary:before{content:"▸";transition:transform .2s var(--ease);color:var(--teal);display:inline-block}.dev-collapse[open]>summary:before{transform:rotate(90deg)}.dev-card .dot--status{width:7px;height:7px}.dev-card{transition:transform .3s var(--ease), border-color .3s var(--ease);gap:8px;padding:14px 15px;display:grid}.dev-card:hover{border-color:color-mix(in srgb, var(--teal) 35%, var(--mt-border));transform:translateY(-2px)}.dev-card__top{align-items:center;gap:8px;display:flex}.dev-card__name{font-family:var(--mt-mono);font-size:13px;font-weight:500}.dev-card .dot{border-radius:50%;flex:none;width:9px;height:9px}.dev-card__meta{font-family:var(--mt-mono);color:var(--mt-text-dim);grid-template-columns:auto 1fr;gap:3px 12px;font-size:11px;display:grid}.dev-card__meta b{color:var(--mt-text);overflow-wrap:anywhere;font-weight:500}details.cfg>summary{cursor:pointer;color:var(--mt-text-dim);font-family:var(--mt-mono);-webkit-user-select:none;user-select:none;padding:4px 0;font-size:12px}.badge{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.06em;color:var(--mt-bg);background:var(--grad);border-radius:999px;padding:1px 7px;font-size:9px;font-weight:600}.overlay{-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px);z-index:10;background:#0000009e;justify-content:flex-end;display:flex;position:fixed;inset:0}.sheet{background:color-mix(in srgb, var(--mt-surface) 96%, var(--mt-bg));border-left:1px solid color-mix(in srgb, var(--teal) 28%, var(--mt-border));align-content:start;gap:12px;width:min(580px,100%);height:100%;padding:18px;display:grid;overflow:auto;box-shadow:-24px 0 60px #00000080}.sheet__hd{align-items:center;gap:10px;display:flex}.sheet__hd h3{font-family:var(--mt-mono);margin:0;font-size:15px}pre.body{background:var(--mt-bg);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);font-family:var(--mt-mono);white-space:pre-wrap;word-break:break-word;max-height:40vh;margin:0;padding:12px;font-size:12px;overflow:auto}pre.body.json{color:#a1a1aa}pre.body.json .j-key{color:#7dd3fc}pre.body.json .j-str{color:#86efac}pre.body.json .j-num{color:#fcd34d}pre.body.json .j-bool{color:#d8b4fe}pre.body.json .j-null{color:#71717a}.muted{color:var(--mt-text-faint);font-family:var(--mt-mono);font-size:11px}.btn-danger{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-bad) 12%, transparent)}.btn-danger:hover{background:color-mix(in srgb, var(--mt-bad) 22%, transparent);border-color:var(--mt-bad)}.btn:hover{border-color:color-mix(in srgb, var(--teal) 55%, var(--mt-border));background:color-mix(in srgb, var(--teal) 10%, var(--mt-surface-2))}.btn.is-active{border-color:var(--teal);color:var(--teal)}.conn-legend{font-family:var(--mt-mono);color:var(--mt-text-dim);flex-wrap:wrap;justify-content:center;gap:16px;margin-top:8px;font-size:11px;display:flex}.conn-legend span{align-items:center;gap:6px;display:inline-flex}.conn-legend i{border-radius:3px;width:10px;height:10px;display:inline-block}.conn-chevron{fill:none;stroke-width:1.6px;stroke-linecap:round;stroke-linejoin:round;opacity:.85}.conn-errrow{cursor:pointer;border-radius:6px}.conn-errrow:hover{background:color-mix(in srgb, var(--mt-bad) 12%, transparent)}@media (prefers-reduced-motion:reduce){.conn-chevron{opacity:.6}}.health-grid{grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:14px;display:grid}.health-card{flex-direction:column;gap:10px;padding:16px;display:flex}.health-card--na{justify-content:center;gap:6px;min-height:120px}.health-card__hd{align-items:center;gap:8px;display:flex}.health-card__sub{font-family:var(--mt-mono);margin-top:-4px;font-size:11px}.health-card__gauges{justify-content:space-around;gap:14px;display:flex}.gauge{flex-direction:column;align-items:center;gap:2px;display:flex}.gauge__label{font-family:var(--mt-mono);letter-spacing:.06em;color:var(--mt-text-dim);font-size:10px}.health-card__charts{gap:8px;display:grid}.health-chart{gap:2px;display:grid}.health-chart__k{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.05em;color:var(--mt-text-dim);font-size:10px}.spark{background:color-mix(in srgb, var(--mt-surface-2) 60%, transparent);border-radius:6px;display:block}.spark--empty{font-family:var(--mt-mono);color:var(--mt-text-faint);text-align:center;padding:12px 6px;font-size:11px}.health-card__foot{font-family:var(--mt-mono);font-size:11px}.topo{flex-direction:column;gap:10px;display:flex}.topo svg{background:radial-gradient(circle at 50% 45%, color-mix(in srgb, var(--mt-accent) 8%, transparent), transparent 60%);border-radius:var(--mt-radius-sm);width:100%;display:block}.topo-edge{stroke:color-mix(in srgb, var(--mt-text-faint) 55%, transparent);stroke-width:1.4px}.topo-edge.is-dashed{stroke:color-mix(in srgb, var(--mt-accent) 60%, transparent);stroke-dasharray:4 4}.topo-node{cursor:pointer}.topo-node rect{fill:var(--mt-surface-2);stroke-width:1.5px;transition:filter .15s}.topo-node.is-neighbor rect{fill:color-mix(in srgb, var(--mt-surface) 70%, transparent);stroke-dasharray:4 3}.topo-node.is-onboard rect{stroke-dasharray:4 3}.topo-node:hover rect,.topo-node.is-picked rect{filter:drop-shadow(0 0 6px color-mix(in srgb, var(--mt-accent) 55%, transparent))}.topo-node.is-picked rect{stroke-width:2.2px}.topo-label{fill:var(--mt-text);font:600 12px var(--mt-mono)}.topo-sub{fill:var(--mt-text-dim);font:10px var(--mt-mono)}.topo-bar-bg{fill:color-mix(in srgb, var(--mt-text-faint) 35%, transparent)}.topo-foot{flex-wrap:wrap;justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.topo-foot .legend{margin-top:0}.topo-pop{background:var(--mt-surface);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);flex:1;min-width:280px;max-width:460px;padding:12px}.topo-pop__hd{align-items:center;gap:8px;margin-bottom:6px;display:flex}.topo-pop__hd .muted{font-size:11px}.topo-stub{background:var(--mt-bg);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);font:11px/1.5 var(--mt-mono);color:var(--mt-text);white-space:pre;margin:0 0 8px;padding:8px 10px;overflow-x:auto}.topo-btn{font:11px var(--mt-mono);color:var(--mt-text);background:var(--mt-surface-2);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);cursor:pointer;padding:5px 10px;transition:border-color .15s,color .15s}.topo-btn:hover{border-color:var(--mt-accent);color:var(--mt-accent)}.cfgstudio{flex-direction:column;gap:10px;display:flex}.cfg-toolbar{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.cfg-status{font:11px var(--mt-mono);border:1px solid var(--mt-border);border-radius:999px;padding:4px 10px}.cfg-status.is-ok{color:var(--mt-good);border-color:color-mix(in srgb, var(--mt-good) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-good) 10%, transparent)}.cfg-status.is-bad{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border));background:color-mix(in srgb, var(--mt-bad) 10%, transparent)}.cfg-select{font:11px var(--mt-mono);color:var(--mt-text);background:var(--mt-surface-2);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);padding:5px 9px}.cfg-save{border-color:color-mix(in srgb, var(--mt-accent) 55%, var(--mt-border));color:var(--mt-accent)}.cfg-msg{font:12px var(--mt-mono);color:var(--mt-text-dim)}.cfg-banner{border-radius:var(--mt-radius-sm);background:color-mix(in srgb, var(--mt-warn) 12%, var(--mt-surface));border:1px solid color-mix(in srgb, var(--mt-warn) 45%, var(--mt-border));flex-wrap:wrap;align-items:center;gap:8px;padding:10px 13px;font-size:12px;display:flex}.cfg-count{font-family:var(--mt-mono);color:var(--mt-warn);font-weight:700}.cfg-warn{color:var(--mt-warn);font-size:11px}.cfg-editor{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:var(--mt-bg);max-height:420px;display:flex;overflow:hidden}.cfg-gutter{text-align:right;color:var(--mt-text-faint);background:var(--mt-surface-2);font:12px/1.5 var(--mt-mono);-webkit-user-select:none;user-select:none;min-width:38px;margin:0;padding:10px 8px;overflow:hidden}.cfg-ta-wrap{flex:1;position:relative}.cfg-hl,.cfg-ta{box-sizing:border-box;font:12px/1.5 var(--mt-mono);white-space:pre;tab-size:2;letter-spacing:normal;border:0;margin:0;padding:10px 12px}.cfg-hl{z-index:0;pointer-events:none;color:var(--mt-text-dim);position:absolute;inset:0;overflow:hidden}.cfg-ta{z-index:1;resize:vertical;color:#0000;width:100%;height:400px;caret-color:var(--mt-accent);background:0 0;outline:none;position:relative}.cfg-ta::selection{background:color-mix(in srgb, var(--mt-accent) 35%, transparent)}.cfg-hl .j-key{color:#7dd3fc}.cfg-hl .j-str{color:#86efac}.cfg-hl .j-num{color:#fcd34d}.cfg-hl .j-bool{color:#d8b4fe}.cfg-hl .j-null{color:#71717a}.cfg-ac{z-index:5;background:var(--mt-surface);border:1px solid var(--mt-accent);border-radius:var(--mt-radius-sm);min-width:150px;max-height:184px;position:absolute;overflow-y:auto;box-shadow:0 8px 24px #00000080}.cfg-ac-item{font:12px var(--mt-mono);color:var(--mt-text-dim);cursor:pointer;padding:5px 11px}.cfg-ac-item.is-sel{background:color-mix(in srgb, var(--mt-accent) 22%, transparent);color:var(--mt-text)}.cfg-chips{flex-wrap:wrap;gap:6px;display:flex}.cfg-chip{font:11px var(--mt-mono);border:1px solid var(--mt-border);border-radius:999px;padding:4px 10px}.cfg-chip.is-ok{color:var(--mt-good);border-color:color-mix(in srgb, var(--mt-good) 45%, var(--mt-border))}.cfg-chip.is-bad{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border))}.cfg-errors{flex-direction:column;gap:3px;display:flex}.cfg-err{font:11px var(--mt-mono);color:var(--mt-bad)}.cfg-err code{color:var(--mt-warn)}.cfg-preview{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);overflow:hidden}.cfg-preview__hd{background:var(--mt-surface-2);align-items:center;gap:8px;padding:9px 13px;font-size:12px;display:flex}.cfg-diff{max-height:320px;font:11px/1.5 var(--mt-mono);color:var(--mt-text-dim);background:var(--mt-bg);margin:0;padding:10px 12px;overflow:auto}.cfg-diff .d-add{color:var(--mt-good)}.cfg-diff .d-del{color:var(--mt-bad)}.cfg-diff .d-hunk{color:var(--mt-accent)}.cap-idle{font-size:13px;line-height:1.5}.cap-idle code{color:var(--mt-accent);font-family:var(--mt-mono)}.cap{flex-direction:column;gap:12px;display:flex}.cap-bar{flex-wrap:wrap;align-items:center;gap:10px;display:flex}.cap-dot{background:var(--mt-text-faint);border-radius:999px;width:9px;height:9px}.cap-dot.is-on{background:var(--mt-good);box-shadow:0 0 0 4px color-mix(in srgb, var(--mt-good) 22%, transparent);animation:1.6s ease-out infinite feed-pulse}.cap-cols{grid-template-columns:220px 1fr;gap:12px;display:grid}@media (width<=720px){.cap-cols{grid-template-columns:1fr}}.cap-side{flex-direction:column;gap:5px;display:flex}.cap-h{text-transform:uppercase;letter-spacing:.05em;color:var(--mt-text-dim);font-size:11px}.cap-pbar{font:11px var(--mt-mono);grid-template-columns:52px 1fr 34px;align-items:center;gap:6px;display:grid}.cap-ptrack{background:var(--mt-surface-2);border-radius:999px;height:8px;overflow:hidden}.cap-ptrack i{border-radius:999px;height:100%;display:block}.cap-pn{text-align:right;color:var(--mt-text-dim)}.cap-talker{font:11px var(--mt-mono);color:var(--mt-text-dim);justify-content:space-between;display:flex}.cap-talker b{color:var(--mt-text)}.cap-list{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:var(--mt-bg);height:360px;font:11px/1.6 var(--mt-mono);overflow-y:auto}.cap-row{white-space:nowrap;grid-template-columns:90px 64px 48px 1fr;gap:8px;padding:1px 10px;display:grid;overflow:hidden}.cap-row:nth-child(odd){background:color-mix(in srgb, var(--mt-surface) 40%, transparent)}.cap-tt{color:var(--mt-text-faint)}.cap-proto{font-weight:600}.cap-len{color:var(--mt-text-dim);text-align:right}.cap-info{color:var(--mt-text);text-overflow:ellipsis;overflow:hidden}.plan-input{resize:vertical;box-sizing:border-box;border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:var(--mt-bg);width:100%;min-height:160px;color:var(--mt-text);font:12px/1.6 var(--mt-mono);outline:none;padding:12px 14px}.plan-input:focus{border-color:color-mix(in srgb, var(--teal) 55%, var(--mt-border))}.plan-input::placeholder{color:var(--mt-text-faint)}.plan-steps{flex-direction:column;gap:6px;margin-top:12px;display:flex}.plan-step{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:color-mix(in srgb, var(--mt-surface-2) 50%, transparent);font:12px var(--mt-mono);border-left-width:3px;align-items:center;gap:10px;padding:8px 12px;display:flex}.plan-step code{color:var(--mt-text);overflow-wrap:anywhere;flex:1}.plan-step.risk-high{border-left-color:var(--mt-bad)}.plan-step.risk-medium{border-left-color:var(--mt-warn)}.plan-step.risk-low{border-left-color:var(--mt-good)}.plan-op{background:color-mix(in srgb, var(--teal) 18%, transparent);width:18px;height:18px;color:var(--teal);border-radius:5px;flex:none;place-items:center;font-weight:700;display:grid}.plan-risk{text-transform:uppercase;letter-spacing:.05em;color:var(--mt-text-dim);font-size:10px}.plan-lock{color:var(--mt-bad);border:1px solid color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border));border-radius:999px;padding:1px 7px;font-size:10px}
28
32
  /*$vite$:1*/
29
33
  </style>
30
34
  </head>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usex/mikrotik-mcp",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "MCP server for MikroTik RouterOS — 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
5
5
  "keywords": [
6
6
  "ai",