@usex/mikrotik-mcp 3.33.0 → 3.34.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 +6 -4
- package/dist/index.js +4 -4
- package/dist/ui/observability.html +4 -4
- package/package.json +4 -4
package/dist/cli.js
CHANGED
|
@@ -1220,6 +1220,8 @@ function devicesPayload(store) {
|
|
|
1220
1220
|
authMode: dc.mac ? "mac-telnet" : dc.keyFilename || dc.privateKey ? "key" : dc.password ? "password" : "none",
|
|
1221
1221
|
isDefault: name === cfg.defaultDevice,
|
|
1222
1222
|
description: dc.description,
|
|
1223
|
+
jumpVia: dc.jumpVia,
|
|
1224
|
+
jumpHost: dc.jumpHost ? { host: dc.jumpHost.host, port: dc.jumpHost.port } : undefined,
|
|
1223
1225
|
status: getDeviceStatus(name),
|
|
1224
1226
|
history: getDeviceHistory(name),
|
|
1225
1227
|
activity: activity.get(name) ?? {
|
|
@@ -2231,7 +2233,7 @@ function registerPrompts(server) {
|
|
|
2231
2233
|
// package.json
|
|
2232
2234
|
var package_default = {
|
|
2233
2235
|
name: "@usex/mikrotik-mcp",
|
|
2234
|
-
version: "3.
|
|
2236
|
+
version: "3.34.0",
|
|
2235
2237
|
description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
2236
2238
|
keywords: [
|
|
2237
2239
|
"ai",
|
|
@@ -2313,7 +2315,7 @@ var package_default = {
|
|
|
2313
2315
|
},
|
|
2314
2316
|
devDependencies: {
|
|
2315
2317
|
"@resvg/resvg-js": "^2.6.2",
|
|
2316
|
-
"@tailwindcss/vite": "^4.3.
|
|
2318
|
+
"@tailwindcss/vite": "^4.3.2",
|
|
2317
2319
|
"@types/bun": "latest",
|
|
2318
2320
|
"@types/node": "^26.0.1",
|
|
2319
2321
|
"@types/react": "^19.2.17",
|
|
@@ -2324,13 +2326,13 @@ var package_default = {
|
|
|
2324
2326
|
"class-variance-authority": "^0.7.1",
|
|
2325
2327
|
clsx: "^2.1.1",
|
|
2326
2328
|
gsap: "^3.15.0",
|
|
2327
|
-
prettier: "^3.9.
|
|
2329
|
+
prettier: "^3.9.3",
|
|
2328
2330
|
react: "^19.2.7",
|
|
2329
2331
|
"react-dom": "^19.2.7",
|
|
2330
2332
|
recharts: "^3.9.0",
|
|
2331
2333
|
"release-it": "^20.2.1",
|
|
2332
2334
|
"tailwind-merge": "^3.6.0",
|
|
2333
|
-
tailwindcss: "^4.3.
|
|
2335
|
+
tailwindcss: "^4.3.2",
|
|
2334
2336
|
vite: "npm:@voidzero-dev/vite-plus-core@latest",
|
|
2335
2337
|
"vite-plus": "latest"
|
|
2336
2338
|
},
|
package/dist/index.js
CHANGED
|
@@ -131,7 +131,7 @@ function registerPrompts(server) {
|
|
|
131
131
|
// package.json
|
|
132
132
|
var package_default = {
|
|
133
133
|
name: "@usex/mikrotik-mcp",
|
|
134
|
-
version: "3.
|
|
134
|
+
version: "3.34.0",
|
|
135
135
|
description: "MCP server for MikroTik RouterOS \u2014 660+ tools over SSH for firewall, NAT, routing, DHCP, DNS, WireGuard, wireless, QoS and more.",
|
|
136
136
|
keywords: [
|
|
137
137
|
"ai",
|
|
@@ -213,7 +213,7 @@ var package_default = {
|
|
|
213
213
|
},
|
|
214
214
|
devDependencies: {
|
|
215
215
|
"@resvg/resvg-js": "^2.6.2",
|
|
216
|
-
"@tailwindcss/vite": "^4.3.
|
|
216
|
+
"@tailwindcss/vite": "^4.3.2",
|
|
217
217
|
"@types/bun": "latest",
|
|
218
218
|
"@types/node": "^26.0.1",
|
|
219
219
|
"@types/react": "^19.2.17",
|
|
@@ -224,13 +224,13 @@ var package_default = {
|
|
|
224
224
|
"class-variance-authority": "^0.7.1",
|
|
225
225
|
clsx: "^2.1.1",
|
|
226
226
|
gsap: "^3.15.0",
|
|
227
|
-
prettier: "^3.9.
|
|
227
|
+
prettier: "^3.9.3",
|
|
228
228
|
react: "^19.2.7",
|
|
229
229
|
"react-dom": "^19.2.7",
|
|
230
230
|
recharts: "^3.9.0",
|
|
231
231
|
"release-it": "^20.2.1",
|
|
232
232
|
"tailwind-merge": "^3.6.0",
|
|
233
|
-
tailwindcss: "^4.3.
|
|
233
|
+
tailwindcss: "^4.3.2",
|
|
234
234
|
vite: "npm:@voidzero-dev/vite-plus-core@latest",
|
|
235
235
|
"vite-plus": "latest"
|
|
236
236
|
},
|
|
@@ -76,12 +76,12 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
|
|
|
76
76
|
`).length;return(0,J.jsxs)(`div`,{className:`cfgstudio`,children:[(0,J.jsxs)(`div`,{className:`cfg-toolbar`,children:[(0,J.jsx)(`span`,{className:`cfg-status ${A?`is-ok`:`is-bad`}`,children:l?`invalid JSON`:s.length?`${s.length} schema issue(s)`:`valid ✓`}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`button`,{className:`topo-btn`,onClick:()=>void F(),children:`Test devices`}),(0,J.jsx)(`button`,{className:`topo-btn`,onClick:()=>void I(),disabled:!A,children:`Preview diff`}),(0,J.jsx)(`select`,{className:`cfg-select`,value:S,onChange:e=>C(Number(e.target.value)),title:`Auto-revert window`,children:pY.map(([e,t])=>(0,J.jsx)(`option`,{value:t,children:e},t))}),(0,J.jsx)(`button`,{className:`topo-btn cfg-save`,onClick:()=>void L(),disabled:!A||!!_,children:`Save`}),(0,J.jsx)(`button`,{className:`topo-btn`,onClick:t,children:`Close`})]}),w&&(0,J.jsx)(`div`,{className:`cfg-msg`,children:w}),_&&(0,J.jsxs)(`div`,{className:`cfg-banner`,children:[(0,J.jsx)(`strong`,{children:`Applied.`}),` `,(_.rollbackMs??0)>0?(0,J.jsxs)(J.Fragment,{children:[`Reverting in `,(0,J.jsxs)(`span`,{className:`cfg-count`,children:[b,`s`]}),` unless you keep it.`]}):(0,J.jsx)(J.Fragment,{children:`Saved without an auto-revert window.`}),_.devicesChanged&&(0,J.jsxs)(`span`,{className:`cfg-warn`,children:[` `,`· device list changed — reconnect the MCP client to expose it to the model`]}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`button`,{className:`topo-btn cfg-save`,onClick:()=>void ee(),children:`Keep changes`}),(0,J.jsx)(`button`,{className:`topo-btn`,onClick:()=>void te(),children:`Revert now`})]}),(0,J.jsxs)(`div`,{className:`cfg-editor`,children:[(0,J.jsx)(`pre`,{className:`cfg-gutter`,"aria-hidden":`true`,ref:k,children:Array.from({length:R},(e,t)=>t+1).join(`
|
|
77
77
|
`)}),(0,J.jsxs)(`div`,{className:`cfg-ta-wrap`,children:[(0,J.jsxs)(`pre`,{className:`cfg-hl`,"aria-hidden":`true`,ref:O,children:[rY(r),`
|
|
78
78
|
`]}),(0,J.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:()=>f(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)}}),d&&(0,J.jsx)(`div`,{className:`cfg-ac`,style:{left:d.x,top:d.y},children:d.items.map((e,t)=>(0,J.jsx)(`div`,{className:`cfg-ac-item${t===d.index?` is-sel`:``}`,onMouseDown:t=>{t.preventDefault(),M(e)},children:e},e))})]})]}),Object.keys(p).length>0&&(0,J.jsx)(`div`,{className:`cfg-chips`,children:Object.entries(p).map(([e,t])=>(0,J.jsxs)(`span`,{className:`cfg-chip ${t.ok?`is-ok`:`is-bad`}`,children:[t.ok?`●`:`○`,` `,e,`: `,t.label]},e))}),(l||s.length>0)&&(0,J.jsx)(`div`,{className:`cfg-errors`,children:l?(0,J.jsxs)(`div`,{className:`cfg-err`,children:[`JSON: `,l]}):s.slice(0,12).map((e,t)=>(0,J.jsxs)(`div`,{className:`cfg-err`,children:[(0,J.jsx)(`code`,{children:e.path}),` — `,e.message]},t))}),h&&(0,J.jsxs)(`div`,{className:`cfg-preview`,children:[(0,J.jsxs)(`div`,{className:`cfg-preview__hd`,children:[(0,J.jsx)(`strong`,{children:`Diff vs current`}),(0,J.jsx)(`span`,{className:`muted`,children:h.summary?.changed?`+${h.summary.added} / -${h.summary.removed}`:`no changes`}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`button`,{className:`topo-btn`,onClick:()=>g(null),children:`✕`})]}),(0,J.jsx)(`pre`,{className:`cfg-diff`,children:(h.unified||`(identical)`).split(`
|
|
79
|
-
`).map((e,t)=>(0,J.jsx)(`div`,{className:e.startsWith(`+`)?`d-add`:e.startsWith(`-`)?`d-del`:e.startsWith(`@@`)?`d-hunk`:``,children:e||` `},t))})]})]})}function hY(e){return e.reachable===!0?{label:`online`,color:`#d4d4d8`}:e.reachable===!1?{label:`offline`,color:`#f87171`}:{label:`checking…`,color:`#71717a`}}function gY(e){let t=0;for(let n=0;n<e.length;n++)t=Math.imul(t,31)+e.charCodeAt(n)>>>0;return`hsl(${t%360} 70% 62%)`}function _Y(e,t,n,r){let i=1-r;return{x:i*i*e.x+2*i*r*t.x+r*r*n.x,y:i*i*e.y+2*i*r*t.y+r*r*n.y}}function vY(e,t,n,r){let i=1-r,a=2*i*(t.x-e.x)+2*r*(n.x-t.x),o=2*i*(t.y-e.y)+2*r*(n.y-t.y);return Math.atan2(o,a)*180/Math.PI}var yY=`#e4e4e7`,bY=`#d4d4d8`,xY=18,SY=e=>e.length>xY?`${e.slice(0,xY-1)}…`:e,CY=e=>Math.max(23,Math.min(70,e.length*3.1+12));function wY({payload:e,pulses:t}){let n=e.devices,r=Math.max(1,n.length),i=n.map(e=>{let t=SY(e.name);return{d:e,label:t,r:CY(t)}}),a=Math.max(23,...i.map(e=>e.r)),o=r>1?(a+18)/Math.sin(Math.PI/r):0,s=Math.max(110+a,o,120),c=a+60,l=Math.max(700,Math.round((s+a+40)*2)),u=Math.round((s+c)*2),d=l/2,f=u/2,p={x:d,y:f},m=i.map((e,t)=>{let n=t/r*Math.PI*2-Math.PI/2+(r%2==0?Math.PI/r:0);return{...e,i:t,x:d+s*Math.cos(n),y:f+s*Math.sin(n)}});return(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`svg`,{className:`conn`,viewBox:`0 0 ${l} ${u}`,width:`100%`,height:u,preserveAspectRatio:`xMidYMid meet`,children:[(0,J.jsxs)(`defs`,{children:[(0,J.jsxs)(`radialGradient`,{id:`conn-hub`,cx:`0.5`,cy:`0.34`,r:`0.75`,children:[(0,J.jsx)(`stop`,{offset:`0`,stopColor:`#fafafa`}),(0,J.jsx)(`stop`,{offset:`0.6`,stopColor:`#d4d4d8`}),(0,J.jsx)(`stop`,{offset:`1`,stopColor:`#a1a1aa`})]}),(0,J.jsxs)(`radialGradient`,{id:`conn-orb`,cx:`0.5`,cy:`0.32`,r:`0.85`,children:[(0,J.jsx)(`stop`,{offset:`0`,stopColor:`#27272a`}),(0,J.jsx)(`stop`,{offset:`1`,stopColor:`#18181b`})]}),(0,J.jsxs)(`radialGradient`,{id:`conn-burst`,cx:`0.5`,cy:`0.5`,r:`0.5`,children:[(0,J.jsx)(`stop`,{offset:`0`,stopColor:`#fafafa`}),(0,J.jsx)(`stop`,{offset:`0.5`,stopColor:`#e4e4e7`}),(0,J.jsx)(`stop`,{offset:`1`,stopColor:`#a1a1aa`,stopOpacity:`0`})]})]}),[.5,.78,1].map((e,t)=>(0,J.jsx)(`circle`,{className:`conn-grid`,cx:d,cy:f,r:s*e},`g-${t}`)),[0,1,2].map(e=>(0,J.jsx)(`circle`,{className:`conn-sonar`,cx:d,cy:f,style:{animationDelay:`${e*1.1}s`}},`s-${e}`)),m.map(({d:e,i:n,x:r,y:i})=>{let a=hY(e.status),o=e.status.reachable===!0,s=e.status.reachable==null,c={x:r,y:i},l=(d+r)/2,u=(f+i)/2,m=r-d,h=i-f,g=Math.hypot(m,h)||1,_=-h/g,v=m/g,y={x:l+_*16,y:u+v*16},b={x:l-_*16,y:u-v*16},x=`M${d},${f} Q${y.x.toFixed(1)},${y.y.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,S=`M${d},${f} Q${b.x.toFixed(1)},${b.y.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,C=`M${d},${f} Q${l.toFixed(1)},${u.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,w=_Y(p,y,c,.52),T=vY(p,y,c,.52),E=_Y(p,b,c,.48),D=vY(p,b,c,.48)+180,O=t[e.name]??0;return(0,J.jsxs)(`g`,{children:[(0,J.jsx)(`path`,{id:`conn-cmd-${n}`,className:`conn-link`,d:x,stroke:o?yY:a.color,strokeOpacity:o?.5:.32,strokeDasharray:s?`2 8`:o?void 0:`6 7`}),(0,J.jsx)(`path`,{id:`conn-res-${n}`,className:`conn-link`,d:S,stroke:o?bY:a.color,strokeOpacity:o?.5:.18,strokeDasharray:o?void 0:`6 7`}),o&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`path`,{className:`conn-flow`,d:x,stroke:`#e4e4e7`}),(0,J.jsx)(`circle`,{className:`conn-packet`,r:3.2,fill:`#e4e4e7`,children:(0,J.jsx)(`animateMotion`,{dur:`2.6s`,repeatCount:`indefinite`,calcMode:`linear`,children:(0,J.jsx)(`mpath`,{href:`#conn-cmd-${n}`})})}),(0,J.jsx)(`rect`,{className:`conn-packet`,x:-2.6,y:-2.6,width:5.2,height:5.2,fill:`#d4d4d8`,transform:`rotate(45)`,children:(0,J.jsx)(`animateMotion`,{dur:`2.6s`,begin:`0.9s`,repeatCount:`indefinite`,calcMode:`linear`,keyPoints:`1;0`,keyTimes:`0;1`,children:(0,J.jsx)(`mpath`,{href:`#conn-res-${n}`})})}),(0,J.jsx)(`path`,{className:`conn-chevron`,d:`M-4,-3 L4,0 L-4,3`,stroke:`#e4e4e7`,transform:`translate(${w.x.toFixed(1)},${w.y.toFixed(1)}) rotate(${T.toFixed(1)})`}),(0,J.jsx)(`path`,{className:`conn-chevron`,d:`M-4,-3 L4,0 L-4,3`,stroke:`#d4d4d8`,transform:`translate(${E.x.toFixed(1)},${E.y.toFixed(1)}) rotate(${D.toFixed(1)})`})]}),O>0&&(0,J.jsx)(`g`,{children:(0,J.jsxs)(`circle`,{r:6,fill:`url(#conn-burst)`,children:[(0,J.jsx)(`animateMotion`,{dur:`1.1s`,repeatCount:`1`,calcMode:`linear`,keyPoints:`0;1;0`,keyTimes:`0;0.5;1`,path:C}),(0,J.jsx)(`animate`,{attributeName:`opacity`,values:`0;1;1;0`,keyTimes:`0;0.1;0.85;1`,dur:`1.1s`,repeatCount:`1`,fill:`freeze`})]})},`burst-${e.name}-${O}`)]},`l-${e.name}`)}),(0,J.jsx)(`circle`,{className:`conn-hub-glow`,cx:d,cy:f,r:42}),(0,J.jsx)(`circle`,{className:`conn-hub-ring`,cx:d,cy:f,r:37}),(0,J.jsx)(`circle`,{cx:d,cy:f,r:29,fill:`url(#conn-hub)`,stroke:`#71717a`,strokeWidth:1.5}),(0,J.jsx)(`text`,{x:d,y:f-4,textAnchor:`middle`,fill:`#09090b`,fontSize:12,fontWeight:700,children:`LLM`}),(0,J.jsx)(`text`,{x:d,y:f+8,textAnchor:`middle`,fill:`#3f3f46`,fontSize:8,fontWeight:600,children:`⇄ MCP`}),(0,J.jsx)(`text`,{x:d,y:f+18,textAnchor:`middle`,fill:`#3f3f46`,fontSize:7.5,fontWeight:600,children:`server`}),m.map(({d:e,x:t,y:n,r,label:i})=>{let a=hY(e.status),o=e.status.reachable===!0,s=o?`${e.status.latencyMs??`?`} ms`:a.label,c=gY(e.name);return(0,J.jsxs)(`g`,{className:`conn-node`,children:[o&&(0,J.jsx)(`circle`,{className:`conn-node-halo`,cx:t,cy:n,r:r+1,stroke:c}),(0,J.jsx)(`circle`,{cx:t,cy:n,r,fill:`url(#conn-orb)`}),(0,J.jsx)(`circle`,{cx:t,cy:n,r,fill:c,opacity:.16}),(0,J.jsx)(`circle`,{cx:t,cy:n,r,fill:`none`,stroke:c,strokeWidth:2.5}),(0,J.jsx)(`circle`,{className:o?`conn-blink`:void 0,cx:t+r*.7,cy:n-r*.7,r:4.5,fill:a.color,stroke:`#0a121b`,strokeWidth:1.5}),(0,J.jsx)(`text`,{x:t,y:n+3.5,textAnchor:`middle`,fill:`#fafafa`,fontSize:10,fontWeight:600,children:i}),(0,J.jsx)(`text`,{x:t,y:n+r+14,textAnchor:`middle`,fill:`#a1a1aa`,fontSize:9,children:e.address??e.host}),(0,J.jsx)(`text`,{x:t,y:n+r+26,textAnchor:`middle`,fill:a.color,fontSize:9,fontWeight:600,children:s})]},`n-${e.name}`)})]}),(0,J.jsxs)(`div`,{className:`conn-legend`,children:[(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{style:{background:yY}}),` command · LLM → device`]}),(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{style:{background:bY}}),` response · device → LLM`]}),(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{style:{background:`#e4e4e7`}}),` live call (round-trip)`]})]})]})}function TY({d:e}){let t=hY(e.status),n=e.status.reachable===!0?`${t.label} · ${e.status.latencyMs??`?`}ms${e.status.version?` · v${e.status.version}`:``}`:e.status.reachable===!1?`${t.label}${e.status.error?` · ${e.status.error}`:``}`:t.label,r=gY(e.name);return(0,J.jsxs)(`div`,{className:`card dev-card`,style:{borderLeft:`3px solid ${r}`},children:[(0,J.jsxs)(`div`,{className:`dev-card__top`,children:[(0,J.jsx)(`span`,{className:`dot`,style:{background:r},title:`device colour`}),(0,J.jsx)(`span`,{className:`dev-card__name`,children:e.name}),e.isDefault&&(0,J.jsx)(Mc,{type:`accent`,children:`default`}),(0,J.jsx)(`span`,{className:`dot dot--status`,style:{background:t.color},title:t.label}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`span`,{className:`chip`,children:e.authMode})]}),(0,J.jsxs)(`div`,{className:`dev-card__meta`,children:[(0,J.jsx)(`span`,{children:e.mac?`mac`:`host`}),(0,J.jsx)(`b`,{children:e.address??`${e.host}:${e.port}`}),(0,J.jsx)(`span`,{children:`user`}),(0,J.jsx)(`b`,{children:e.username}),(0,J.jsx)(`span`,{children:`status`}),(0,J.jsx)(`b`,{style:{color:t.color},children:n}),(0,J.jsx)(`span`,{children:`activity`}),(0,J.jsxs)(`b`,{children:[e.activity.calls,` calls · `,e.activity.errors,` err`,e.activity.avgMs?` · ${xc(e.activity.avgMs)} avg`:``]}),e.description&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`span`,{children:`note`}),(0,J.jsx)(`b`,{children:e.description})]})]})]})}function EY({event:e,onClose:t}){let[n,r]=(0,v.useState)(sY);return(0,J.jsx)(`div`,{className:`overlay`,onClick:e=>e.target===e.currentTarget&&t(),children:(0,J.jsxs)(`div`,{className:`sheet`,children:[(0,J.jsxs)(`div`,{className:`sheet__hd`,children:[(0,J.jsx)(`span`,{className:`risk risk-${e.risk}`,children:e.risk}),(0,J.jsxs)(`h3`,{className:`sheet__tool`,children:[e.tool,(0,J.jsx)(bc,{text:e.tool,className:`iconbtn`,icon:!0,title:`Copy tool name`})]}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(Ac,{type:`secondary`,size:`sm`,onClick:t,children:`✕ Close`})]}),(0,J.jsxs)(`div`,{className:`kv__body`,children:[(0,J.jsx)(`div`,{className:`kv__k`,children:`title`}),(0,J.jsx)(`div`,{className:`kv__v`,children:e.title}),(0,J.jsx)(`div`,{className:`kv__k`,children:`time`}),(0,J.jsx)(`div`,{className:`kv__v`,children:new Date(e.ts).toLocaleString(void 0,{hour12:!1})}),(0,J.jsx)(`div`,{className:`kv__k`,children:`device`}),(0,J.jsx)(`div`,{className:`kv__v`,children:e.device??`—`}),(0,J.jsx)(`div`,{className:`kv__k`,children:`transport`}),(0,J.jsx)(`div`,{className:`kv__v`,children:e.transport??`—`}),(0,J.jsx)(`div`,{className:`kv__k`,children:`duration`}),(0,J.jsx)(`div`,{className:`kv__v`,children:xc(e.durationMs)}),(0,J.jsx)(`div`,{className:`kv__k`,children:`status`}),(0,J.jsx)(`div`,{className:`kv__v`,children:(0,J.jsx)(`span`,{className:e.isError?`status-err`:`status-ok`,children:e.isError?`error`:`ok`})}),(0,J.jsx)(`div`,{className:`kv__k`,children:`output size`}),(0,J.jsxs)(`div`,{className:`kv__v`,children:[Sc(e.outputBytes),e.truncated?` (truncated)`:``]}),(0,J.jsx)(`div`,{className:`kv__k`,children:`structured`}),(0,J.jsx)(`div`,{className:`kv__v`,children:e.hasStructured?`yes (renders an MCP App view)`:`no`})]}),e.error&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`h2`,{className:`muted`,children:`ERROR`}),(0,J.jsx)(`pre`,{className:`body`,style:{color:`var(--mt-bad)`},children:e.error})]}),(0,J.jsxs)(`div`,{className:`sheet__hd`,children:[(0,J.jsx)(`h2`,{className:`muted`,style:{margin:0},children:`INPUT`}),(0,J.jsx)(`span`,{style:{flex:1}}),e.input&&(0,J.jsx)(Ac,{type:`secondary`,size:`sm`,ghost:!0,onClick:()=>r(e=>{let t=!e;return cY(t),t}),title:n?`Showing pretty-printed JSON — click for raw`:`Showing raw JSON — click to pretty-print`,children:n?`✦ Pretty`:`{ } Raw`}),(0,J.jsx)(bc,{text:e.input,title:`Copy input JSON`})]}),e.input?(0,J.jsx)(uY,{value:lY(e.input,n)}):(0,J.jsx)(`pre`,{className:`body`,children:`—`}),(0,J.jsxs)(`div`,{className:`sheet__hd`,children:[(0,J.jsx)(`h2`,{className:`muted`,style:{margin:0},children:`OUTPUT`}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(bc,{text:e.output,title:`Copy output`})]}),(0,J.jsx)(`pre`,{className:`body ros`,children:e.output?aY(e.output):`—`})]})})}var DY=e=>e==null?`?`:Sc(e);function OY({d:e}){let t=e.status,n=e.history??[];return t.reachable===!0||n.length>0?(0,J.jsxs)(`div`,{className:`card health-card`,children:[(0,J.jsxs)(`div`,{className:`health-card__hd`,children:[(0,J.jsx)(Nc,{color:hY(t).color}),(0,J.jsx)(`span`,{className:`dev-card__name`,children:e.name}),e.isDefault&&(0,J.jsx)(Mc,{type:`accent`,children:`default`}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(Mc,{type:t.version?`success`:`default`,children:t.version?`v${t.version}`:`—`})]}),(0,J.jsxs)(`div`,{className:`health-card__sub muted`,children:[t.boardName??`router`,t.architecture?` · ${t.architecture}`:``,t.cpuCount?` · ${t.cpuCount} cpu`:``,t.uptime?` · up ${t.uptime}`:``]}),(0,J.jsxs)(`div`,{className:`health-card__gauges`,children:[(0,J.jsx)(HJ,{value:t.cpuLoad,label:`CPU`,color:Ec.cpu}),(0,J.jsx)(HJ,{value:t.memUsedPct,label:`MEM`,color:Ec.mem}),(0,J.jsx)(HJ,{value:t.hddUsedPct,label:`DISK`,color:Ec.disk})]}),(0,J.jsxs)(`div`,{className:`health-card__charts`,children:[(0,J.jsxs)(`div`,{className:`health-chart`,children:[(0,J.jsx)(`span`,{className:`health-chart__k`,children:`CPU load`}),(0,J.jsx)(VJ,{id:`${e.name}-cpu`,values:n.map(e=>e.cpuLoad),color:Ec.cpu,maxValue:100,unit:`%`})]}),(0,J.jsxs)(`div`,{className:`health-chart`,children:[(0,J.jsx)(`span`,{className:`health-chart__k`,children:`Memory used`}),(0,J.jsx)(VJ,{id:`${e.name}-mem`,values:n.map(e=>e.memUsedPct),color:Ec.mem,maxValue:100,unit:`%`})]}),(0,J.jsxs)(`div`,{className:`health-chart`,children:[(0,J.jsx)(`span`,{className:`health-chart__k`,children:`Probe latency`}),(0,J.jsx)(VJ,{id:`${e.name}-lat`,values:n.map(e=>e.latencyMs),color:Ec.latency,unit:`ms`})]})]}),(0,J.jsxs)(`div`,{className:`health-card__foot muted`,children:[`RAM `,DY(t.totalMemory&&t.freeMemory?t.totalMemory-t.freeMemory:void 0),` /`,` `,DY(t.totalMemory),` · free disk `,DY(t.freeHdd)]})]}):(0,J.jsxs)(`div`,{className:`card health-card health-card--na`,children:[(0,J.jsxs)(`div`,{className:`health-card__hd`,children:[(0,J.jsx)(Nc,{color:hY(t).color}),(0,J.jsx)(`span`,{className:`dev-card__name`,children:e.name}),e.isDefault&&(0,J.jsx)(Mc,{type:`accent`,children:`default`})]}),(0,J.jsx)(`p`,{className:`muted`,style:{margin:0},children:t.reachable===!1?`Offline — ${t.error??`unreachable`}`:e.mac?`Waiting for the first MAC-Telnet probe (these run every few minutes to avoid contending with tool calls)…`:`Waiting for the first health probe…`})]})}function kY(e,t){let n=(0,v.useRef)(e),r=(0,v.useRef)(t);n.current=e,r.current=t,(0,v.useEffect)(()=>{let e=!1,t=null,i=null,a=()=>{e||(i=new EventSource(dc(`/api/sse`)),i.addEventListener(`hello`,()=>r.current(`sse`)),i.addEventListener(`tool`,e=>{try{n.current(JSON.parse(e.data))}catch{}}),i.onerror=()=>{i&&i.readyState===EventSource.CONNECTING&&r.current(`off`)})},o=()=>{if(e)return;let i=location.protocol===`https:`?`wss`:`ws`;t=new WebSocket(dc(`${i}://${location.host}/api/stream`));let s=!1;t.onopen=()=>{s=!0,r.current(`ws`)},t.onerror=()=>t?.close(),t.onmessage=e=>{try{let t=JSON.parse(e.data);t.type===`event`&&t.event&&n.current(t.event)}catch{}},t.onclose=()=>{e||(r.current(`off`),s?setTimeout(o,2e3):a())}};return o(),()=>{e=!0,t?.close(),i?.close()}},[])}function AY(e){(0,v.useLayoutEffect)(()=>{let t=e.current;if(!t||window.matchMedia(`(prefers-reduced-motion: reduce)`).matches)return;document.documentElement.classList.add(`js-motion`);let n=new WeakSet,r=e=>{n.has(e)||(n.add(e),Ri.set(e,{opacity:0,y:26}),q.create({trigger:e,start:`top 90%`,once:!0,onEnter:()=>Ri.to(e,{opacity:1,y:0,duration:.7,ease:`power3.out`})}))},i=e=>{for(let t of e.querySelectorAll(`.reveal`))r(t)};i(t);let a=new MutationObserver(e=>{for(let t of e)for(let e of t.addedNodes)e instanceof Element&&(e.matches(`.reveal`)&&r(e),i(e))});a.observe(t,{childList:!0,subtree:!0});let o=setInterval(()=>q.refresh(),1200),s=setTimeout(()=>clearInterval(o),7e3);return()=>{a.disconnect(),clearInterval(o),clearTimeout(s);for(let e of q.getAll())e.kill();document.documentElement.classList.remove(`js-motion`)}},[e])}function jY({m:e,busy:t,onToggle:n}){return(0,J.jsxs)(`label`,{className:`mod-row`,"data-on":e.enabled?`1`:void 0,title:e.description,children:[(0,J.jsx)(`input`,{type:`checkbox`,checked:e.enabled,disabled:t,onChange:()=>n(e.slug,!e.enabled)}),(0,J.jsxs)(`span`,{className:`mod-row__main`,children:[(0,J.jsxs)(`span`,{className:`mod-row__name`,children:[e.label,(0,J.jsx)(`code`,{className:`mod-row__slug`,children:e.slug})]}),(0,J.jsx)(`span`,{className:`mod-row__desc muted`,children:e.description})]}),(0,J.jsxs)(`span`,{className:`mod-row__count muted`,children:[e.toolCount,` tool`,e.toolCount===1?``:`s`]})]})}function MY(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(new Set),[i,a]=(0,v.useState)(null),[o,s]=(0,v.useState)(``),c=(0,v.useCallback)(()=>{fc(`/api/modules`).then(t).catch(()=>a(`could not load modules`))},[]);(0,v.useEffect)(()=>c(),[c]);let l=e=>t(t=>t?{...t,...e}:e),u=(0,v.useCallback)(async(e,n)=>{r(t=>new Set(t).add(e)),t(t=>t&&{...t,modules:t.modules.map(t=>t.slug===e?{...t,enabled:n}:t)});let i=await pc(`/api/modules/toggle`,{slug:e,enabled:n}).catch(()=>({error:`request failed`}));if(r(t=>{let n=new Set(t);return n.delete(e),n}),i.error||i.ok===!1){a(i.error??`toggle failed`),c();return}l(i);let o=i.persisted?`saved to config`:`applied live (not saved)`,s=i.warning?` ⚠ ${i.warning}`:``;a(`${e} ${n?`enabled`:`disabled`} — ${o}. Reconnect the MCP client (or restart the server) for the tool list to update.${s}`)},[c]),d=(0,v.useCallback)(async(e,t)=>{for(let n of e)await u(n,t)},[u]),f=(0,v.useMemo)(()=>{if(!e)return[];let t=o.trim().toLowerCase(),n=e=>!t||e.slug.toLowerCase().includes(t)||e.label.toLowerCase().includes(t)||e.group.toLowerCase().includes(t)||e.description.toLowerCase().includes(t),r=new Map;for(let t of e.modules){if(!n(t))continue;let e=r.get(t.group)??[];e.push(t),r.set(t.group,e)}return[...r.entries()].map(([e,t])=>({group:e,modules:t}))},[e,o]);if(!e)return(0,J.jsx)(`div`,{className:`muted`,children:`loading modules…`});let p=f.reduce((e,t)=>e+t.modules.length,0);return(0,J.jsx)(`section`,{className:`view`,children:(0,J.jsxs)(gc,{title:`Tool modules`,className:`reveal`,extra:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`span`,{className:`muted`,children:[e.enabledModules,`/`,e.total,` modules · `,e.enabledTools,`/`,e.totalTools,` `,`tools exposed`]}),(0,J.jsx)(`button`,{className:`btn`,onClick:c,style:{marginLeft:10},children:`↻ Refresh`})]}),children:[(0,J.jsxs)(`div`,{className:`legend`,style:{margin:`0 0 10px`},children:[(0,J.jsxs)(`span`,{children:[`writes to: `,(0,J.jsx)(`code`,{children:e.source?.path??`config file`})]}),(0,J.jsx)(`span`,{children:e.hasAllowList?`allow-list active`:`all modules on by default`})]}),(0,J.jsxs)(`p`,{className:`muted`,style:{margin:`0 0 12px`,fontSize:12},children:[`Toggle a module to expose or hide all of its tools. Disabling adds it to`,` `,(0,J.jsx)(`code`,{children:`tools.disabledModules`}),` in your config file; enabling removes it (or adds it to`,` `,(0,J.jsx)(`code`,{children:`tools.enabledModules`}),` when an allow-list is in force). Trimming the surface below ~150–200 tools makes every remaining tool reliably findable by the MCP client. The client must reconnect for changes to take effect.`]}),(0,J.jsxs)(`div`,{className:`mod-toolbar`,children:[(0,J.jsx)(`input`,{className:`input`,placeholder:`Filter modules by name, slug, group or description…`,value:o,onChange:e=>s(e.target.value)}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>void d(e.modules.filter(e=>!e.enabled).map(e=>e.slug),!0),children:`Enable all`}),` `,(0,J.jsx)(`button`,{className:`btn`,onClick:()=>void d(e.modules.filter(e=>e.enabled).map(e=>e.slug),!1),children:`Disable all`})]}),i&&(0,J.jsx)(`div`,{className:`cfg-msg`,style:{marginTop:12},children:i}),p===0?(0,J.jsxs)(`div`,{className:`muted`,style:{padding:12},children:[`No modules match “`,o,`”.`]}):(0,J.jsx)(`div`,{className:`mod-groups`,children:f.map(({group:e,modules:t})=>{let r=t.filter(e=>e.enabled).length,i=t.map(e=>e.slug);return(0,J.jsxs)(`div`,{className:`mod-group`,children:[(0,J.jsxs)(`div`,{className:`mod-group__hd`,children:[(0,J.jsx)(`h3`,{className:`mod-group__title`,children:e}),(0,J.jsxs)(`span`,{className:`muted`,children:[r,`/`,t.length,` on`]}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`button`,{className:`btn btn-sm`,onClick:()=>void d(i.filter(e=>{let n=t.find(t=>t.slug===e);return n?!n.enabled:!1}),!0),children:`Enable group`}),` `,(0,J.jsx)(`button`,{className:`btn btn-sm`,onClick:()=>void d(i.filter(e=>{let n=t.find(t=>t.slug===e);return n?n.enabled:!1}),!1),children:`Disable group`})]}),(0,J.jsx)(`div`,{className:`mod-list`,children:t.map(e=>(0,J.jsx)(jY,{m:e,busy:n.has(e.slug),onToggle:(e,t)=>void u(e,t)},e.slug))})]},e)})})]})})}var NY={TCP:`#e4e4e7`,UDP:`#d4d4d8`,ICMP:`#a1a1aa`,ICMPv6:`#a1a1aa`,ARP:`#e4e4e7`,IPv6:`#a1a1aa`},PY=e=>e&&NY[e]||`#71717a`;function FY(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(!1);(0,v.useEffect)(()=>{let e=()=>void fc(`/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 pc(`/api/capture/stop`,{}).catch(()=>{}),r(!1)};if(!i||!i.running&&i.packets===0)return(0,J.jsxs)(`div`,{className:`cap-idle muted`,children:[`No capture running. Start one with the `,(0,J.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,J.jsxs)(`div`,{className:`cap`,children:[(0,J.jsxs)(`div`,{className:`cap-bar`,children:[(0,J.jsx)(`span`,{className:`cap-dot${i.running?` is-on`:``}`}),(0,J.jsx)(`b`,{children:i.running?`capturing`:`stopped`}),(0,J.jsxs)(`span`,{className:`muted`,children:[`UDP `,i.port]}),(0,J.jsxs)(`span`,{className:`muted`,children:[wc(i.packets),` pkts · `,Sc(i.bytes)]}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`a`,{className:`btn`,href:dc(`/api/capture/pcap`),download:`capture.pcap`,children:`⤓ pcap`}),(0,J.jsx)(`button`,{className:`btn btn-danger`,onClick:()=>void o(),disabled:n||!i.running,children:`■ Stop`})]}),(0,J.jsxs)(`div`,{className:`cap-cols`,children:[(0,J.jsxs)(`div`,{className:`cap-side`,children:[(0,J.jsx)(`div`,{className:`cap-h`,children:`Protocols`}),Object.entries(i.protocols).map(([e,t])=>(0,J.jsxs)(`div`,{className:`cap-pbar`,children:[(0,J.jsx)(`span`,{className:`cap-plabel`,style:{color:PY(e)},children:e}),(0,J.jsx)(`span`,{className:`cap-ptrack`,children:(0,J.jsx)(`i`,{style:{width:`${t/s*100}%`,background:PY(e)}})}),(0,J.jsx)(`span`,{className:`cap-pn`,children:t})]},e)),(0,J.jsx)(`div`,{className:`cap-h`,style:{marginTop:12},children:`Top talkers`}),i.topTalkers.length===0&&(0,J.jsx)(`div`,{className:`muted`,children:`—`}),i.topTalkers.map(e=>(0,J.jsxs)(`div`,{className:`cap-talker`,children:[(0,J.jsx)(`span`,{children:e.addr}),(0,J.jsx)(`b`,{children:e.count})]},e.addr))]}),(0,J.jsx)(`div`,{className:`cap-list`,children:a.length===0?(0,J.jsx)(`div`,{className:`muted`,style:{padding:10},children:`waiting for packets…`}):a.map((e,t)=>(0,J.jsxs)(`div`,{className:`cap-row`,children:[(0,J.jsx)(`span`,{className:`cap-tt`,children:Cc(e.ts)}),(0,J.jsx)(`span`,{className:`cap-proto`,style:{color:PY(e.protocol)},children:e.protocol??e.ethType}),(0,J.jsx)(`span`,{className:`cap-len`,children:e.len}),(0,J.jsx)(`span`,{className:`cap-info`,children:e.info})]},t))})]})]})}function IY(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(null),[i,a]=(0,v.useState)(null),[o,s]=(0,v.useState)(null),c=(0,v.useCallback)(()=>{fc(`/api/s3/list`).then(t).catch(()=>t({configured:!1,objects:[]}))},[]);(0,v.useEffect)(()=>c(),[c]);let l=e=>{fc(`/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`))},u=async e=>{a(e);let t=await pc(`/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,J.jsx)(`section`,{className:`view`,children:(0,J.jsxs)(gc,{title:`S3 backups`,className:`reveal`,extra:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`span`,{className:`muted`,children:[e.target,` · `,e.objects.length,` object`,e.objects.length===1?``:`s`,e.truncated?` (truncated)`:``]}),(0,J.jsx)(`button`,{className:`btn`,onClick:c,style:{marginLeft:10},children:`↻ Refresh`})]}),children:[o&&(0,J.jsx)(`div`,{className:`cfg-msg`,children:o}),e.objects.length===0?(0,J.jsxs)(`div`,{className:`muted`,style:{padding:12},children:[`No objects in the bucket. Upload one with the `,(0,J.jsx)(`code`,{children:`upload_backup_to_s3`}),` tool.`]}):(0,J.jsx)(`div`,{className:`feedwrap`,children:(0,J.jsxs)(`table`,{className:`feed`,children:[(0,J.jsx)(`thead`,{children:(0,J.jsxs)(`tr`,{children:[(0,J.jsx)(`th`,{children:`key`}),(0,J.jsx)(`th`,{className:`num`,children:`size`}),(0,J.jsx)(`th`,{children:`modified`}),(0,J.jsx)(`th`,{style:{width:200},children:`actions`})]})}),(0,J.jsx)(`tbody`,{children:e.objects.map(e=>(0,J.jsxs)(`tr`,{children:[(0,J.jsx)(`td`,{children:e.key}),(0,J.jsx)(`td`,{className:`num`,children:Sc(e.size)}),(0,J.jsx)(`td`,{children:e.lastModified?new Date(e.lastModified).toLocaleString(void 0,{hour12:!1}):`—`}),(0,J.jsxs)(`td`,{onClick:e=>e.stopPropagation(),children:[(0,J.jsx)(`button`,{className:`btn`,onClick:()=>l(e.key),children:`⤓ Download`}),` `,n===e.key?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`button`,{className:`btn btn-danger`,disabled:i===e.key,onClick:()=>void u(e.key),children:`✓ Confirm`}),` `,(0,J.jsx)(`button`,{className:`btn`,onClick:()=>r(null),children:`Cancel`})]}):(0,J.jsx)(`button`,{className:`btn`,onClick:()=>r(e.key),children:`🗑 Delete`})]})]},e.key))})]})})]})}):(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`☁️`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`S3 is not configured`}),(0,J.jsxs)(`p`,{className:`feed-empty__sub`,children:[`Add an `,(0,J.jsx)(`code`,{children:`s3`}),` block (bucket + credentials) to your config to manage backup objects here.`]})]}):(0,J.jsx)(`div`,{className:`muted`,children:`loading S3 objects…`})}function LY(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(``),[i,a]=(0,v.useState)(``),[o,s]=(0,v.useState)(null),[c,l]=(0,v.useState)(null),u=(0,v.useCallback)(()=>{fc(`/api/snapshots`).then(e=>t(e.snapshots)).catch(()=>t([]))},[]);(0,v.useEffect)(()=>u(),[u]);let d=e=>{fc(`/api/snapshot/${encodeURIComponent(e)}`).then(s).catch(()=>{})},f=()=>{!n||!i||pc(`/api/snapshots/diff`,{from:n,to:i}).then(l).catch(()=>{})};if(!e)return(0,J.jsx)(`div`,{className:`muted`,children:`loading snapshots…`});if(e.length===0)return(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`🕰️`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`No config snapshots yet`}),(0,J.jsxs)(`p`,{className:`feed-empty__sub`,children:[`Capture one with the `,(0,J.jsx)(`code`,{children:`capture_config_snapshot`}),` tool — then time-travel diff any two here.`]})]});let p=e.map(e=>(0,J.jsxs)(`option`,{value:e.id,children:[e.device,` · `,e.label??e.id,` · `,Cc(e.ts)]},e.id));return(0,J.jsxs)(`section`,{className:`view`,children:[(0,J.jsxs)(gc,{title:`Config snapshots`,className:`reveal`,extra:(0,J.jsx)(`button`,{className:`btn`,onClick:u,children:`↻ Refresh`}),children:[(0,J.jsxs)(`div`,{className:`toolbar`,style:{marginBottom:12},children:[(0,J.jsxs)(`select`,{className:`btn`,value:n,onChange:e=>r(e.target.value),children:[(0,J.jsx)(`option`,{value:``,children:`diff from…`}),p]}),(0,J.jsxs)(`select`,{className:`btn`,value:i,onChange:e=>a(e.target.value),children:[(0,J.jsx)(`option`,{value:``,children:`to…`}),p]}),(0,J.jsx)(`button`,{className:`btn is-active`,onClick:f,disabled:!n||!i,children:`Diff →`}),(0,J.jsxs)(`span`,{className:`muted`,children:[e.length,` snapshots`]})]}),(0,J.jsx)(`div`,{className:`feedwrap`,children:(0,J.jsxs)(`table`,{className:`feed`,children:[(0,J.jsx)(`thead`,{children:(0,J.jsxs)(`tr`,{children:[(0,J.jsx)(`th`,{children:`captured`}),(0,J.jsx)(`th`,{children:`device`}),(0,J.jsx)(`th`,{children:`label`}),(0,J.jsx)(`th`,{children:`version`}),(0,J.jsx)(`th`,{className:`num`,children:`lines`}),(0,J.jsx)(`th`,{className:`num`,children:`size`}),(0,J.jsx)(`th`,{children:`output`})]})}),(0,J.jsx)(`tbody`,{children:e.map(e=>(0,J.jsxs)(`tr`,{className:o?.id===e.id?`is-selected`:void 0,onClick:()=>d(e.id),children:[(0,J.jsx)(`td`,{children:Cc(e.ts)}),(0,J.jsx)(`td`,{children:e.device}),(0,J.jsx)(`td`,{children:e.label??`—`}),(0,J.jsx)(`td`,{children:e.rosVersion??`—`}),(0,J.jsx)(`td`,{className:`num`,children:e.lines}),(0,J.jsx)(`td`,{className:`num`,children:Sc(e.bytes)}),(0,J.jsx)(`td`,{className:`preview`,children:`view export →`})]},e.id))})]})})]}),c&&(0,J.jsx)(gc,{title:`Time-travel diff`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:c.summary.changed?`+${c.summary.added} / -${c.summary.removed}`:`identical`}),children:(0,J.jsx)(`pre`,{className:`cfg-diff`,children:(c.unified||`(identical)`).split(`
|
|
80
|
-
`).map((e,t)=>(0,J.jsx)(`div`,{className:e.startsWith(`+`)?`d-add`:e.startsWith(`-`)?`d-del`:e.startsWith(`@@`)?`d-hunk`:``,children:e||` `},t))})}),o&&(0,J.jsx)(gc,{title:`Snapshot · ${o.label??o.id}`,className:`reveal`,extra:(0,J.jsx)(Ac,{type:`secondary`,size:`sm`,onClick:()=>s(null),children:`✕ Close`}),children:(0,J.jsx)(`pre`,{className:`body`,style:{maxHeight:460},children:o.body||`(empty)`})})]})}function RY(e){return e==null?`#3f3f46`:e>=85?`#f87171`:e>=60?`#a1a1aa`:`#d4d4d8`}function zY({topo:e,onOnboard:t}){let[n,r]=(0,v.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),l=Math.max(360,Math.round((c+78)*2)),u=l/2,d=new Map,f=new Map;i.forEach((e,t)=>{if(i.length===1){d.set(e.id,{x:380,y:u}),f.set(e.id,-Math.PI/2);return}let n=t/o*Math.PI*2-Math.PI/2;f.set(e.id,n),d.set(e.id,{x:380+s*Math.cos(n),y:u+s*Math.sin(n)})});let p=new Map,m=new Set(a.map(e=>e.id));for(let t of e.edges)m.has(t.to)&&!p.has(t.to)&&p.set(t.to,t.from);let h=new Map;for(let e of a){let t=p.get(e.id)??i[0]?.id??``;h.set(t,[...h.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;d.set(e.id,{x:380+c*Math.cos(n),y:u+c*Math.sin(n)})});else for(let[e,t]of h){let n=f.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);d.set(e,{x:380+c*Math.cos(a),y:u+c*Math.sin(a)})})}let g=new Map(e.nodes.map(e=>[e.id,e])),_=n?g.get(n):null;return(0,J.jsxs)(`div`,{className:`topo`,children:[(0,J.jsxs)(`svg`,{viewBox:`0 0 760 ${l}`,width:`100%`,height:Math.min(l,540),preserveAspectRatio:`xMidYMid meet`,children:[e.edges.map((e,t)=>{let n=d.get(e.from),r=d.get(e.to);return!n||!r?null:(0,J.jsx)(`line`,{className:`topo-edge${m.has(e.to)?` is-dashed`:``}`,x1:n.x,y1:n.y,x2:r.x,y2:r.y,children:(0,J.jsxs)(`title`,{children:[e.from,` → `,e.to,e.interface?` (${e.interface})`:``]})},`e-${t}`)}),e.nodes.map(e=>{let t=d.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,J.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,J.jsx)(`rect`,{width:a,height:o,rx:9,style:{stroke:l}}),(0,J.jsx)(`text`,{className:`topo-label`,x:9,y:16,children:(e.label||e.id).slice(0,16)}),i?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`text`,{className:`topo-sub`,x:9,y:30,children:(e.board||e.ip||``).slice(0,18)}),(0,J.jsx)(`rect`,{className:`topo-bar-bg`,x:9,y:37,width:a-18,height:4,rx:2}),(0,J.jsx)(`rect`,{x:9,y:37,width:(a-18)*Math.min(100,e.cpuLoad??0)/100,height:4,rx:2,style:{fill:RY(e.cpuLoad)}}),(0,J.jsx)(`rect`,{className:`topo-bar-bg`,x:9,y:43,width:a-18,height:4,rx:2}),(0,J.jsx)(`rect`,{x:9,y:43,width:(a-18)*Math.min(100,e.memUsedPct??0)/100,height:4,rx:2,style:{fill:RY(e.memUsedPct)}})]}):(0,J.jsx)(`text`,{className:`topo-sub`,x:9,y:29,children:e.onboardable?`+ onboard`:e.ip||e.mac||``})]},e.id)})]}),(0,J.jsxs)(`div`,{className:`topo-foot`,children:[(0,J.jsxs)(`span`,{className:`legend`,children:[(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{className:`dot`,style:{background:`#d4d4d8`}}),` online`]}),(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{className:`dot`,style:{background:`#f87171`}}),` offline`]}),(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{className:`dot`,style:{background:`#e4e4e7`}}),` neighbour`]}),(0,J.jsxs)(`span`,{className:`muted`,children:[e.stats.devices,` devices · `,e.stats.neighbors,` discovered ·`,` `,e.stats.onboardable,` onboardable`]})]}),_&&(0,J.jsxs)(`div`,{className:`topo-pop`,children:[(0,J.jsxs)(`div`,{className:`topo-pop__hd`,children:[(0,J.jsx)(`strong`,{children:_.label}),(0,J.jsx)(`span`,{className:`muted`,children:[_.board,_.version,_.mac].filter(Boolean).join(` · `)||`no details advertised`}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`button`,{className:`topo-btn`,onClick:()=>r(null),children:`✕`})]}),_.suggestedConfig?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`div`,{className:`muted`,style:{margin:`2px 0 6px`},children:`Not managed yet — add this to your device config to onboard it:`}),(0,J.jsx)(`pre`,{className:`topo-stub`,children:JSON.stringify({[_.suggestedConfig.name]:BY(_.suggestedConfig)},null,2)}),(0,J.jsx)(bc,{className:`topo-btn`,title:`Copy config stub`,label:`Copy config stub`,text:JSON.stringify({[_.suggestedConfig.name]:BY(_.suggestedConfig)},null,2)}),t&&(0,J.jsx)(`button`,{className:`topo-btn cfg-save`,onClick:()=>t(_.suggestedConfig.name,BY(_.suggestedConfig)),children:`Add to config →`})]}):(0,J.jsxs)(`div`,{className:`muted`,children:[`Managed device`,_.ip?` · ${_.ip}`:``,_.uptime?` · up ${_.uptime}`:``]})]})]})]})}function BY(e){let t={port:e.port,username:e.username};return e.host&&(t.host=e.host),e.mac&&(t.mac=e.mac),t}Ri.registerPlugin(q);function VY(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 HY=[{id:`overview`,label:`Overview`,sub:`Calls, latency & risk at a glance`},{id:`devices`,label:`Devices`,sub:`Connectivity radar & system health`},{id:`clients`,label:`Clients`,sub:`Connected LAN devices — usage, block/allow, pin IP`},{id:`aaa`,label:`RADIUS & UM`,sub:`RADIUS client & User Manager RADIUS server`},{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:`backups`,label:`Backups`,sub:`Local config vault — create, restore, manage`},{id:`modules`,label:`Modules`,sub:`Enable/disable tool modules — curate the surface`},{id:`config`,label:`Config`,sub:`Effective configuration & safe editor`},{id:`feed`,label:`Live Feed`,sub:`Every tool call, in real time`}],UY=new Set(HY.map(e=>e.id)),WY=`mt-view`;function GY(){try{let e=location.hash.replace(/^#\/?/,``);return UY.has(e)?e:null}catch{return null}}function KY(){let e=GY();if(e)return e;try{let e=localStorage.getItem(WY);if(e&&UY.has(e))return e}catch{}return`overview`}var qY=[`#ededed`,`#a1a1a1`],JY={overview:qY,devices:qY,clients:qY,aaa:qY,topology:qY,packets:qY,snapshots:qY,plan:qY,s3:qY,backups:qY,modules:qY,config:qY,feed:qY},YY={overview:{what:`A live pulse of all MCP tool activity: total calls, error rate, p50/p95 latency, the busiest tools, and a risk breakdown — over a time window you choose.`,tips:[`Change the time window (top-right) to zoom from the last 5 minutes out to 24 hours.`,`The risk donut splits calls by annotation: read · write · destructive · dangerous.`,`A rising error line usually points at one device or one tool — jump to Live Feed to see which.`]},devices:{what:`Every configured router with its live reachability (SSH or MAC-Telnet), latency, identity, and system health — CPU, memory and disk — refreshed continuously.`,tips:[`Each device gets a stable colour so you can track it across the connectivity radar.`,`Health (CPU/Mem/Disk) is probed periodically; MAC-Telnet devices are probed on a slower cadence.`,`Latency tiers are colour-coded green → amber → red; a grey node is currently unreachable.`]},clients:{what:`The LAN devices connected to a router — merged from its DHCP leases and ARP table — with live Download/Upload charts, and one-click controls to block/allow a device, pin (reserve) its IP, change that IP, or relabel it.`,tips:[`Pick the router (top-right) to inspect its connected devices; filter by IP, MAC or name.`,`Click a device to open its live ↓/↑ traffic chart — needs a simple queue targeting its IP.`,`Block/allow is enforced by MAC, so it survives the device changing IP; “Pin IP” makes its lease static.`]},aaa:{what:"Full management of the router's RADIUS client (`/radius`) and the built-in User Manager RADIUS server (`/user-manager`): RADIUS servers + incoming CoA, and User Manager users, service profiles, rate/quota limitations, NAS clients, profile assignments, accounting sessions, and global settings.",tips:[`Pick the router (top-right), then switch tabs across RADIUS, Users, Profiles, Limitations, NAS, Assignments, Sessions and Settings.`,`Every tab is full CRUD: add, edit, enable/disable and remove — secrets are write-only and shown redacted.`,`The Usage & Heatmap tab shows each user's 3-month download/upload and a GitHub-style connection heatmap, persisted locally.`,`If a device lacks the user-manager package, the User Manager tabs explain how to install it; RADIUS-client tabs still work.`]},topology:{what:`A Layer-2 map of neighbours each router discovers via MNDP / CDP / LLDP — the physical adjacency of your network, drawn live.`,tips:[`Solid nodes are configured devices; faint nodes are discovered-but-unmanaged neighbours.`,`Use “Add to config →” on an unmanaged neighbour to pre-fill it in the Config editor.`,`Drag to pan; the layout settles automatically as new neighbours arrive.`]},packets:{what:`Live packet capture streamed from a router over TZSP — decode headers in real time without leaving the dashboard.`,tips:[`Pick a device and start the capture; packets decode as they arrive.`,`Stop the capture when done — it frees the router-side sniffer.`,`Great for debugging a protocol issue alongside the Live Feed of tool calls.`]},snapshots:{what:`Point-in-time captures of a device’s full configuration (/export), stored locally so you can diff any two and see exactly what changed.`,tips:[`Capture a snapshot before a risky change, then diff after to audit the delta.`,`The diff is line-level: green added, red removed.`,`Snapshots are device config exports — for the dashboard’s OWN config history see the Config page.`]},plan:{what:`Dry-run the exact RouterOS commands a change would run before it touches a device — a change plan you can review and trust.`,tips:[`Paste or build intended commands to see them validated and ordered.`,`Nothing is sent to the device from here — it’s a preview.`,`Pair with Safe Mode (auto-revert) when you do apply for real.`]},s3:{what:`Browse, download, and delete backup objects in your configured S3-compatible bucket — your off-box archive of device backups and exports.`,tips:[`Filter by key prefix to find a device’s backups quickly.`,`Download fetches the object through a presigned URL; delete is permanent.`,`For host-side .rsc backups instead, use the Backups page.`]},backups:{what:`A local config vault on the MCP server: capture a device’s /export as a timestamped .rsc file, then download, upload, rename, restore (via Safe Mode), or delete it.`,tips:[`Restore offers a dry-run (applies then rolls back) before you commit for real.`,`Edit the vault path inline in the header — it’s saved to your config.`,`Filenames are stamped in the device’s local 24-hour clock.`]},modules:{what:"Every tool module in the catalog with a live on/off switch. Toggling one writes your config file's `tools` block (disabledModules / enabledModules) and applies it immediately, so you can curate exactly which scopes the MCP server exposes.",tips:[`MCP clients search-rank tools and degrade past ~100; trim the surface below ~150–200 tools so every remaining tool is reliably findable.`,`Disable adds the module to tools.disabledModules; enable removes it (or adds it to an active allow-list) — your config file is updated live on each toggle.`,`Changes need an MCP client reconnect (or a server restart) to actually shrink/grow the visible tool list.`]},config:{what:`View the effective configuration, edit it safely with schema-aware validation and auto-rollback, browse a full field guide, and travel through config version history.`,tips:[`Every successful apply is auto-saved to the version timeline — restore any point in time.`,`Save a named checkpoint before a big change for an easy, labelled rollback.`,`The Field Guide documents every config option, its type, and default — straight from the schema.`]},feed:{what:`Every tool call as it happens — tool, device, risk, duration, and success/error — with full request/response detail on click.`,tips:[`Filter by status to isolate failures, or by tool/device to follow one thread.`,`Click any row to open the full (secret-redacted) request and response.`,`Pause the stream when you want to inspect without rows shifting under you.`]}};function XY({view:e}){let t=YY[e];return(0,J.jsxs)(`div`,{className:`pagehelp reveal`,role:`region`,"aria-label":`Page help`,children:[(0,J.jsx)(`div`,{className:`pagehelp__icon`,"aria-hidden":`true`,children:`?`}),(0,J.jsxs)(`div`,{className:`pagehelp__body`,children:[(0,J.jsx)(`p`,{className:`pagehelp__what`,children:t.what}),(0,J.jsx)(`ul`,{className:`pagehelp__tips`,children:t.tips.map((e,t)=>(0,J.jsx)(`li`,{children:e},t))})]})]})}function ZY({name:e}){return(0,J.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.7`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:{overview:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`rect`,{x:`3`,y:`3`,width:`8`,height:`8`,rx:`1.6`}),(0,J.jsx)(`rect`,{x:`13`,y:`3`,width:`8`,height:`5`,rx:`1.6`}),(0,J.jsx)(`rect`,{x:`13`,y:`10`,width:`8`,height:`11`,rx:`1.6`}),(0,J.jsx)(`rect`,{x:`3`,y:`13`,width:`8`,height:`8`,rx:`1.6`})]}),devices:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`rect`,{x:`3`,y:`4`,width:`18`,height:`7`,rx:`2`}),(0,J.jsx)(`rect`,{x:`3`,y:`13`,width:`18`,height:`7`,rx:`2`}),(0,J.jsx)(`path`,{d:`M7 7.5h.01M7 16.5h.01`})]}),clients:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`circle`,{cx:`9`,cy:`8`,r:`3`}),(0,J.jsx)(`path`,{d:`M3.5 19a5.5 5.5 0 0 1 11 0`}),(0,J.jsx)(`path`,{d:`M16 7.5a2.5 2.5 0 0 1 0 5M17.5 19a4.5 4.5 0 0 0-2-3.6`})]}),aaa:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`path`,{d:`M12 3 4 6v5c0 4.4 3.2 7.6 8 9 4.8-1.4 8-4.6 8-9V6l-8-3Z`}),(0,J.jsx)(`path`,{d:`M9.5 11.5 11 13l3.5-3.5`})]}),topology:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`circle`,{cx:`12`,cy:`5`,r:`2.4`}),(0,J.jsx)(`circle`,{cx:`5`,cy:`19`,r:`2.4`}),(0,J.jsx)(`circle`,{cx:`19`,cy:`19`,r:`2.4`}),(0,J.jsx)(`path`,{d:`M12 7.4 6.4 16.6M12 7.4 17.6 16.6`})]}),packets:(0,J.jsx)(`path`,{d:`M3 12h4l2-7 4 14 2-7h6`}),snapshots:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`path`,{d:`M12 3 3 7.5 12 12 21 7.5 12 3Z`}),(0,J.jsx)(`path`,{d:`M3 12 12 16.5 21 12`}),(0,J.jsx)(`path`,{d:`M3 16.5 12 21 21 16.5`})]}),plan:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`circle`,{cx:`6`,cy:`6`,r:`2.3`}),(0,J.jsx)(`circle`,{cx:`6`,cy:`18`,r:`2.3`}),(0,J.jsx)(`circle`,{cx:`18`,cy:`8`,r:`2.3`}),(0,J.jsx)(`path`,{d:`M6 8.3v7.4M8.3 6H13a3 3 0 0 1 3 3v0`})]}),s3:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`ellipse`,{cx:`12`,cy:`6`,rx:`7`,ry:`2.6`}),(0,J.jsx)(`path`,{d:`M5 6v12c0 1.5 3.1 2.6 7 2.6s7-1.1 7-2.6V6`}),(0,J.jsx)(`path`,{d:`M5 12c0 1.5 3.1 2.6 7 2.6s7-1.1 7-2.6`})]}),backups:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`path`,{d:`M3 6.5 5 3.5h14l2 3`}),(0,J.jsx)(`rect`,{x:`3`,y:`6.5`,width:`18`,height:`14`,rx:`2`}),(0,J.jsx)(`path`,{d:`M9.5 12h5`})]}),modules:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`rect`,{x:`3`,y:`5`,width:`18`,height:`6`,rx:`3`}),(0,J.jsx)(`circle`,{cx:`8`,cy:`8`,r:`1.5`}),(0,J.jsx)(`rect`,{x:`3`,y:`13`,width:`18`,height:`6`,rx:`3`}),(0,J.jsx)(`circle`,{cx:`16`,cy:`16`,r:`1.5`})]}),config:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`path`,{d:`M4 7h8M16 7h4M4 17h4M12 17h8`}),(0,J.jsx)(`circle`,{cx:`14`,cy:`7`,r:`2.2`}),(0,J.jsx)(`circle`,{cx:`10`,cy:`17`,r:`2.2`})]}),feed:(0,J.jsx)(`path`,{d:`M4 6h16M4 12h16M4 18h10`})}[e]})}function QY(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(null),[i,a]=(0,v.useState)(null),[o,s]=(0,v.useState)(null),[c,l]=(0,v.useState)(null),[u,d]=(0,v.useState)(!1),[f,p]=(0,v.useState)(null),[m,h]=(0,v.useState)([]),[g,_]=(0,v.useState)(36e5),[y,b]=(0,v.useState)(!1),[x,S]=(0,v.useState)(`off`),[C,w]=(0,v.useState)(null),[T,E]=(0,v.useState)(()=>new Set),[D,O]=(0,v.useState)({}),[k,A]=(0,v.useState)({tool:``,risk:``,device:``,status:``,q:``}),j=(0,v.useRef)(y);j.current=y;let M=(0,v.useRef)(null);AY(M);let[N,P]=(0,v.useState)(KY),F=(0,v.useCallback)(e=>{P(e);try{GY()!==e&&(location.hash=e),localStorage.setItem(WY,e)}catch{}},[]);(0,v.useEffect)(()=>{if(GY()!==N)try{location.hash=N}catch{}let e=()=>{let e=GY();e&&F(e)};return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]);let[I,L]=(0,v.useState)(()=>{try{let e=localStorage.getItem(`mt-help-open`);return new Set(e?JSON.parse(e):[])}catch{return new Set}}),ee=e=>L(t=>{let n=new Set(t);n.has(e)?n.delete(e):n.add(e);try{localStorage.setItem(`mt-help-open`,JSON.stringify([...n]))}catch{}return n}),[te,R]=(0,v.useState)(``),[z,ne]=(0,v.useState)(`all`),B=(0,v.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]),V=(0,v.useMemo)(()=>{let e=i?.devices??[],t=te.trim().toLowerCase();return e.filter(e=>!(z===`online`&&e.status.reachable!==!0||z===`offline`&&e.status.reachable!==!1||t&&!e.name.toLowerCase().includes(t)&&!(e.address??e.host??``).toLowerCase().includes(t)))},[i,te,z]);kY((0,v.useCallback)(e=>{if(j.current)return;h(t=>[e,...t].slice(0,kc));let t=e.device;t&&O(e=>({...e,[t]:(e[t]??0)+1}))},[]),(0,v.useCallback)(e=>S(e),[])),(0,v.useEffect)(()=>{fc(`/api/events?limit=${kc}`).then(e=>h(e.events)).catch(()=>{})},[]);let re=(0,v.useCallback)(()=>{fc(`/api/stats?window=${g}&buckets=60`).then(t).catch(()=>{})},[g]);(0,v.useEffect)(()=>{re();let e=()=>{re(),fc(`/api/meta`).then(r).catch(()=>{}),fc(`/api/devices`).then(a).catch(()=>{}),fc(`/api/topology`).then(s).catch(()=>{})};e();let t=setInterval(()=>{j.current||e()},4e3);return()=>clearInterval(t)},[re]),(0,v.useEffect)(()=>{let e=()=>void fc(`/api/config`).then(l).catch(()=>{});e();let t=setInterval(e,3e4);return()=>clearInterval(t)},[]),(0,v.useEffect)(()=>{let e=e=>{e.key===`Escape`&&w(null)};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]);let ie=(0,v.useMemo)(()=>{let e=k.q.trim().toLowerCase();return m.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)},[m,k]),ae=!!(k.tool||k.risk||k.device||k.status||k.q),oe=(0,v.useMemo)(()=>m.filter(e=>e.isError),[m]),se=(0,v.useMemo)(()=>({ok:m.length-oe.length,error:oe.length}),[m,oe]),H=(0,v.useCallback)(async e=>{try{w(await fc(`/api/event/${encodeURIComponent(e.id)}`))}catch{w(e)}},[]),ce=(0,v.useMemo)(()=>ie.slice(0,200),[ie]),le=(0,v.useMemo)(()=>ce.map(e=>e.id),[ce]),ue=le.length>0&&le.every(e=>T.has(e)),de=!ue&&le.some(e=>T.has(e)),fe=(0,v.useCallback)(e=>{E(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),pe=(0,v.useCallback)(()=>{E(e=>{let t=new Set(e);if(le.length>0&&le.every(e=>t.has(e)))for(let e of le)t.delete(e);else for(let e of le)t.add(e);return t})},[le]),[me,he]=(0,v.useState)(!1),ge=(0,v.useCallback)(async()=>{let e=[...T];if(e.length!==0)try{await mc({ids:e});let t=new Set(e);h(e=>e.filter(e=>!t.has(e.id))),E(new Set)}catch{}finally{he(!1)}},[T]),_e=e=>{if(e===`json`){VY(`mcp-events.json`,JSON.stringify(ie,null,2),`application/json`);return}let t=[`ts`,`tool`,`risk`,`device`,`durationMs`,`isError`,`error`],n=e=>/[",\n]/.test(e)?`"${e.replace(/"/g,`""`)}"`:e,r=ie.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(`
|
|
81
|
-
`);VY(`mcp-events.csv`,`${t.join(`,`)}\n${r}\n`,`text/csv`)},ve=e?e.errorRate>=.2?`is-bad`:e.errorRate>=.05?`is-warn`:`is-good`:``,ye=c?.mcp??{},be=c?.dashboard??{},xe=(e,t,n)=>(0,J.jsxs)(`select`,{className:`btn`,value:k[e],onChange:t=>A(n=>({...n,[e]:t.target.value})),children:[(0,J.jsx)(`option`,{value:``,children:t}),n.map(e=>(0,J.jsx)(`option`,{value:e,children:e},e))]}),Se=HY.find(e=>e.id===N)??HY[0];return(0,J.jsxs)(`div`,{className:`shell`,ref:M,"data-view":N,style:{"--page-accent":JY[N][0],"--page-accent-2":JY[N][1]},children:[(0,J.jsxs)(`aside`,{className:`nav`,children:[(0,J.jsxs)(`div`,{className:`nav__brand`,children:[(0,J.jsx)(`div`,{className:`nav__mark`,children:(0,J.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,"aria-hidden":`true`,children:(0,J.jsxs)(`g`,{stroke:`#18181b`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,J.jsx)(`path`,{d:`M12 12 L4 5 M12 12 L20 5 M12 12 L12 20`}),(0,J.jsx)(`circle`,{cx:`12`,cy:`12`,r:`3`,fill:`#18181b`,stroke:`none`}),(0,J.jsx)(`circle`,{cx:`4`,cy:`5`,r:`1.9`,fill:`#18181b`,stroke:`none`}),(0,J.jsx)(`circle`,{cx:`20`,cy:`5`,r:`1.9`,fill:`#18181b`,stroke:`none`}),(0,J.jsx)(`circle`,{cx:`12`,cy:`20`,r:`1.9`,fill:`#18181b`,stroke:`none`})]})})}),(0,J.jsxs)(`div`,{className:`nav__brandtext`,children:[(0,J.jsx)(`b`,{children:`MikroTik MCP`}),(0,J.jsx)(`small`,{children:`Observability`})]})]}),(0,J.jsx)(`nav`,{className:`nav__items`,children:HY.map(e=>(0,J.jsxs)(`button`,{className:`nav__item${N===e.id?` is-active`:``}`,onClick:()=>F(e.id),children:[(0,J.jsx)(ZY,{name:e.id}),(0,J.jsx)(`span`,{children:e.label}),e.id===`feed`&&m.length>0&&(0,J.jsx)(`span`,{className:`nav__badge`,children:m.length>999?`999+`:m.length},m.length)]},e.id))}),(0,J.jsxs)(`div`,{className:`nav__foot`,children:[(0,J.jsxs)(`span`,{className:`hero__live${x===`off`?``:` is-on`}${x===`ws`?` is-ws`:x===`sse`?` is-sse`:``}`,title:`Live transport: WebSocket (preferred) or SSE fallback`,children:[(0,J.jsx)(`span`,{className:`dot`}),x===`off`?`offline`:`live · ${x}`]}),(0,J.jsx)(`small`,{className:`muted`,children:n?`${wc(n.total)} events · ${n.transport}`:`connecting…`})]})]}),(0,J.jsxs)(`main`,{className:`main`,"data-view":N,children:[(0,J.jsxs)(`header`,{className:`topline reveal`,children:[(0,J.jsxs)(`div`,{className:`topline__txt`,children:[(0,J.jsx)(`h1`,{children:Se.label}),(0,J.jsx)(`small`,{children:Se.sub})]}),(0,J.jsx)(`span`,{className:`topline__spacer`}),N===`overview`&&(0,J.jsx)(`select`,{className:`btn`,value:g,onChange:e=>_(Number(e.target.value)),title:`Stats time window`,children:Oc.map(([e,t])=>(0,J.jsxs)(`option`,{value:t,children:[`window: `,e]},t))}),(0,J.jsxs)(`button`,{className:`help-toggle${I.has(N)?` is-on`:``}`,onClick:()=>ee(N),"aria-expanded":I.has(N),title:`About this page`,children:[(0,J.jsx)(`span`,{className:`help-toggle__q`,"aria-hidden":`true`,children:`?`}),`Help`]})]}),I.has(N)&&(0,J.jsx)(XY,{view:N}),N===`overview`&&(0,J.jsxs)(`section`,{className:`view`,children:[(0,J.jsx)(`div`,{className:`cards reveal`,children:e?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(_c,{k:`Calls (window)`,v:wc(e.total)}),(0,J.jsx)(_c,{k:`Calls / min`,v:e.callsPerMin.toFixed(1)}),(0,J.jsx)(_c,{k:`Error rate`,v:`${(e.errorRate*100).toFixed(1)}%`,sub:`${e.errors} err`,cls:ve}),(0,J.jsx)(_c,{k:`Avg latency`,v:xc(e.latency.avg)}),(0,J.jsx)(_c,{k:`p95 latency`,v:xc(e.latency.p95)}),(0,J.jsx)(_c,{k:`p99 latency`,v:xc(e.latency.p99)}),(0,J.jsx)(_c,{k:`Distinct tools`,v:wc(e.distinctTools)}),(0,J.jsx)(_c,{k:`Output volume`,v:Sc(e.outputBytes)})]}):(0,J.jsxs)(`div`,{className:`stat`,children:[(0,J.jsx)(`p`,{className:`k`,children:`Loading…`}),(0,J.jsx)(`div`,{className:`v`,children:`—`})]})}),(0,J.jsxs)(`div`,{className:`bento reveal`,children:[(0,J.jsx)(gc,{title:`Calls over time`,className:`b-series`,children:e?(0,J.jsx)(zJ,{series:e.series}):(0,J.jsx)(`div`,{className:`muted`,children:`no data`})}),e&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(gc,{title:`By risk`,className:`b-risk`,children:(0,J.jsx)(BJ,{segments:Object.keys(e.byRisk).map(t=>({label:t,value:e.byRisk[t],color:Dc[t]}))})}),(0,J.jsx)(gc,{title:`Top tools`,className:`b-tools`,children:(0,J.jsx)(vc,{rows:e.byTool.map(e=>({label:e.tool,value:e.count,sub:`${e.count}× · ${xc(e.p95Ms)} p95${e.errors?` · ${e.errors} err`:``}`,color:e.errors?`var(--mt-bad)`:void 0}))})}),(0,J.jsx)(gc,{title:`Status`,className:`b-status`,children:(0,J.jsx)(BJ,{centerLabel:`calls`,segments:[{label:`ok`,value:se.ok,color:`#a1a1a1`},{label:`error`,value:se.error,color:`#ff5c5c`}]})}),(0,J.jsx)(gc,{title:`By device`,className:`b-device`,children:e.byDevice.length?(0,J.jsx)(vc,{rows:e.byDevice.map(e=>({label:e.device,value:e.count}))}):(0,J.jsx)(`div`,{className:`muted`,children:`single device`})}),(0,J.jsx)(gc,{title:`Recent errors`,className:`b-errors`,children:oe.length?(0,J.jsx)(`div`,{className:`hbar`,children:oe.slice(0,8).map(e=>(0,J.jsxs)(`div`,{className:`hbar__row conn-errrow`,style:{gridTemplateColumns:`auto 1fr`},onClick:()=>void H(e),children:[(0,J.jsx)(`span`,{className:`muted`,children:Cc(e.ts)}),(0,J.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,J.jsx)(`div`,{className:`muted`,children:`no errors 🎉`})})]})]})]}),N===`devices`&&(i&&i.devices.length>0?(0,J.jsxs)(`section`,{className:`view`,children:[(0,J.jsxs)(`div`,{className:`dev-toolbar reveal`,children:[(0,J.jsx)(`input`,{className:`search`,type:`search`,placeholder:`Search devices by name or address…`,value:te,onChange:e=>R(e.target.value),style:{flex:1,minWidth:180}}),(0,J.jsx)(`div`,{className:`dev-filters`,children:[`all`,`online`,`offline`].map(e=>(0,J.jsx)(`button`,{className:`dev-fbtn${z===e?` is-active`:``}`,onClick:()=>ne(e),children:e===`all`?`All ${B.total}`:e===`online`?`Online ${B.online}`:`Offline ${B.offline}`},e))}),(0,J.jsxs)(`span`,{className:`muted`,children:[V.length,`/`,B.total,` shown`]})]}),(0,J.jsxs)(`details`,{className:`dev-collapse reveal`,open:B.total<=8,children:[(0,J.jsxs)(`summary`,{children:[`Connectivity radar`,(0,J.jsxs)(`span`,{className:`muted`,children:[` `,`· `,B.online,` online · `,B.offline,` offline ·`,` `,B.total,` total`]})]}),(0,J.jsx)(wY,{payload:i,pulses:D})]}),V.length===0?(0,J.jsxs)(`div`,{className:`feed-empty reveal`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`🔍`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`No devices match`}),(0,J.jsx)(`p`,{className:`feed-empty__sub`,children:`Try a different search or status filter.`})]}):(0,J.jsx)(`div`,{className:`dev-grid-wide reveal`,children:V.map(e=>(0,J.jsx)(TY,{d:e},e.name))}),V.length>0&&(0,J.jsx)(gc,{title:`Device system health`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:`CPU · memory · disk · latency · live probe`}),children:(0,J.jsx)(`div`,{className:`health-grid`,children:V.map(e=>(0,J.jsx)(OY,{d:e},e.name))})})]}):(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`🖧`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`No devices configured`}),(0,J.jsx)(jc,{type:`secondary`,label:`Tip`,children:`Add a device to your config to see connectivity and system health here.`})]})),N===`clients`&&(0,J.jsx)(XJ,{}),N===`aaa`&&(0,J.jsx)(pl,{}),N===`topology`&&(o&&o.nodes.length>0?(0,J.jsx)(`section`,{className:`view`,children:(0,J.jsx)(gc,{title:`Network topology`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:`Layer-2 neighbours via MNDP/CDP/LLDP · click a neighbour to onboard it`}),children:(0,J.jsx)(zY,{topo:o,onOnboard:(e,t)=>{p({name:e,body:t}),d(!0),F(`config`)}})})}):(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`🛰️`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`No neighbours discovered yet`}),(0,J.jsx)(`p`,{className:`feed-empty__sub`,children:`Layer-2 neighbours (MNDP / CDP / LLDP) appear here as the device reports them.`})]})),N===`packets`&&(0,J.jsx)(`section`,{className:`view`,children:(0,J.jsx)(gc,{title:`Packet capture`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:`live TZSP decode · /tool sniffer streaming`}),children:(0,J.jsx)(FY,{})})}),N===`snapshots`&&(0,J.jsx)(LY,{}),N===`plan`&&(0,J.jsx)(hl,{}),N===`s3`&&(0,J.jsx)(IY,{}),N===`backups`&&(0,J.jsx)(Lc,{}),N===`modules`&&(0,J.jsx)(MY,{}),N===`config`&&(c?(0,J.jsxs)(`section`,{className:`view`,children:[(0,J.jsx)(gc,{title:`Configuration`,className:`reveal`,extra:(0,J.jsx)(`button`,{className:`btn`,onClick:()=>d(e=>!e),title:`Edit the config JSON with autocomplete, validation and safe-apply`,children:u?`View`:`✎ Edit config`}),children:u?(0,J.jsx)(mY,{initial:f?{...c,devices:{...c.devices,[f.name]:f.body}}:c,onClose:()=>{d(!1),p(null)},onReload:()=>{p(null),fc(`/api/config`).then(l).catch(()=>{})}},f?`seed-${f.name}`:`config`):(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`div`,{className:`legend`,style:{margin:`0 0 10px`},children:[(0,J.jsxs)(`span`,{children:[`transport: `,Tc(ye.transport)]}),(0,J.jsxs)(`span`,{children:[`read-only: `,c.readOnly?`yes`:`no`]}),(0,J.jsxs)(`span`,{children:[`dashboard: `,Tc(be.host),`:`,Tc(be.port)]}),(0,J.jsxs)(`span`,{children:[`capture: `,be.captureBody?`on`:`off`]}),(0,J.jsxs)(`span`,{children:[`s3: `,c.s3?`configured`:`off`]})]}),(0,J.jsxs)(`details`,{className:`cfg`,children:[(0,J.jsx)(`summary`,{children:`Full effective configuration (secrets redacted)`}),(0,J.jsx)(uY,{value:c,maxHeight:340})]})]})}),(0,J.jsx)(gc,{title:`Version history`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:`point-in-time snapshots · diff & restore`}),children:(0,J.jsx)($J,{onRestored:()=>void fc(`/api/config`).then(l).catch(()=>{})})}),(0,J.jsx)(gc,{title:`Field guide`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:`every config option, documented from the schema`}),children:(0,J.jsx)(tY,{})})]}):(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(Pc,{}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`Loading configuration…`})]})),N===`feed`&&(0,J.jsxs)(`div`,{className:`panel reveal`,children:[(0,J.jsxs)(`div`,{className:`sheet__hd`,style:{marginBottom:12},children:[(0,J.jsx)(`h2`,{style:{margin:0},children:`Live tool calls`}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsxs)(`span`,{className:`muted`,children:[ie.length,` shown · `,m.length,` buffered`]})]}),(0,J.jsxs)(`div`,{className:`toolbar`,style:{marginBottom:12},children:[(0,J.jsx)(`div`,{className:`grow`,style:{flex:1,minWidth:180},children:(0,J.jsx)(`input`,{className:`search`,type:`search`,placeholder:`Search tool / input / output / error…`,value:k.q,onChange:e=>A(t=>({...t,q:e.target.value}))})}),xe(`tool`,`all tools`,n?.tools??[]),xe(`risk`,`all risk`,[`READ`,`WRITE`,`WRITE_IDEMPOTENT`,`DESTRUCTIVE`,`DANGEROUS`]),xe(`device`,`all devices`,n?.devices??[]),xe(`status`,`all status`,[`ok`,`error`]),(0,J.jsx)(`button`,{className:`btn${y?` is-active`:``}`,onClick:()=>b(e=>!e),children:y?`▶ Resume`:`⏸ Pause`}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>_e(`csv`),children:`CSV`}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>_e(`json`),children:`JSON`}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>A({tool:``,risk:``,device:``,status:``,q:``}),children:`Clear`}),me&&T.size>0?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`button`,{className:`btn btn-danger`,onClick:()=>void ge(),children:[`✓ Confirm delete (`,T.size,`)`]}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>he(!1),children:`Cancel`})]}):(0,J.jsxs)(`button`,{className:`btn`,disabled:T.size===0,onClick:()=>he(!0),title:`Delete the selected rows`,children:[`🗑 Delete`,T.size>0?` (${T.size})`:``]})]}),ie.length===0?ae?(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`🔍`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`No calls match your filters`}),(0,J.jsxs)(`p`,{className:`feed-empty__sub`,children:[m.length,` call`,m.length===1?``:`s`,` buffered — try widening the search or the risk / device / status filters.`]}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>A({tool:``,risk:``,device:``,status:``,q:``}),children:`Clear filters`})]}):(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__pulse${x===`off`?``:` is-on`}`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:x===`off`?`Not connected`:`Listening for tool calls…`}),(0,J.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,J.jsx)(`div`,{className:`feedwrap`,children:(0,J.jsxs)(`table`,{className:`feed`,children:[(0,J.jsx)(`thead`,{children:(0,J.jsxs)(`tr`,{children:[(0,J.jsx)(`th`,{style:{width:28},children:(0,J.jsx)(`input`,{type:`checkbox`,"aria-label":`Select all shown rows`,checked:ue,ref:e=>{e&&(e.indeterminate=de)},onChange:pe})}),(0,J.jsx)(`th`,{children:`time`}),(0,J.jsx)(`th`,{children:`tool`}),(0,J.jsx)(`th`,{children:`risk`}),(0,J.jsx)(`th`,{children:`device`}),(0,J.jsx)(`th`,{className:`num`,children:`dur`}),(0,J.jsx)(`th`,{children:`status`}),(0,J.jsx)(`th`,{children:`output`})]})}),(0,J.jsx)(`tbody`,{children:ce.map(e=>(0,J.jsxs)(`tr`,{className:`${e.isError?`is-err`:``}${T.has(e.id)?` is-selected`:``}`.trim()||void 0,onClick:()=>void H(e),children:[(0,J.jsx)(`td`,{onClick:e=>e.stopPropagation(),children:(0,J.jsx)(`input`,{type:`checkbox`,"aria-label":`Select row`,checked:T.has(e.id),onChange:()=>fe(e.id)})}),(0,J.jsx)(`td`,{children:Cc(e.ts)}),(0,J.jsx)(`td`,{children:e.tool}),(0,J.jsx)(`td`,{children:(0,J.jsx)(`span`,{className:`risk risk-${e.risk}`,children:e.risk.replace(`WRITE_IDEMPOTENT`,`WRITE·I`)})}),(0,J.jsx)(`td`,{children:e.device??`—`}),(0,J.jsx)(`td`,{className:`num`,children:xc(e.durationMs)}),(0,J.jsx)(`td`,{children:(0,J.jsx)(`span`,{className:e.isError?`status-err`:`status-ok`,children:e.isError?`error`:`ok`})}),(0,J.jsx)(`td`,{className:`preview`,children:e.isError?e.error??`error`:e.output||`—`})]},e.id))})]})})]})]}),C&&(0,J.jsx)(EY,{event:C,onClose:()=>w(null)})]})}var $Y=document.getElementById(`root`);$Y&&(0,y.createRoot)($Y).render((0,J.jsx)(QY,{}));
|
|
79
|
+
`).map((e,t)=>(0,J.jsx)(`div`,{className:e.startsWith(`+`)?`d-add`:e.startsWith(`-`)?`d-del`:e.startsWith(`@@`)?`d-hunk`:``,children:e||` `},t))})]})]})}function hY(e){return e.reachable===!0?{label:`online`,color:`#d4d4d8`}:e.reachable===!1?{label:`offline`,color:`#f87171`}:{label:`checking…`,color:`#71717a`}}function gY(e){let t=0;for(let n=0;n<e.length;n++)t=Math.imul(t,31)+e.charCodeAt(n)>>>0;return`hsl(${t%360} 70% 62%)`}function _Y(e,t,n,r){let i=1-r;return{x:i*i*e.x+2*i*r*t.x+r*r*n.x,y:i*i*e.y+2*i*r*t.y+r*r*n.y}}function vY(e,t,n,r){let i=1-r,a=2*i*(t.x-e.x)+2*r*(n.x-t.x),o=2*i*(t.y-e.y)+2*r*(n.y-t.y);return Math.atan2(o,a)*180/Math.PI}var yY=`#e4e4e7`,bY=`#d4d4d8`,xY=`#f59e0b`;function SY(e){return e.jumpVia?e.jumpVia:e.jumpHost?`${e.jumpHost.host}:${e.jumpHost.port}`:null}var CY=18,wY=e=>e.length>CY?`${e.slice(0,CY-1)}…`:e,TY=e=>Math.max(23,Math.min(70,e.length*3.1+12));function EY({payload:e,pulses:t}){let n=e.devices,r=Math.max(1,n.length),i=n.map(e=>{let t=wY(e.name);return{d:e,label:t,r:TY(t)}}),a=Math.max(23,...i.map(e=>e.r)),o=r>1?(a+18)/Math.sin(Math.PI/r):0,s=Math.max(110+a,o,120),c=a+60,l=Math.max(700,Math.round((s+a+40)*2)),u=Math.round((s+c)*2),d=l/2,f=u/2,p={x:d,y:f},m=i.map((e,t)=>{let n=t/r*Math.PI*2-Math.PI/2+(r%2==0?Math.PI/r:0);return{...e,i:t,x:d+s*Math.cos(n),y:f+s*Math.sin(n)}});return(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`svg`,{className:`conn`,viewBox:`0 0 ${l} ${u}`,width:`100%`,height:u,preserveAspectRatio:`xMidYMid meet`,children:[(0,J.jsxs)(`defs`,{children:[(0,J.jsxs)(`radialGradient`,{id:`conn-hub`,cx:`0.5`,cy:`0.34`,r:`0.75`,children:[(0,J.jsx)(`stop`,{offset:`0`,stopColor:`#fafafa`}),(0,J.jsx)(`stop`,{offset:`0.6`,stopColor:`#d4d4d8`}),(0,J.jsx)(`stop`,{offset:`1`,stopColor:`#a1a1aa`})]}),(0,J.jsxs)(`radialGradient`,{id:`conn-orb`,cx:`0.5`,cy:`0.32`,r:`0.85`,children:[(0,J.jsx)(`stop`,{offset:`0`,stopColor:`#27272a`}),(0,J.jsx)(`stop`,{offset:`1`,stopColor:`#18181b`})]}),(0,J.jsxs)(`radialGradient`,{id:`conn-burst`,cx:`0.5`,cy:`0.5`,r:`0.5`,children:[(0,J.jsx)(`stop`,{offset:`0`,stopColor:`#fafafa`}),(0,J.jsx)(`stop`,{offset:`0.5`,stopColor:`#e4e4e7`}),(0,J.jsx)(`stop`,{offset:`1`,stopColor:`#a1a1aa`,stopOpacity:`0`})]}),(0,J.jsxs)(`linearGradient`,{id:`conn-tunnel-grad`,x1:`0`,y1:`0`,x2:`1`,y2:`0`,children:[(0,J.jsx)(`stop`,{offset:`0`,stopColor:xY,stopOpacity:`0.15`}),(0,J.jsx)(`stop`,{offset:`0.5`,stopColor:xY,stopOpacity:`0.95`}),(0,J.jsx)(`stop`,{offset:`1`,stopColor:xY,stopOpacity:`0.15`})]}),(0,J.jsxs)(`filter`,{id:`conn-tunnel-glow`,x:`-40%`,y:`-40%`,width:`180%`,height:`180%`,children:[(0,J.jsx)(`feGaussianBlur`,{stdDeviation:`3`,result:`b`}),(0,J.jsxs)(`feMerge`,{children:[(0,J.jsx)(`feMergeNode`,{in:`b`}),(0,J.jsx)(`feMergeNode`,{in:`SourceGraphic`})]})]})]}),[.5,.78,1].map((e,t)=>(0,J.jsx)(`circle`,{className:`conn-grid`,cx:d,cy:f,r:s*e},`g-${t}`)),[0,1,2].map(e=>(0,J.jsx)(`circle`,{className:`conn-sonar`,cx:d,cy:f,style:{animationDelay:`${e*1.1}s`}},`s-${e}`)),m.map(({d:e,i:n,x:r,y:i})=>{let a=hY(e.status),o=e.status.reachable===!0,s=e.status.reachable==null,c={x:r,y:i},l=(d+r)/2,u=(f+i)/2,m=r-d,h=i-f,g=Math.hypot(m,h)||1,_=-h/g,v=m/g,y={x:l+_*16,y:u+v*16},b={x:l-_*16,y:u-v*16},x=`M${d},${f} Q${y.x.toFixed(1)},${y.y.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,S=`M${d},${f} Q${b.x.toFixed(1)},${b.y.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,C=`M${d},${f} Q${l.toFixed(1)},${u.toFixed(1)} ${r.toFixed(1)},${i.toFixed(1)}`,w=_Y(p,y,c,.52),T=vY(p,y,c,.52),E=_Y(p,b,c,.48),D=vY(p,b,c,.48)+180,O=t[e.name]??0;return(0,J.jsxs)(`g`,{children:[(0,J.jsx)(`path`,{id:`conn-cmd-${n}`,className:`conn-link`,d:x,stroke:o?yY:a.color,strokeOpacity:o?.5:.32,strokeDasharray:s?`2 8`:o?void 0:`6 7`}),(0,J.jsx)(`path`,{id:`conn-res-${n}`,className:`conn-link`,d:S,stroke:o?bY:a.color,strokeOpacity:o?.5:.18,strokeDasharray:o?void 0:`6 7`}),o&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`path`,{className:`conn-flow`,d:x,stroke:`#e4e4e7`}),(0,J.jsx)(`circle`,{className:`conn-packet`,r:3.2,fill:`#e4e4e7`,children:(0,J.jsx)(`animateMotion`,{dur:`2.6s`,repeatCount:`indefinite`,calcMode:`linear`,children:(0,J.jsx)(`mpath`,{href:`#conn-cmd-${n}`})})}),(0,J.jsx)(`rect`,{className:`conn-packet`,x:-2.6,y:-2.6,width:5.2,height:5.2,fill:`#d4d4d8`,transform:`rotate(45)`,children:(0,J.jsx)(`animateMotion`,{dur:`2.6s`,begin:`0.9s`,repeatCount:`indefinite`,calcMode:`linear`,keyPoints:`1;0`,keyTimes:`0;1`,children:(0,J.jsx)(`mpath`,{href:`#conn-res-${n}`})})}),(0,J.jsx)(`path`,{className:`conn-chevron`,d:`M-4,-3 L4,0 L-4,3`,stroke:`#e4e4e7`,transform:`translate(${w.x.toFixed(1)},${w.y.toFixed(1)}) rotate(${T.toFixed(1)})`}),(0,J.jsx)(`path`,{className:`conn-chevron`,d:`M-4,-3 L4,0 L-4,3`,stroke:`#d4d4d8`,transform:`translate(${E.x.toFixed(1)},${E.y.toFixed(1)}) rotate(${D.toFixed(1)})`})]}),O>0&&(0,J.jsx)(`g`,{children:(0,J.jsxs)(`circle`,{r:6,fill:`url(#conn-burst)`,children:[(0,J.jsx)(`animateMotion`,{dur:`1.1s`,repeatCount:`1`,calcMode:`linear`,keyPoints:`0;1;0`,keyTimes:`0;0.5;1`,path:C}),(0,J.jsx)(`animate`,{attributeName:`opacity`,values:`0;1;1;0`,keyTimes:`0;0.1;0.85;1`,dur:`1.1s`,repeatCount:`1`,fill:`freeze`})]})},`burst-${e.name}-${O}`)]},`l-${e.name}`)}),m.map(e=>{let t=e.d.jumpVia,n=t?m.find(e=>e.d.name===t):void 0,r=!n&&e.d.jumpHost?e.d.jumpHost:void 0;if(!n&&!r)return null;let i={x:e.x,y:e.y},a=Math.hypot(e.x-d,e.y-f)||1,o=(e.x-d)/a,s=(e.y-f)/a,c=n?{x:n.x,y:n.y}:{x:e.x+o*(e.r+46),y:e.y+s*(e.r+46)},l=(c.x+i.x)/2,u=(c.y+i.y)/2,p=Math.hypot(l-d,u-f)||1,h=n?50:16,g={x:l+(l-d)/p*h,y:u+(u-f)/p*h},_=`M${c.x.toFixed(1)},${c.y.toFixed(1)} Q${g.x.toFixed(1)},${g.y.toFixed(1)} ${i.x.toFixed(1)},${i.y.toFixed(1)}`,v=SY(e.d)??``,y=_Y(c,g,i,.5),b=`conn-tunnel-${e.i}`;return(0,J.jsxs)(`g`,{className:`conn-tunnel-g`,children:[(0,J.jsx)(`path`,{className:`conn-tunnel-halo`,d:_,stroke:xY}),(0,J.jsx)(`path`,{id:b,className:`conn-tunnel`,d:_,stroke:`url(#conn-tunnel-grad)`,filter:`url(#conn-tunnel-glow)`}),r&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`circle`,{className:`conn-tunnel-sat`,cx:c.x,cy:c.y,r:11,fill:`#1c1917`,stroke:`#f59e0b`}),(0,J.jsx)(`text`,{x:c.x,y:c.y+3.5,textAnchor:`middle`,fontSize:11,children:`🛡️`})]}),(0,J.jsxs)(`text`,{className:`conn-tunnel-lock`,fontSize:13,textAnchor:`middle`,children:[`🔒`,(0,J.jsx)(`animateMotion`,{dur:`2.4s`,repeatCount:`indefinite`,calcMode:`linear`,rotate:`auto`,children:(0,J.jsx)(`mpath`,{href:`#${b}`})})]}),(0,J.jsxs)(`g`,{transform:`translate(${y.x.toFixed(1)},${y.y.toFixed(1)})`,children:[(0,J.jsx)(`rect`,{className:`conn-tunnel-badge`,x:-Math.max(24,v.length*3.3+14),y:-9,rx:9,width:Math.max(48,v.length*6.6+28),height:18}),(0,J.jsxs)(`text`,{className:`conn-tunnel-badge-tx`,x:0,y:3.5,textAnchor:`middle`,fontSize:9,children:[`⤳ via `,v]})]})]},`jump-${e.d.name}`)}),(0,J.jsx)(`circle`,{className:`conn-hub-glow`,cx:d,cy:f,r:42}),(0,J.jsx)(`circle`,{className:`conn-hub-ring`,cx:d,cy:f,r:37}),(0,J.jsx)(`circle`,{cx:d,cy:f,r:29,fill:`url(#conn-hub)`,stroke:`#71717a`,strokeWidth:1.5}),(0,J.jsx)(`text`,{x:d,y:f-4,textAnchor:`middle`,fill:`#09090b`,fontSize:12,fontWeight:700,children:`LLM`}),(0,J.jsx)(`text`,{x:d,y:f+8,textAnchor:`middle`,fill:`#3f3f46`,fontSize:8,fontWeight:600,children:`⇄ MCP`}),(0,J.jsx)(`text`,{x:d,y:f+18,textAnchor:`middle`,fill:`#3f3f46`,fontSize:7.5,fontWeight:600,children:`server`}),m.map(({d:e,x:t,y:n,r,label:i})=>{let a=hY(e.status),o=e.status.reachable===!0,s=o?`${e.status.latencyMs??`?`} ms`:a.label,c=gY(e.name);return(0,J.jsxs)(`g`,{className:`conn-node`,children:[o&&(0,J.jsx)(`circle`,{className:`conn-node-halo`,cx:t,cy:n,r:r+1,stroke:c}),(0,J.jsx)(`circle`,{cx:t,cy:n,r,fill:`url(#conn-orb)`}),(0,J.jsx)(`circle`,{cx:t,cy:n,r,fill:c,opacity:.16}),(0,J.jsx)(`circle`,{cx:t,cy:n,r,fill:`none`,stroke:c,strokeWidth:2.5}),(0,J.jsx)(`circle`,{className:o?`conn-blink`:void 0,cx:t+r*.7,cy:n-r*.7,r:4.5,fill:a.color,stroke:`#0a121b`,strokeWidth:1.5}),(0,J.jsx)(`text`,{x:t,y:n+3.5,textAnchor:`middle`,fill:`#fafafa`,fontSize:10,fontWeight:600,children:i}),(0,J.jsx)(`text`,{x:t,y:n+r+14,textAnchor:`middle`,fill:`#a1a1aa`,fontSize:9,children:e.address??e.host}),(0,J.jsx)(`text`,{x:t,y:n+r+26,textAnchor:`middle`,fill:a.color,fontSize:9,fontWeight:600,children:s})]},`n-${e.name}`)})]}),(0,J.jsxs)(`div`,{className:`conn-legend`,children:[(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{style:{background:yY}}),` command · LLM → device`]}),(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{style:{background:bY}}),` response · device → LLM`]}),(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{style:{background:`#e4e4e7`}}),` live call (round-trip)`]}),e.devices.some(e=>SY(e))&&(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{style:{background:`#f59e0b`}}),` 🔒 SSH jump tunnel (ProxyJump)`]})]})]})}function DY({d:e}){let t=hY(e.status),n=e.status.reachable===!0?`${t.label} · ${e.status.latencyMs??`?`}ms${e.status.version?` · v${e.status.version}`:``}`:e.status.reachable===!1?`${t.label}${e.status.error?` · ${e.status.error}`:``}`:t.label,r=gY(e.name);return(0,J.jsxs)(`div`,{className:`card dev-card`,style:{borderLeft:`3px solid ${r}`},children:[(0,J.jsxs)(`div`,{className:`dev-card__top`,children:[(0,J.jsx)(`span`,{className:`dot`,style:{background:r},title:`device colour`}),(0,J.jsx)(`span`,{className:`dev-card__name`,children:e.name}),e.isDefault&&(0,J.jsx)(Mc,{type:`accent`,children:`default`}),(0,J.jsx)(`span`,{className:`dot dot--status`,style:{background:t.color},title:t.label}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`span`,{className:`chip`,children:e.authMode})]}),(0,J.jsxs)(`div`,{className:`dev-card__meta`,children:[(0,J.jsx)(`span`,{children:e.mac?`mac`:`host`}),(0,J.jsx)(`b`,{children:e.address??`${e.host}:${e.port}`}),(0,J.jsx)(`span`,{children:`user`}),(0,J.jsx)(`b`,{children:e.username}),(0,J.jsx)(`span`,{children:`status`}),(0,J.jsx)(`b`,{style:{color:t.color},children:n}),(0,J.jsx)(`span`,{children:`activity`}),(0,J.jsxs)(`b`,{children:[e.activity.calls,` calls · `,e.activity.errors,` err`,e.activity.avgMs?` · ${xc(e.activity.avgMs)} avg`:``]}),e.description&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`span`,{children:`note`}),(0,J.jsx)(`b`,{children:e.description})]})]}),SY(e)&&(0,J.jsxs)(`div`,{className:`jump-route`,title:`Reached over SSH through the bastion ${SY(e)} (ProxyJump) — no port exposed on ${e.name}.`,children:[(0,J.jsx)(`span`,{className:`jump-route__hop jump-route__hop--src`,title:`MCP server`,children:`🛰️`}),(0,J.jsx)(`span`,{className:`jump-route__wire`}),(0,J.jsxs)(`span`,{className:`jump-route__hop jump-route__hop--bastion`,children:[`🛡️ `,SY(e),e.jumpVia?(0,J.jsx)(`i`,{className:`jump-route__tag`,children:`jump`}):null]}),(0,J.jsx)(`span`,{className:`jump-route__wire jump-route__wire--enc`,children:(0,J.jsx)(`span`,{className:`jump-route__lock`,"aria-hidden":!0,children:`🔒`})}),(0,J.jsxs)(`span`,{className:`jump-route__hop jump-route__hop--dest`,style:{borderColor:r},title:e.name,children:[`📡 `,e.name]})]})]})}function OY({event:e,onClose:t}){let[n,r]=(0,v.useState)(sY);return(0,J.jsx)(`div`,{className:`overlay`,onClick:e=>e.target===e.currentTarget&&t(),children:(0,J.jsxs)(`div`,{className:`sheet`,children:[(0,J.jsxs)(`div`,{className:`sheet__hd`,children:[(0,J.jsx)(`span`,{className:`risk risk-${e.risk}`,children:e.risk}),(0,J.jsxs)(`h3`,{className:`sheet__tool`,children:[e.tool,(0,J.jsx)(bc,{text:e.tool,className:`iconbtn`,icon:!0,title:`Copy tool name`})]}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(Ac,{type:`secondary`,size:`sm`,onClick:t,children:`✕ Close`})]}),(0,J.jsxs)(`div`,{className:`kv__body`,children:[(0,J.jsx)(`div`,{className:`kv__k`,children:`title`}),(0,J.jsx)(`div`,{className:`kv__v`,children:e.title}),(0,J.jsx)(`div`,{className:`kv__k`,children:`time`}),(0,J.jsx)(`div`,{className:`kv__v`,children:new Date(e.ts).toLocaleString(void 0,{hour12:!1})}),(0,J.jsx)(`div`,{className:`kv__k`,children:`device`}),(0,J.jsx)(`div`,{className:`kv__v`,children:e.device??`—`}),(0,J.jsx)(`div`,{className:`kv__k`,children:`transport`}),(0,J.jsx)(`div`,{className:`kv__v`,children:e.transport??`—`}),(0,J.jsx)(`div`,{className:`kv__k`,children:`duration`}),(0,J.jsx)(`div`,{className:`kv__v`,children:xc(e.durationMs)}),(0,J.jsx)(`div`,{className:`kv__k`,children:`status`}),(0,J.jsx)(`div`,{className:`kv__v`,children:(0,J.jsx)(`span`,{className:e.isError?`status-err`:`status-ok`,children:e.isError?`error`:`ok`})}),(0,J.jsx)(`div`,{className:`kv__k`,children:`output size`}),(0,J.jsxs)(`div`,{className:`kv__v`,children:[Sc(e.outputBytes),e.truncated?` (truncated)`:``]}),(0,J.jsx)(`div`,{className:`kv__k`,children:`structured`}),(0,J.jsx)(`div`,{className:`kv__v`,children:e.hasStructured?`yes (renders an MCP App view)`:`no`})]}),e.error&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`h2`,{className:`muted`,children:`ERROR`}),(0,J.jsx)(`pre`,{className:`body`,style:{color:`var(--mt-bad)`},children:e.error})]}),(0,J.jsxs)(`div`,{className:`sheet__hd`,children:[(0,J.jsx)(`h2`,{className:`muted`,style:{margin:0},children:`INPUT`}),(0,J.jsx)(`span`,{style:{flex:1}}),e.input&&(0,J.jsx)(Ac,{type:`secondary`,size:`sm`,ghost:!0,onClick:()=>r(e=>{let t=!e;return cY(t),t}),title:n?`Showing pretty-printed JSON — click for raw`:`Showing raw JSON — click to pretty-print`,children:n?`✦ Pretty`:`{ } Raw`}),(0,J.jsx)(bc,{text:e.input,title:`Copy input JSON`})]}),e.input?(0,J.jsx)(uY,{value:lY(e.input,n)}):(0,J.jsx)(`pre`,{className:`body`,children:`—`}),(0,J.jsxs)(`div`,{className:`sheet__hd`,children:[(0,J.jsx)(`h2`,{className:`muted`,style:{margin:0},children:`OUTPUT`}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(bc,{text:e.output,title:`Copy output`})]}),(0,J.jsx)(`pre`,{className:`body ros`,children:e.output?aY(e.output):`—`})]})})}var kY=e=>e==null?`?`:Sc(e);function AY({d:e}){let t=e.status,n=e.history??[];return t.reachable===!0||n.length>0?(0,J.jsxs)(`div`,{className:`card health-card`,children:[(0,J.jsxs)(`div`,{className:`health-card__hd`,children:[(0,J.jsx)(Nc,{color:hY(t).color}),(0,J.jsx)(`span`,{className:`dev-card__name`,children:e.name}),e.isDefault&&(0,J.jsx)(Mc,{type:`accent`,children:`default`}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(Mc,{type:t.version?`success`:`default`,children:t.version?`v${t.version}`:`—`})]}),(0,J.jsxs)(`div`,{className:`health-card__sub muted`,children:[t.boardName??`router`,t.architecture?` · ${t.architecture}`:``,t.cpuCount?` · ${t.cpuCount} cpu`:``,t.uptime?` · up ${t.uptime}`:``]}),(0,J.jsxs)(`div`,{className:`health-card__gauges`,children:[(0,J.jsx)(HJ,{value:t.cpuLoad,label:`CPU`,color:Ec.cpu}),(0,J.jsx)(HJ,{value:t.memUsedPct,label:`MEM`,color:Ec.mem}),(0,J.jsx)(HJ,{value:t.hddUsedPct,label:`DISK`,color:Ec.disk})]}),(0,J.jsxs)(`div`,{className:`health-card__charts`,children:[(0,J.jsxs)(`div`,{className:`health-chart`,children:[(0,J.jsx)(`span`,{className:`health-chart__k`,children:`CPU load`}),(0,J.jsx)(VJ,{id:`${e.name}-cpu`,values:n.map(e=>e.cpuLoad),color:Ec.cpu,maxValue:100,unit:`%`})]}),(0,J.jsxs)(`div`,{className:`health-chart`,children:[(0,J.jsx)(`span`,{className:`health-chart__k`,children:`Memory used`}),(0,J.jsx)(VJ,{id:`${e.name}-mem`,values:n.map(e=>e.memUsedPct),color:Ec.mem,maxValue:100,unit:`%`})]}),(0,J.jsxs)(`div`,{className:`health-chart`,children:[(0,J.jsx)(`span`,{className:`health-chart__k`,children:`Probe latency`}),(0,J.jsx)(VJ,{id:`${e.name}-lat`,values:n.map(e=>e.latencyMs),color:Ec.latency,unit:`ms`})]})]}),(0,J.jsxs)(`div`,{className:`health-card__foot muted`,children:[`RAM `,kY(t.totalMemory&&t.freeMemory?t.totalMemory-t.freeMemory:void 0),` /`,` `,kY(t.totalMemory),` · free disk `,kY(t.freeHdd)]})]}):(0,J.jsxs)(`div`,{className:`card health-card health-card--na`,children:[(0,J.jsxs)(`div`,{className:`health-card__hd`,children:[(0,J.jsx)(Nc,{color:hY(t).color}),(0,J.jsx)(`span`,{className:`dev-card__name`,children:e.name}),e.isDefault&&(0,J.jsx)(Mc,{type:`accent`,children:`default`})]}),(0,J.jsx)(`p`,{className:`muted`,style:{margin:0},children:t.reachable===!1?`Offline — ${t.error??`unreachable`}`:e.mac?`Waiting for the first MAC-Telnet probe (these run every few minutes to avoid contending with tool calls)…`:`Waiting for the first health probe…`})]})}function jY(e,t){let n=(0,v.useRef)(e),r=(0,v.useRef)(t);n.current=e,r.current=t,(0,v.useEffect)(()=>{let e=!1,t=null,i=null,a=()=>{e||(i=new EventSource(dc(`/api/sse`)),i.addEventListener(`hello`,()=>r.current(`sse`)),i.addEventListener(`tool`,e=>{try{n.current(JSON.parse(e.data))}catch{}}),i.onerror=()=>{i&&i.readyState===EventSource.CONNECTING&&r.current(`off`)})},o=()=>{if(e)return;let i=location.protocol===`https:`?`wss`:`ws`;t=new WebSocket(dc(`${i}://${location.host}/api/stream`));let s=!1;t.onopen=()=>{s=!0,r.current(`ws`)},t.onerror=()=>t?.close(),t.onmessage=e=>{try{let t=JSON.parse(e.data);t.type===`event`&&t.event&&n.current(t.event)}catch{}},t.onclose=()=>{e||(r.current(`off`),s?setTimeout(o,2e3):a())}};return o(),()=>{e=!0,t?.close(),i?.close()}},[])}function MY(e){(0,v.useLayoutEffect)(()=>{let t=e.current;if(!t||window.matchMedia(`(prefers-reduced-motion: reduce)`).matches)return;document.documentElement.classList.add(`js-motion`);let n=new WeakSet,r=e=>{n.has(e)||(n.add(e),Ri.set(e,{opacity:0,y:26}),q.create({trigger:e,start:`top 90%`,once:!0,onEnter:()=>Ri.to(e,{opacity:1,y:0,duration:.7,ease:`power3.out`})}))},i=e=>{for(let t of e.querySelectorAll(`.reveal`))r(t)};i(t);let a=new MutationObserver(e=>{for(let t of e)for(let e of t.addedNodes)e instanceof Element&&(e.matches(`.reveal`)&&r(e),i(e))});a.observe(t,{childList:!0,subtree:!0});let o=setInterval(()=>q.refresh(),1200),s=setTimeout(()=>clearInterval(o),7e3);return()=>{a.disconnect(),clearInterval(o),clearTimeout(s);for(let e of q.getAll())e.kill();document.documentElement.classList.remove(`js-motion`)}},[e])}function NY({m:e,busy:t,onToggle:n}){return(0,J.jsxs)(`label`,{className:`mod-row`,"data-on":e.enabled?`1`:void 0,title:e.description,children:[(0,J.jsx)(`input`,{type:`checkbox`,checked:e.enabled,disabled:t,onChange:()=>n(e.slug,!e.enabled)}),(0,J.jsxs)(`span`,{className:`mod-row__main`,children:[(0,J.jsxs)(`span`,{className:`mod-row__name`,children:[e.label,(0,J.jsx)(`code`,{className:`mod-row__slug`,children:e.slug})]}),(0,J.jsx)(`span`,{className:`mod-row__desc muted`,children:e.description})]}),(0,J.jsxs)(`span`,{className:`mod-row__count muted`,children:[e.toolCount,` tool`,e.toolCount===1?``:`s`]})]})}function PY(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(new Set),[i,a]=(0,v.useState)(null),[o,s]=(0,v.useState)(``),c=(0,v.useCallback)(()=>{fc(`/api/modules`).then(t).catch(()=>a(`could not load modules`))},[]);(0,v.useEffect)(()=>c(),[c]);let l=e=>t(t=>t?{...t,...e}:e),u=(0,v.useCallback)(async(e,n)=>{r(t=>new Set(t).add(e)),t(t=>t&&{...t,modules:t.modules.map(t=>t.slug===e?{...t,enabled:n}:t)});let i=await pc(`/api/modules/toggle`,{slug:e,enabled:n}).catch(()=>({error:`request failed`}));if(r(t=>{let n=new Set(t);return n.delete(e),n}),i.error||i.ok===!1){a(i.error??`toggle failed`),c();return}l(i);let o=i.persisted?`saved to config`:`applied live (not saved)`,s=i.warning?` ⚠ ${i.warning}`:``;a(`${e} ${n?`enabled`:`disabled`} — ${o}. Reconnect the MCP client (or restart the server) for the tool list to update.${s}`)},[c]),d=(0,v.useCallback)(async(e,t)=>{for(let n of e)await u(n,t)},[u]),f=(0,v.useMemo)(()=>{if(!e)return[];let t=o.trim().toLowerCase(),n=e=>!t||e.slug.toLowerCase().includes(t)||e.label.toLowerCase().includes(t)||e.group.toLowerCase().includes(t)||e.description.toLowerCase().includes(t),r=new Map;for(let t of e.modules){if(!n(t))continue;let e=r.get(t.group)??[];e.push(t),r.set(t.group,e)}return[...r.entries()].map(([e,t])=>({group:e,modules:t}))},[e,o]);if(!e)return(0,J.jsx)(`div`,{className:`muted`,children:`loading modules…`});let p=f.reduce((e,t)=>e+t.modules.length,0);return(0,J.jsx)(`section`,{className:`view`,children:(0,J.jsxs)(gc,{title:`Tool modules`,className:`reveal`,extra:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`span`,{className:`muted`,children:[e.enabledModules,`/`,e.total,` modules · `,e.enabledTools,`/`,e.totalTools,` `,`tools exposed`]}),(0,J.jsx)(`button`,{className:`btn`,onClick:c,style:{marginLeft:10},children:`↻ Refresh`})]}),children:[(0,J.jsxs)(`div`,{className:`legend`,style:{margin:`0 0 10px`},children:[(0,J.jsxs)(`span`,{children:[`writes to: `,(0,J.jsx)(`code`,{children:e.source?.path??`config file`})]}),(0,J.jsx)(`span`,{children:e.hasAllowList?`allow-list active`:`all modules on by default`})]}),(0,J.jsxs)(`p`,{className:`muted`,style:{margin:`0 0 12px`,fontSize:12},children:[`Toggle a module to expose or hide all of its tools. Disabling adds it to`,` `,(0,J.jsx)(`code`,{children:`tools.disabledModules`}),` in your config file; enabling removes it (or adds it to`,` `,(0,J.jsx)(`code`,{children:`tools.enabledModules`}),` when an allow-list is in force). Trimming the surface below ~150–200 tools makes every remaining tool reliably findable by the MCP client. The client must reconnect for changes to take effect.`]}),(0,J.jsxs)(`div`,{className:`mod-toolbar`,children:[(0,J.jsx)(`input`,{className:`input`,placeholder:`Filter modules by name, slug, group or description…`,value:o,onChange:e=>s(e.target.value)}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>void d(e.modules.filter(e=>!e.enabled).map(e=>e.slug),!0),children:`Enable all`}),` `,(0,J.jsx)(`button`,{className:`btn`,onClick:()=>void d(e.modules.filter(e=>e.enabled).map(e=>e.slug),!1),children:`Disable all`})]}),i&&(0,J.jsx)(`div`,{className:`cfg-msg`,style:{marginTop:12},children:i}),p===0?(0,J.jsxs)(`div`,{className:`muted`,style:{padding:12},children:[`No modules match “`,o,`”.`]}):(0,J.jsx)(`div`,{className:`mod-groups`,children:f.map(({group:e,modules:t})=>{let r=t.filter(e=>e.enabled).length,i=t.map(e=>e.slug);return(0,J.jsxs)(`div`,{className:`mod-group`,children:[(0,J.jsxs)(`div`,{className:`mod-group__hd`,children:[(0,J.jsx)(`h3`,{className:`mod-group__title`,children:e}),(0,J.jsxs)(`span`,{className:`muted`,children:[r,`/`,t.length,` on`]}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`button`,{className:`btn btn-sm`,onClick:()=>void d(i.filter(e=>{let n=t.find(t=>t.slug===e);return n?!n.enabled:!1}),!0),children:`Enable group`}),` `,(0,J.jsx)(`button`,{className:`btn btn-sm`,onClick:()=>void d(i.filter(e=>{let n=t.find(t=>t.slug===e);return n?n.enabled:!1}),!1),children:`Disable group`})]}),(0,J.jsx)(`div`,{className:`mod-list`,children:t.map(e=>(0,J.jsx)(NY,{m:e,busy:n.has(e.slug),onToggle:(e,t)=>void u(e,t)},e.slug))})]},e)})})]})})}var FY={TCP:`#e4e4e7`,UDP:`#d4d4d8`,ICMP:`#a1a1aa`,ICMPv6:`#a1a1aa`,ARP:`#e4e4e7`,IPv6:`#a1a1aa`},IY=e=>e&&FY[e]||`#71717a`;function LY(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(!1);(0,v.useEffect)(()=>{let e=()=>void fc(`/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 pc(`/api/capture/stop`,{}).catch(()=>{}),r(!1)};if(!i||!i.running&&i.packets===0)return(0,J.jsxs)(`div`,{className:`cap-idle muted`,children:[`No capture running. Start one with the `,(0,J.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,J.jsxs)(`div`,{className:`cap`,children:[(0,J.jsxs)(`div`,{className:`cap-bar`,children:[(0,J.jsx)(`span`,{className:`cap-dot${i.running?` is-on`:``}`}),(0,J.jsx)(`b`,{children:i.running?`capturing`:`stopped`}),(0,J.jsxs)(`span`,{className:`muted`,children:[`UDP `,i.port]}),(0,J.jsxs)(`span`,{className:`muted`,children:[wc(i.packets),` pkts · `,Sc(i.bytes)]}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`a`,{className:`btn`,href:dc(`/api/capture/pcap`),download:`capture.pcap`,children:`⤓ pcap`}),(0,J.jsx)(`button`,{className:`btn btn-danger`,onClick:()=>void o(),disabled:n||!i.running,children:`■ Stop`})]}),(0,J.jsxs)(`div`,{className:`cap-cols`,children:[(0,J.jsxs)(`div`,{className:`cap-side`,children:[(0,J.jsx)(`div`,{className:`cap-h`,children:`Protocols`}),Object.entries(i.protocols).map(([e,t])=>(0,J.jsxs)(`div`,{className:`cap-pbar`,children:[(0,J.jsx)(`span`,{className:`cap-plabel`,style:{color:IY(e)},children:e}),(0,J.jsx)(`span`,{className:`cap-ptrack`,children:(0,J.jsx)(`i`,{style:{width:`${t/s*100}%`,background:IY(e)}})}),(0,J.jsx)(`span`,{className:`cap-pn`,children:t})]},e)),(0,J.jsx)(`div`,{className:`cap-h`,style:{marginTop:12},children:`Top talkers`}),i.topTalkers.length===0&&(0,J.jsx)(`div`,{className:`muted`,children:`—`}),i.topTalkers.map(e=>(0,J.jsxs)(`div`,{className:`cap-talker`,children:[(0,J.jsx)(`span`,{children:e.addr}),(0,J.jsx)(`b`,{children:e.count})]},e.addr))]}),(0,J.jsx)(`div`,{className:`cap-list`,children:a.length===0?(0,J.jsx)(`div`,{className:`muted`,style:{padding:10},children:`waiting for packets…`}):a.map((e,t)=>(0,J.jsxs)(`div`,{className:`cap-row`,children:[(0,J.jsx)(`span`,{className:`cap-tt`,children:Cc(e.ts)}),(0,J.jsx)(`span`,{className:`cap-proto`,style:{color:IY(e.protocol)},children:e.protocol??e.ethType}),(0,J.jsx)(`span`,{className:`cap-len`,children:e.len}),(0,J.jsx)(`span`,{className:`cap-info`,children:e.info})]},t))})]})]})}function RY(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(null),[i,a]=(0,v.useState)(null),[o,s]=(0,v.useState)(null),c=(0,v.useCallback)(()=>{fc(`/api/s3/list`).then(t).catch(()=>t({configured:!1,objects:[]}))},[]);(0,v.useEffect)(()=>c(),[c]);let l=e=>{fc(`/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`))},u=async e=>{a(e);let t=await pc(`/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,J.jsx)(`section`,{className:`view`,children:(0,J.jsxs)(gc,{title:`S3 backups`,className:`reveal`,extra:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`span`,{className:`muted`,children:[e.target,` · `,e.objects.length,` object`,e.objects.length===1?``:`s`,e.truncated?` (truncated)`:``]}),(0,J.jsx)(`button`,{className:`btn`,onClick:c,style:{marginLeft:10},children:`↻ Refresh`})]}),children:[o&&(0,J.jsx)(`div`,{className:`cfg-msg`,children:o}),e.objects.length===0?(0,J.jsxs)(`div`,{className:`muted`,style:{padding:12},children:[`No objects in the bucket. Upload one with the `,(0,J.jsx)(`code`,{children:`upload_backup_to_s3`}),` tool.`]}):(0,J.jsx)(`div`,{className:`feedwrap`,children:(0,J.jsxs)(`table`,{className:`feed`,children:[(0,J.jsx)(`thead`,{children:(0,J.jsxs)(`tr`,{children:[(0,J.jsx)(`th`,{children:`key`}),(0,J.jsx)(`th`,{className:`num`,children:`size`}),(0,J.jsx)(`th`,{children:`modified`}),(0,J.jsx)(`th`,{style:{width:200},children:`actions`})]})}),(0,J.jsx)(`tbody`,{children:e.objects.map(e=>(0,J.jsxs)(`tr`,{children:[(0,J.jsx)(`td`,{children:e.key}),(0,J.jsx)(`td`,{className:`num`,children:Sc(e.size)}),(0,J.jsx)(`td`,{children:e.lastModified?new Date(e.lastModified).toLocaleString(void 0,{hour12:!1}):`—`}),(0,J.jsxs)(`td`,{onClick:e=>e.stopPropagation(),children:[(0,J.jsx)(`button`,{className:`btn`,onClick:()=>l(e.key),children:`⤓ Download`}),` `,n===e.key?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`button`,{className:`btn btn-danger`,disabled:i===e.key,onClick:()=>void u(e.key),children:`✓ Confirm`}),` `,(0,J.jsx)(`button`,{className:`btn`,onClick:()=>r(null),children:`Cancel`})]}):(0,J.jsx)(`button`,{className:`btn`,onClick:()=>r(e.key),children:`🗑 Delete`})]})]},e.key))})]})})]})}):(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`☁️`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`S3 is not configured`}),(0,J.jsxs)(`p`,{className:`feed-empty__sub`,children:[`Add an `,(0,J.jsx)(`code`,{children:`s3`}),` block (bucket + credentials) to your config to manage backup objects here.`]})]}):(0,J.jsx)(`div`,{className:`muted`,children:`loading S3 objects…`})}function zY(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(``),[i,a]=(0,v.useState)(``),[o,s]=(0,v.useState)(null),[c,l]=(0,v.useState)(null),u=(0,v.useCallback)(()=>{fc(`/api/snapshots`).then(e=>t(e.snapshots)).catch(()=>t([]))},[]);(0,v.useEffect)(()=>u(),[u]);let d=e=>{fc(`/api/snapshot/${encodeURIComponent(e)}`).then(s).catch(()=>{})},f=()=>{!n||!i||pc(`/api/snapshots/diff`,{from:n,to:i}).then(l).catch(()=>{})};if(!e)return(0,J.jsx)(`div`,{className:`muted`,children:`loading snapshots…`});if(e.length===0)return(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`🕰️`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`No config snapshots yet`}),(0,J.jsxs)(`p`,{className:`feed-empty__sub`,children:[`Capture one with the `,(0,J.jsx)(`code`,{children:`capture_config_snapshot`}),` tool — then time-travel diff any two here.`]})]});let p=e.map(e=>(0,J.jsxs)(`option`,{value:e.id,children:[e.device,` · `,e.label??e.id,` · `,Cc(e.ts)]},e.id));return(0,J.jsxs)(`section`,{className:`view`,children:[(0,J.jsxs)(gc,{title:`Config snapshots`,className:`reveal`,extra:(0,J.jsx)(`button`,{className:`btn`,onClick:u,children:`↻ Refresh`}),children:[(0,J.jsxs)(`div`,{className:`toolbar`,style:{marginBottom:12},children:[(0,J.jsxs)(`select`,{className:`btn`,value:n,onChange:e=>r(e.target.value),children:[(0,J.jsx)(`option`,{value:``,children:`diff from…`}),p]}),(0,J.jsxs)(`select`,{className:`btn`,value:i,onChange:e=>a(e.target.value),children:[(0,J.jsx)(`option`,{value:``,children:`to…`}),p]}),(0,J.jsx)(`button`,{className:`btn is-active`,onClick:f,disabled:!n||!i,children:`Diff →`}),(0,J.jsxs)(`span`,{className:`muted`,children:[e.length,` snapshots`]})]}),(0,J.jsx)(`div`,{className:`feedwrap`,children:(0,J.jsxs)(`table`,{className:`feed`,children:[(0,J.jsx)(`thead`,{children:(0,J.jsxs)(`tr`,{children:[(0,J.jsx)(`th`,{children:`captured`}),(0,J.jsx)(`th`,{children:`device`}),(0,J.jsx)(`th`,{children:`label`}),(0,J.jsx)(`th`,{children:`version`}),(0,J.jsx)(`th`,{className:`num`,children:`lines`}),(0,J.jsx)(`th`,{className:`num`,children:`size`}),(0,J.jsx)(`th`,{children:`output`})]})}),(0,J.jsx)(`tbody`,{children:e.map(e=>(0,J.jsxs)(`tr`,{className:o?.id===e.id?`is-selected`:void 0,onClick:()=>d(e.id),children:[(0,J.jsx)(`td`,{children:Cc(e.ts)}),(0,J.jsx)(`td`,{children:e.device}),(0,J.jsx)(`td`,{children:e.label??`—`}),(0,J.jsx)(`td`,{children:e.rosVersion??`—`}),(0,J.jsx)(`td`,{className:`num`,children:e.lines}),(0,J.jsx)(`td`,{className:`num`,children:Sc(e.bytes)}),(0,J.jsx)(`td`,{className:`preview`,children:`view export →`})]},e.id))})]})})]}),c&&(0,J.jsx)(gc,{title:`Time-travel diff`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:c.summary.changed?`+${c.summary.added} / -${c.summary.removed}`:`identical`}),children:(0,J.jsx)(`pre`,{className:`cfg-diff`,children:(c.unified||`(identical)`).split(`
|
|
80
|
+
`).map((e,t)=>(0,J.jsx)(`div`,{className:e.startsWith(`+`)?`d-add`:e.startsWith(`-`)?`d-del`:e.startsWith(`@@`)?`d-hunk`:``,children:e||` `},t))})}),o&&(0,J.jsx)(gc,{title:`Snapshot · ${o.label??o.id}`,className:`reveal`,extra:(0,J.jsx)(Ac,{type:`secondary`,size:`sm`,onClick:()=>s(null),children:`✕ Close`}),children:(0,J.jsx)(`pre`,{className:`body`,style:{maxHeight:460},children:o.body||`(empty)`})})]})}function BY(e){return e==null?`#3f3f46`:e>=85?`#f87171`:e>=60?`#a1a1aa`:`#d4d4d8`}function VY({topo:e,onOnboard:t}){let[n,r]=(0,v.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),l=Math.max(360,Math.round((c+78)*2)),u=l/2,d=new Map,f=new Map;i.forEach((e,t)=>{if(i.length===1){d.set(e.id,{x:380,y:u}),f.set(e.id,-Math.PI/2);return}let n=t/o*Math.PI*2-Math.PI/2;f.set(e.id,n),d.set(e.id,{x:380+s*Math.cos(n),y:u+s*Math.sin(n)})});let p=new Map,m=new Set(a.map(e=>e.id));for(let t of e.edges)m.has(t.to)&&!p.has(t.to)&&p.set(t.to,t.from);let h=new Map;for(let e of a){let t=p.get(e.id)??i[0]?.id??``;h.set(t,[...h.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;d.set(e.id,{x:380+c*Math.cos(n),y:u+c*Math.sin(n)})});else for(let[e,t]of h){let n=f.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);d.set(e,{x:380+c*Math.cos(a),y:u+c*Math.sin(a)})})}let g=new Map(e.nodes.map(e=>[e.id,e])),_=n?g.get(n):null;return(0,J.jsxs)(`div`,{className:`topo`,children:[(0,J.jsxs)(`svg`,{viewBox:`0 0 760 ${l}`,width:`100%`,height:Math.min(l,540),preserveAspectRatio:`xMidYMid meet`,children:[e.edges.map((e,t)=>{let n=d.get(e.from),r=d.get(e.to);return!n||!r?null:(0,J.jsx)(`line`,{className:`topo-edge${m.has(e.to)?` is-dashed`:``}`,x1:n.x,y1:n.y,x2:r.x,y2:r.y,children:(0,J.jsxs)(`title`,{children:[e.from,` → `,e.to,e.interface?` (${e.interface})`:``]})},`e-${t}`)}),e.nodes.map(e=>{let t=d.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,J.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,J.jsx)(`rect`,{width:a,height:o,rx:9,style:{stroke:l}}),(0,J.jsx)(`text`,{className:`topo-label`,x:9,y:16,children:(e.label||e.id).slice(0,16)}),i?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`text`,{className:`topo-sub`,x:9,y:30,children:(e.board||e.ip||``).slice(0,18)}),(0,J.jsx)(`rect`,{className:`topo-bar-bg`,x:9,y:37,width:a-18,height:4,rx:2}),(0,J.jsx)(`rect`,{x:9,y:37,width:(a-18)*Math.min(100,e.cpuLoad??0)/100,height:4,rx:2,style:{fill:BY(e.cpuLoad)}}),(0,J.jsx)(`rect`,{className:`topo-bar-bg`,x:9,y:43,width:a-18,height:4,rx:2}),(0,J.jsx)(`rect`,{x:9,y:43,width:(a-18)*Math.min(100,e.memUsedPct??0)/100,height:4,rx:2,style:{fill:BY(e.memUsedPct)}})]}):(0,J.jsx)(`text`,{className:`topo-sub`,x:9,y:29,children:e.onboardable?`+ onboard`:e.ip||e.mac||``})]},e.id)})]}),(0,J.jsxs)(`div`,{className:`topo-foot`,children:[(0,J.jsxs)(`span`,{className:`legend`,children:[(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{className:`dot`,style:{background:`#d4d4d8`}}),` online`]}),(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{className:`dot`,style:{background:`#f87171`}}),` offline`]}),(0,J.jsxs)(`span`,{children:[(0,J.jsx)(`i`,{className:`dot`,style:{background:`#e4e4e7`}}),` neighbour`]}),(0,J.jsxs)(`span`,{className:`muted`,children:[e.stats.devices,` devices · `,e.stats.neighbors,` discovered ·`,` `,e.stats.onboardable,` onboardable`]})]}),_&&(0,J.jsxs)(`div`,{className:`topo-pop`,children:[(0,J.jsxs)(`div`,{className:`topo-pop__hd`,children:[(0,J.jsx)(`strong`,{children:_.label}),(0,J.jsx)(`span`,{className:`muted`,children:[_.board,_.version,_.mac].filter(Boolean).join(` · `)||`no details advertised`}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsx)(`button`,{className:`topo-btn`,onClick:()=>r(null),children:`✕`})]}),_.suggestedConfig?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`div`,{className:`muted`,style:{margin:`2px 0 6px`},children:`Not managed yet — add this to your device config to onboard it:`}),(0,J.jsx)(`pre`,{className:`topo-stub`,children:JSON.stringify({[_.suggestedConfig.name]:HY(_.suggestedConfig)},null,2)}),(0,J.jsx)(bc,{className:`topo-btn`,title:`Copy config stub`,label:`Copy config stub`,text:JSON.stringify({[_.suggestedConfig.name]:HY(_.suggestedConfig)},null,2)}),t&&(0,J.jsx)(`button`,{className:`topo-btn cfg-save`,onClick:()=>t(_.suggestedConfig.name,HY(_.suggestedConfig)),children:`Add to config →`})]}):(0,J.jsxs)(`div`,{className:`muted`,children:[`Managed device`,_.ip?` · ${_.ip}`:``,_.uptime?` · up ${_.uptime}`:``]})]})]})]})}function HY(e){let t={port:e.port,username:e.username};return e.host&&(t.host=e.host),e.mac&&(t.mac=e.mac),t}Ri.registerPlugin(q);function UY(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 WY=[{id:`overview`,label:`Overview`,sub:`Calls, latency & risk at a glance`},{id:`devices`,label:`Devices`,sub:`Connectivity radar & system health`},{id:`clients`,label:`Clients`,sub:`Connected LAN devices — usage, block/allow, pin IP`},{id:`aaa`,label:`RADIUS & UM`,sub:`RADIUS client & User Manager RADIUS server`},{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:`backups`,label:`Backups`,sub:`Local config vault — create, restore, manage`},{id:`modules`,label:`Modules`,sub:`Enable/disable tool modules — curate the surface`},{id:`config`,label:`Config`,sub:`Effective configuration & safe editor`},{id:`feed`,label:`Live Feed`,sub:`Every tool call, in real time`}],GY=new Set(WY.map(e=>e.id)),KY=`mt-view`;function qY(){try{let e=location.hash.replace(/^#\/?/,``);return GY.has(e)?e:null}catch{return null}}function JY(){let e=qY();if(e)return e;try{let e=localStorage.getItem(KY);if(e&&GY.has(e))return e}catch{}return`overview`}var YY=[`#ededed`,`#a1a1a1`],XY={overview:YY,devices:YY,clients:YY,aaa:YY,topology:YY,packets:YY,snapshots:YY,plan:YY,s3:YY,backups:YY,modules:YY,config:YY,feed:YY},ZY={overview:{what:`A live pulse of all MCP tool activity: total calls, error rate, p50/p95 latency, the busiest tools, and a risk breakdown — over a time window you choose.`,tips:[`Change the time window (top-right) to zoom from the last 5 minutes out to 24 hours.`,`The risk donut splits calls by annotation: read · write · destructive · dangerous.`,`A rising error line usually points at one device or one tool — jump to Live Feed to see which.`]},devices:{what:`Every configured router with its live reachability (SSH or MAC-Telnet), latency, identity, and system health — CPU, memory and disk — refreshed continuously.`,tips:[`Each device gets a stable colour so you can track it across the connectivity radar.`,`Health (CPU/Mem/Disk) is probed periodically; MAC-Telnet devices are probed on a slower cadence.`,`Latency tiers are colour-coded green → amber → red; a grey node is currently unreachable.`]},clients:{what:`The LAN devices connected to a router — merged from its DHCP leases and ARP table — with live Download/Upload charts, and one-click controls to block/allow a device, pin (reserve) its IP, change that IP, or relabel it.`,tips:[`Pick the router (top-right) to inspect its connected devices; filter by IP, MAC or name.`,`Click a device to open its live ↓/↑ traffic chart — needs a simple queue targeting its IP.`,`Block/allow is enforced by MAC, so it survives the device changing IP; “Pin IP” makes its lease static.`]},aaa:{what:"Full management of the router's RADIUS client (`/radius`) and the built-in User Manager RADIUS server (`/user-manager`): RADIUS servers + incoming CoA, and User Manager users, service profiles, rate/quota limitations, NAS clients, profile assignments, accounting sessions, and global settings.",tips:[`Pick the router (top-right), then switch tabs across RADIUS, Users, Profiles, Limitations, NAS, Assignments, Sessions and Settings.`,`Every tab is full CRUD: add, edit, enable/disable and remove — secrets are write-only and shown redacted.`,`The Usage & Heatmap tab shows each user's 3-month download/upload and a GitHub-style connection heatmap, persisted locally.`,`If a device lacks the user-manager package, the User Manager tabs explain how to install it; RADIUS-client tabs still work.`]},topology:{what:`A Layer-2 map of neighbours each router discovers via MNDP / CDP / LLDP — the physical adjacency of your network, drawn live.`,tips:[`Solid nodes are configured devices; faint nodes are discovered-but-unmanaged neighbours.`,`Use “Add to config →” on an unmanaged neighbour to pre-fill it in the Config editor.`,`Drag to pan; the layout settles automatically as new neighbours arrive.`]},packets:{what:`Live packet capture streamed from a router over TZSP — decode headers in real time without leaving the dashboard.`,tips:[`Pick a device and start the capture; packets decode as they arrive.`,`Stop the capture when done — it frees the router-side sniffer.`,`Great for debugging a protocol issue alongside the Live Feed of tool calls.`]},snapshots:{what:`Point-in-time captures of a device’s full configuration (/export), stored locally so you can diff any two and see exactly what changed.`,tips:[`Capture a snapshot before a risky change, then diff after to audit the delta.`,`The diff is line-level: green added, red removed.`,`Snapshots are device config exports — for the dashboard’s OWN config history see the Config page.`]},plan:{what:`Dry-run the exact RouterOS commands a change would run before it touches a device — a change plan you can review and trust.`,tips:[`Paste or build intended commands to see them validated and ordered.`,`Nothing is sent to the device from here — it’s a preview.`,`Pair with Safe Mode (auto-revert) when you do apply for real.`]},s3:{what:`Browse, download, and delete backup objects in your configured S3-compatible bucket — your off-box archive of device backups and exports.`,tips:[`Filter by key prefix to find a device’s backups quickly.`,`Download fetches the object through a presigned URL; delete is permanent.`,`For host-side .rsc backups instead, use the Backups page.`]},backups:{what:`A local config vault on the MCP server: capture a device’s /export as a timestamped .rsc file, then download, upload, rename, restore (via Safe Mode), or delete it.`,tips:[`Restore offers a dry-run (applies then rolls back) before you commit for real.`,`Edit the vault path inline in the header — it’s saved to your config.`,`Filenames are stamped in the device’s local 24-hour clock.`]},modules:{what:"Every tool module in the catalog with a live on/off switch. Toggling one writes your config file's `tools` block (disabledModules / enabledModules) and applies it immediately, so you can curate exactly which scopes the MCP server exposes.",tips:[`MCP clients search-rank tools and degrade past ~100; trim the surface below ~150–200 tools so every remaining tool is reliably findable.`,`Disable adds the module to tools.disabledModules; enable removes it (or adds it to an active allow-list) — your config file is updated live on each toggle.`,`Changes need an MCP client reconnect (or a server restart) to actually shrink/grow the visible tool list.`]},config:{what:`View the effective configuration, edit it safely with schema-aware validation and auto-rollback, browse a full field guide, and travel through config version history.`,tips:[`Every successful apply is auto-saved to the version timeline — restore any point in time.`,`Save a named checkpoint before a big change for an easy, labelled rollback.`,`The Field Guide documents every config option, its type, and default — straight from the schema.`]},feed:{what:`Every tool call as it happens — tool, device, risk, duration, and success/error — with full request/response detail on click.`,tips:[`Filter by status to isolate failures, or by tool/device to follow one thread.`,`Click any row to open the full (secret-redacted) request and response.`,`Pause the stream when you want to inspect without rows shifting under you.`]}};function QY({view:e}){let t=ZY[e];return(0,J.jsxs)(`div`,{className:`pagehelp reveal`,role:`region`,"aria-label":`Page help`,children:[(0,J.jsx)(`div`,{className:`pagehelp__icon`,"aria-hidden":`true`,children:`?`}),(0,J.jsxs)(`div`,{className:`pagehelp__body`,children:[(0,J.jsx)(`p`,{className:`pagehelp__what`,children:t.what}),(0,J.jsx)(`ul`,{className:`pagehelp__tips`,children:t.tips.map((e,t)=>(0,J.jsx)(`li`,{children:e},t))})]})]})}function $Y({name:e}){return(0,J.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.7`,strokeLinecap:`round`,strokeLinejoin:`round`,"aria-hidden":`true`,children:{overview:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`rect`,{x:`3`,y:`3`,width:`8`,height:`8`,rx:`1.6`}),(0,J.jsx)(`rect`,{x:`13`,y:`3`,width:`8`,height:`5`,rx:`1.6`}),(0,J.jsx)(`rect`,{x:`13`,y:`10`,width:`8`,height:`11`,rx:`1.6`}),(0,J.jsx)(`rect`,{x:`3`,y:`13`,width:`8`,height:`8`,rx:`1.6`})]}),devices:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`rect`,{x:`3`,y:`4`,width:`18`,height:`7`,rx:`2`}),(0,J.jsx)(`rect`,{x:`3`,y:`13`,width:`18`,height:`7`,rx:`2`}),(0,J.jsx)(`path`,{d:`M7 7.5h.01M7 16.5h.01`})]}),clients:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`circle`,{cx:`9`,cy:`8`,r:`3`}),(0,J.jsx)(`path`,{d:`M3.5 19a5.5 5.5 0 0 1 11 0`}),(0,J.jsx)(`path`,{d:`M16 7.5a2.5 2.5 0 0 1 0 5M17.5 19a4.5 4.5 0 0 0-2-3.6`})]}),aaa:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`path`,{d:`M12 3 4 6v5c0 4.4 3.2 7.6 8 9 4.8-1.4 8-4.6 8-9V6l-8-3Z`}),(0,J.jsx)(`path`,{d:`M9.5 11.5 11 13l3.5-3.5`})]}),topology:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`circle`,{cx:`12`,cy:`5`,r:`2.4`}),(0,J.jsx)(`circle`,{cx:`5`,cy:`19`,r:`2.4`}),(0,J.jsx)(`circle`,{cx:`19`,cy:`19`,r:`2.4`}),(0,J.jsx)(`path`,{d:`M12 7.4 6.4 16.6M12 7.4 17.6 16.6`})]}),packets:(0,J.jsx)(`path`,{d:`M3 12h4l2-7 4 14 2-7h6`}),snapshots:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`path`,{d:`M12 3 3 7.5 12 12 21 7.5 12 3Z`}),(0,J.jsx)(`path`,{d:`M3 12 12 16.5 21 12`}),(0,J.jsx)(`path`,{d:`M3 16.5 12 21 21 16.5`})]}),plan:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`circle`,{cx:`6`,cy:`6`,r:`2.3`}),(0,J.jsx)(`circle`,{cx:`6`,cy:`18`,r:`2.3`}),(0,J.jsx)(`circle`,{cx:`18`,cy:`8`,r:`2.3`}),(0,J.jsx)(`path`,{d:`M6 8.3v7.4M8.3 6H13a3 3 0 0 1 3 3v0`})]}),s3:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`ellipse`,{cx:`12`,cy:`6`,rx:`7`,ry:`2.6`}),(0,J.jsx)(`path`,{d:`M5 6v12c0 1.5 3.1 2.6 7 2.6s7-1.1 7-2.6V6`}),(0,J.jsx)(`path`,{d:`M5 12c0 1.5 3.1 2.6 7 2.6s7-1.1 7-2.6`})]}),backups:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`path`,{d:`M3 6.5 5 3.5h14l2 3`}),(0,J.jsx)(`rect`,{x:`3`,y:`6.5`,width:`18`,height:`14`,rx:`2`}),(0,J.jsx)(`path`,{d:`M9.5 12h5`})]}),modules:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`rect`,{x:`3`,y:`5`,width:`18`,height:`6`,rx:`3`}),(0,J.jsx)(`circle`,{cx:`8`,cy:`8`,r:`1.5`}),(0,J.jsx)(`rect`,{x:`3`,y:`13`,width:`18`,height:`6`,rx:`3`}),(0,J.jsx)(`circle`,{cx:`16`,cy:`16`,r:`1.5`})]}),config:(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(`path`,{d:`M4 7h8M16 7h4M4 17h4M12 17h8`}),(0,J.jsx)(`circle`,{cx:`14`,cy:`7`,r:`2.2`}),(0,J.jsx)(`circle`,{cx:`10`,cy:`17`,r:`2.2`})]}),feed:(0,J.jsx)(`path`,{d:`M4 6h16M4 12h16M4 18h10`})}[e]})}function eX(){let[e,t]=(0,v.useState)(null),[n,r]=(0,v.useState)(null),[i,a]=(0,v.useState)(null),[o,s]=(0,v.useState)(null),[c,l]=(0,v.useState)(null),[u,d]=(0,v.useState)(!1),[f,p]=(0,v.useState)(null),[m,h]=(0,v.useState)([]),[g,_]=(0,v.useState)(36e5),[y,b]=(0,v.useState)(!1),[x,S]=(0,v.useState)(`off`),[C,w]=(0,v.useState)(null),[T,E]=(0,v.useState)(()=>new Set),[D,O]=(0,v.useState)({}),[k,A]=(0,v.useState)({tool:``,risk:``,device:``,status:``,q:``}),j=(0,v.useRef)(y);j.current=y;let M=(0,v.useRef)(null);MY(M);let[N,P]=(0,v.useState)(JY),F=(0,v.useCallback)(e=>{P(e);try{qY()!==e&&(location.hash=e),localStorage.setItem(KY,e)}catch{}},[]);(0,v.useEffect)(()=>{if(qY()!==N)try{location.hash=N}catch{}let e=()=>{let e=qY();e&&F(e)};return window.addEventListener(`hashchange`,e),()=>window.removeEventListener(`hashchange`,e)},[]);let[I,L]=(0,v.useState)(()=>{try{let e=localStorage.getItem(`mt-help-open`);return new Set(e?JSON.parse(e):[])}catch{return new Set}}),ee=e=>L(t=>{let n=new Set(t);n.has(e)?n.delete(e):n.add(e);try{localStorage.setItem(`mt-help-open`,JSON.stringify([...n]))}catch{}return n}),[te,R]=(0,v.useState)(``),[z,ne]=(0,v.useState)(`all`),B=(0,v.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]),V=(0,v.useMemo)(()=>{let e=i?.devices??[],t=te.trim().toLowerCase();return e.filter(e=>!(z===`online`&&e.status.reachable!==!0||z===`offline`&&e.status.reachable!==!1||t&&!e.name.toLowerCase().includes(t)&&!(e.address??e.host??``).toLowerCase().includes(t)))},[i,te,z]);jY((0,v.useCallback)(e=>{if(j.current)return;h(t=>[e,...t].slice(0,kc));let t=e.device;t&&O(e=>({...e,[t]:(e[t]??0)+1}))},[]),(0,v.useCallback)(e=>S(e),[])),(0,v.useEffect)(()=>{fc(`/api/events?limit=${kc}`).then(e=>h(e.events)).catch(()=>{})},[]);let re=(0,v.useCallback)(()=>{fc(`/api/stats?window=${g}&buckets=60`).then(t).catch(()=>{})},[g]);(0,v.useEffect)(()=>{re();let e=()=>{re(),fc(`/api/meta`).then(r).catch(()=>{}),fc(`/api/devices`).then(a).catch(()=>{}),fc(`/api/topology`).then(s).catch(()=>{})};e();let t=setInterval(()=>{j.current||e()},4e3);return()=>clearInterval(t)},[re]),(0,v.useEffect)(()=>{let e=()=>void fc(`/api/config`).then(l).catch(()=>{});e();let t=setInterval(e,3e4);return()=>clearInterval(t)},[]),(0,v.useEffect)(()=>{let e=e=>{e.key===`Escape`&&w(null)};return window.addEventListener(`keydown`,e),()=>window.removeEventListener(`keydown`,e)},[]);let ie=(0,v.useMemo)(()=>{let e=k.q.trim().toLowerCase();return m.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)},[m,k]),ae=!!(k.tool||k.risk||k.device||k.status||k.q),oe=(0,v.useMemo)(()=>m.filter(e=>e.isError),[m]),se=(0,v.useMemo)(()=>({ok:m.length-oe.length,error:oe.length}),[m,oe]),H=(0,v.useCallback)(async e=>{try{w(await fc(`/api/event/${encodeURIComponent(e.id)}`))}catch{w(e)}},[]),ce=(0,v.useMemo)(()=>ie.slice(0,200),[ie]),le=(0,v.useMemo)(()=>ce.map(e=>e.id),[ce]),ue=le.length>0&&le.every(e=>T.has(e)),de=!ue&&le.some(e=>T.has(e)),fe=(0,v.useCallback)(e=>{E(t=>{let n=new Set(t);return n.has(e)?n.delete(e):n.add(e),n})},[]),pe=(0,v.useCallback)(()=>{E(e=>{let t=new Set(e);if(le.length>0&&le.every(e=>t.has(e)))for(let e of le)t.delete(e);else for(let e of le)t.add(e);return t})},[le]),[me,he]=(0,v.useState)(!1),ge=(0,v.useCallback)(async()=>{let e=[...T];if(e.length!==0)try{await mc({ids:e});let t=new Set(e);h(e=>e.filter(e=>!t.has(e.id))),E(new Set)}catch{}finally{he(!1)}},[T]),_e=e=>{if(e===`json`){UY(`mcp-events.json`,JSON.stringify(ie,null,2),`application/json`);return}let t=[`ts`,`tool`,`risk`,`device`,`durationMs`,`isError`,`error`],n=e=>/[",\n]/.test(e)?`"${e.replace(/"/g,`""`)}"`:e,r=ie.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(`
|
|
81
|
+
`);UY(`mcp-events.csv`,`${t.join(`,`)}\n${r}\n`,`text/csv`)},ve=e?e.errorRate>=.2?`is-bad`:e.errorRate>=.05?`is-warn`:`is-good`:``,ye=c?.mcp??{},be=c?.dashboard??{},xe=(e,t,n)=>(0,J.jsxs)(`select`,{className:`btn`,value:k[e],onChange:t=>A(n=>({...n,[e]:t.target.value})),children:[(0,J.jsx)(`option`,{value:``,children:t}),n.map(e=>(0,J.jsx)(`option`,{value:e,children:e},e))]}),Se=WY.find(e=>e.id===N)??WY[0];return(0,J.jsxs)(`div`,{className:`shell`,ref:M,"data-view":N,style:{"--page-accent":XY[N][0],"--page-accent-2":XY[N][1]},children:[(0,J.jsxs)(`aside`,{className:`nav`,children:[(0,J.jsxs)(`div`,{className:`nav__brand`,children:[(0,J.jsx)(`div`,{className:`nav__mark`,children:(0,J.jsx)(`svg`,{viewBox:`0 0 24 24`,fill:`none`,"aria-hidden":`true`,children:(0,J.jsxs)(`g`,{stroke:`#18181b`,strokeWidth:`2`,strokeLinecap:`round`,strokeLinejoin:`round`,children:[(0,J.jsx)(`path`,{d:`M12 12 L4 5 M12 12 L20 5 M12 12 L12 20`}),(0,J.jsx)(`circle`,{cx:`12`,cy:`12`,r:`3`,fill:`#18181b`,stroke:`none`}),(0,J.jsx)(`circle`,{cx:`4`,cy:`5`,r:`1.9`,fill:`#18181b`,stroke:`none`}),(0,J.jsx)(`circle`,{cx:`20`,cy:`5`,r:`1.9`,fill:`#18181b`,stroke:`none`}),(0,J.jsx)(`circle`,{cx:`12`,cy:`20`,r:`1.9`,fill:`#18181b`,stroke:`none`})]})})}),(0,J.jsxs)(`div`,{className:`nav__brandtext`,children:[(0,J.jsx)(`b`,{children:`MikroTik MCP`}),(0,J.jsx)(`small`,{children:`Observability`})]})]}),(0,J.jsx)(`nav`,{className:`nav__items`,children:WY.map(e=>(0,J.jsxs)(`button`,{className:`nav__item${N===e.id?` is-active`:``}`,onClick:()=>F(e.id),children:[(0,J.jsx)($Y,{name:e.id}),(0,J.jsx)(`span`,{children:e.label}),e.id===`feed`&&m.length>0&&(0,J.jsx)(`span`,{className:`nav__badge`,children:m.length>999?`999+`:m.length},m.length)]},e.id))}),(0,J.jsxs)(`div`,{className:`nav__foot`,children:[(0,J.jsxs)(`span`,{className:`hero__live${x===`off`?``:` is-on`}${x===`ws`?` is-ws`:x===`sse`?` is-sse`:``}`,title:`Live transport: WebSocket (preferred) or SSE fallback`,children:[(0,J.jsx)(`span`,{className:`dot`}),x===`off`?`offline`:`live · ${x}`]}),(0,J.jsx)(`small`,{className:`muted`,children:n?`${wc(n.total)} events · ${n.transport}`:`connecting…`})]})]}),(0,J.jsxs)(`main`,{className:`main`,"data-view":N,children:[(0,J.jsxs)(`header`,{className:`topline reveal`,children:[(0,J.jsxs)(`div`,{className:`topline__txt`,children:[(0,J.jsx)(`h1`,{children:Se.label}),(0,J.jsx)(`small`,{children:Se.sub})]}),(0,J.jsx)(`span`,{className:`topline__spacer`}),N===`overview`&&(0,J.jsx)(`select`,{className:`btn`,value:g,onChange:e=>_(Number(e.target.value)),title:`Stats time window`,children:Oc.map(([e,t])=>(0,J.jsxs)(`option`,{value:t,children:[`window: `,e]},t))}),(0,J.jsxs)(`button`,{className:`help-toggle${I.has(N)?` is-on`:``}`,onClick:()=>ee(N),"aria-expanded":I.has(N),title:`About this page`,children:[(0,J.jsx)(`span`,{className:`help-toggle__q`,"aria-hidden":`true`,children:`?`}),`Help`]})]}),I.has(N)&&(0,J.jsx)(QY,{view:N}),N===`overview`&&(0,J.jsxs)(`section`,{className:`view`,children:[(0,J.jsx)(`div`,{className:`cards reveal`,children:e?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(_c,{k:`Calls (window)`,v:wc(e.total)}),(0,J.jsx)(_c,{k:`Calls / min`,v:e.callsPerMin.toFixed(1)}),(0,J.jsx)(_c,{k:`Error rate`,v:`${(e.errorRate*100).toFixed(1)}%`,sub:`${e.errors} err`,cls:ve}),(0,J.jsx)(_c,{k:`Avg latency`,v:xc(e.latency.avg)}),(0,J.jsx)(_c,{k:`p95 latency`,v:xc(e.latency.p95)}),(0,J.jsx)(_c,{k:`p99 latency`,v:xc(e.latency.p99)}),(0,J.jsx)(_c,{k:`Distinct tools`,v:wc(e.distinctTools)}),(0,J.jsx)(_c,{k:`Output volume`,v:Sc(e.outputBytes)})]}):(0,J.jsxs)(`div`,{className:`stat`,children:[(0,J.jsx)(`p`,{className:`k`,children:`Loading…`}),(0,J.jsx)(`div`,{className:`v`,children:`—`})]})}),(0,J.jsxs)(`div`,{className:`bento reveal`,children:[(0,J.jsx)(gc,{title:`Calls over time`,className:`b-series`,children:e?(0,J.jsx)(zJ,{series:e.series}):(0,J.jsx)(`div`,{className:`muted`,children:`no data`})}),e&&(0,J.jsxs)(J.Fragment,{children:[(0,J.jsx)(gc,{title:`By risk`,className:`b-risk`,children:(0,J.jsx)(BJ,{segments:Object.keys(e.byRisk).map(t=>({label:t,value:e.byRisk[t],color:Dc[t]}))})}),(0,J.jsx)(gc,{title:`Top tools`,className:`b-tools`,children:(0,J.jsx)(vc,{rows:e.byTool.map(e=>({label:e.tool,value:e.count,sub:`${e.count}× · ${xc(e.p95Ms)} p95${e.errors?` · ${e.errors} err`:``}`,color:e.errors?`var(--mt-bad)`:void 0}))})}),(0,J.jsx)(gc,{title:`Status`,className:`b-status`,children:(0,J.jsx)(BJ,{centerLabel:`calls`,segments:[{label:`ok`,value:se.ok,color:`#a1a1a1`},{label:`error`,value:se.error,color:`#ff5c5c`}]})}),(0,J.jsx)(gc,{title:`By device`,className:`b-device`,children:e.byDevice.length?(0,J.jsx)(vc,{rows:e.byDevice.map(e=>({label:e.device,value:e.count}))}):(0,J.jsx)(`div`,{className:`muted`,children:`single device`})}),(0,J.jsx)(gc,{title:`Recent errors`,className:`b-errors`,children:oe.length?(0,J.jsx)(`div`,{className:`hbar`,children:oe.slice(0,8).map(e=>(0,J.jsxs)(`div`,{className:`hbar__row conn-errrow`,style:{gridTemplateColumns:`auto 1fr`},onClick:()=>void H(e),children:[(0,J.jsx)(`span`,{className:`muted`,children:Cc(e.ts)}),(0,J.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,J.jsx)(`div`,{className:`muted`,children:`no errors 🎉`})})]})]})]}),N===`devices`&&(i&&i.devices.length>0?(0,J.jsxs)(`section`,{className:`view`,children:[(0,J.jsxs)(`div`,{className:`dev-toolbar reveal`,children:[(0,J.jsx)(`input`,{className:`search`,type:`search`,placeholder:`Search devices by name or address…`,value:te,onChange:e=>R(e.target.value),style:{flex:1,minWidth:180}}),(0,J.jsx)(`div`,{className:`dev-filters`,children:[`all`,`online`,`offline`].map(e=>(0,J.jsx)(`button`,{className:`dev-fbtn${z===e?` is-active`:``}`,onClick:()=>ne(e),children:e===`all`?`All ${B.total}`:e===`online`?`Online ${B.online}`:`Offline ${B.offline}`},e))}),(0,J.jsxs)(`span`,{className:`muted`,children:[V.length,`/`,B.total,` shown`]})]}),(0,J.jsxs)(`details`,{className:`dev-collapse reveal`,open:B.total<=8,children:[(0,J.jsxs)(`summary`,{children:[`Connectivity radar`,(0,J.jsxs)(`span`,{className:`muted`,children:[` `,`· `,B.online,` online · `,B.offline,` offline ·`,` `,B.total,` total`]})]}),(0,J.jsx)(EY,{payload:i,pulses:D})]}),V.length===0?(0,J.jsxs)(`div`,{className:`feed-empty reveal`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`🔍`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`No devices match`}),(0,J.jsx)(`p`,{className:`feed-empty__sub`,children:`Try a different search or status filter.`})]}):(0,J.jsx)(`div`,{className:`dev-grid-wide reveal`,children:V.map(e=>(0,J.jsx)(DY,{d:e},e.name))}),V.length>0&&(0,J.jsx)(gc,{title:`Device system health`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:`CPU · memory · disk · latency · live probe`}),children:(0,J.jsx)(`div`,{className:`health-grid`,children:V.map(e=>(0,J.jsx)(AY,{d:e},e.name))})})]}):(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`🖧`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`No devices configured`}),(0,J.jsx)(jc,{type:`secondary`,label:`Tip`,children:`Add a device to your config to see connectivity and system health here.`})]})),N===`clients`&&(0,J.jsx)(XJ,{}),N===`aaa`&&(0,J.jsx)(pl,{}),N===`topology`&&(o&&o.nodes.length>0?(0,J.jsx)(`section`,{className:`view`,children:(0,J.jsx)(gc,{title:`Network topology`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:`Layer-2 neighbours via MNDP/CDP/LLDP · click a neighbour to onboard it`}),children:(0,J.jsx)(VY,{topo:o,onOnboard:(e,t)=>{p({name:e,body:t}),d(!0),F(`config`)}})})}):(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`🛰️`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`No neighbours discovered yet`}),(0,J.jsx)(`p`,{className:`feed-empty__sub`,children:`Layer-2 neighbours (MNDP / CDP / LLDP) appear here as the device reports them.`})]})),N===`packets`&&(0,J.jsx)(`section`,{className:`view`,children:(0,J.jsx)(gc,{title:`Packet capture`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:`live TZSP decode · /tool sniffer streaming`}),children:(0,J.jsx)(LY,{})})}),N===`snapshots`&&(0,J.jsx)(zY,{}),N===`plan`&&(0,J.jsx)(hl,{}),N===`s3`&&(0,J.jsx)(RY,{}),N===`backups`&&(0,J.jsx)(Lc,{}),N===`modules`&&(0,J.jsx)(PY,{}),N===`config`&&(c?(0,J.jsxs)(`section`,{className:`view`,children:[(0,J.jsx)(gc,{title:`Configuration`,className:`reveal`,extra:(0,J.jsx)(`button`,{className:`btn`,onClick:()=>d(e=>!e),title:`Edit the config JSON with autocomplete, validation and safe-apply`,children:u?`View`:`✎ Edit config`}),children:u?(0,J.jsx)(mY,{initial:f?{...c,devices:{...c.devices,[f.name]:f.body}}:c,onClose:()=>{d(!1),p(null)},onReload:()=>{p(null),fc(`/api/config`).then(l).catch(()=>{})}},f?`seed-${f.name}`:`config`):(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`div`,{className:`legend`,style:{margin:`0 0 10px`},children:[(0,J.jsxs)(`span`,{children:[`transport: `,Tc(ye.transport)]}),(0,J.jsxs)(`span`,{children:[`read-only: `,c.readOnly?`yes`:`no`]}),(0,J.jsxs)(`span`,{children:[`dashboard: `,Tc(be.host),`:`,Tc(be.port)]}),(0,J.jsxs)(`span`,{children:[`capture: `,be.captureBody?`on`:`off`]}),(0,J.jsxs)(`span`,{children:[`s3: `,c.s3?`configured`:`off`]})]}),(0,J.jsxs)(`details`,{className:`cfg`,children:[(0,J.jsx)(`summary`,{children:`Full effective configuration (secrets redacted)`}),(0,J.jsx)(uY,{value:c,maxHeight:340})]})]})}),(0,J.jsx)(gc,{title:`Version history`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:`point-in-time snapshots · diff & restore`}),children:(0,J.jsx)($J,{onRestored:()=>void fc(`/api/config`).then(l).catch(()=>{})})}),(0,J.jsx)(gc,{title:`Field guide`,className:`reveal`,extra:(0,J.jsx)(`span`,{className:`muted`,children:`every config option, documented from the schema`}),children:(0,J.jsx)(tY,{})})]}):(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(Pc,{}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`Loading configuration…`})]})),N===`feed`&&(0,J.jsxs)(`div`,{className:`panel reveal`,children:[(0,J.jsxs)(`div`,{className:`sheet__hd`,style:{marginBottom:12},children:[(0,J.jsx)(`h2`,{style:{margin:0},children:`Live tool calls`}),(0,J.jsx)(`span`,{style:{flex:1}}),(0,J.jsxs)(`span`,{className:`muted`,children:[ie.length,` shown · `,m.length,` buffered`]})]}),(0,J.jsxs)(`div`,{className:`toolbar`,style:{marginBottom:12},children:[(0,J.jsx)(`div`,{className:`grow`,style:{flex:1,minWidth:180},children:(0,J.jsx)(`input`,{className:`search`,type:`search`,placeholder:`Search tool / input / output / error…`,value:k.q,onChange:e=>A(t=>({...t,q:e.target.value}))})}),xe(`tool`,`all tools`,n?.tools??[]),xe(`risk`,`all risk`,[`READ`,`WRITE`,`WRITE_IDEMPOTENT`,`DESTRUCTIVE`,`DANGEROUS`]),xe(`device`,`all devices`,n?.devices??[]),xe(`status`,`all status`,[`ok`,`error`]),(0,J.jsx)(`button`,{className:`btn${y?` is-active`:``}`,onClick:()=>b(e=>!e),children:y?`▶ Resume`:`⏸ Pause`}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>_e(`csv`),children:`CSV`}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>_e(`json`),children:`JSON`}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>A({tool:``,risk:``,device:``,status:``,q:``}),children:`Clear`}),me&&T.size>0?(0,J.jsxs)(J.Fragment,{children:[(0,J.jsxs)(`button`,{className:`btn btn-danger`,onClick:()=>void ge(),children:[`✓ Confirm delete (`,T.size,`)`]}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>he(!1),children:`Cancel`})]}):(0,J.jsxs)(`button`,{className:`btn`,disabled:T.size===0,onClick:()=>he(!0),title:`Delete the selected rows`,children:[`🗑 Delete`,T.size>0?` (${T.size})`:``]})]}),ie.length===0?ae?(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__icon`,children:`🔍`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:`No calls match your filters`}),(0,J.jsxs)(`p`,{className:`feed-empty__sub`,children:[m.length,` call`,m.length===1?``:`s`,` buffered — try widening the search or the risk / device / status filters.`]}),(0,J.jsx)(`button`,{className:`btn`,onClick:()=>A({tool:``,risk:``,device:``,status:``,q:``}),children:`Clear filters`})]}):(0,J.jsxs)(`div`,{className:`feed-empty`,children:[(0,J.jsx)(`div`,{className:`feed-empty__pulse${x===`off`?``:` is-on`}`}),(0,J.jsx)(`p`,{className:`feed-empty__title`,children:x===`off`?`Not connected`:`Listening for tool calls…`}),(0,J.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,J.jsx)(`div`,{className:`feedwrap`,children:(0,J.jsxs)(`table`,{className:`feed`,children:[(0,J.jsx)(`thead`,{children:(0,J.jsxs)(`tr`,{children:[(0,J.jsx)(`th`,{style:{width:28},children:(0,J.jsx)(`input`,{type:`checkbox`,"aria-label":`Select all shown rows`,checked:ue,ref:e=>{e&&(e.indeterminate=de)},onChange:pe})}),(0,J.jsx)(`th`,{children:`time`}),(0,J.jsx)(`th`,{children:`tool`}),(0,J.jsx)(`th`,{children:`risk`}),(0,J.jsx)(`th`,{children:`device`}),(0,J.jsx)(`th`,{className:`num`,children:`dur`}),(0,J.jsx)(`th`,{children:`status`}),(0,J.jsx)(`th`,{children:`output`})]})}),(0,J.jsx)(`tbody`,{children:ce.map(e=>(0,J.jsxs)(`tr`,{className:`${e.isError?`is-err`:``}${T.has(e.id)?` is-selected`:``}`.trim()||void 0,onClick:()=>void H(e),children:[(0,J.jsx)(`td`,{onClick:e=>e.stopPropagation(),children:(0,J.jsx)(`input`,{type:`checkbox`,"aria-label":`Select row`,checked:T.has(e.id),onChange:()=>fe(e.id)})}),(0,J.jsx)(`td`,{children:Cc(e.ts)}),(0,J.jsx)(`td`,{children:e.tool}),(0,J.jsx)(`td`,{children:(0,J.jsx)(`span`,{className:`risk risk-${e.risk}`,children:e.risk.replace(`WRITE_IDEMPOTENT`,`WRITE·I`)})}),(0,J.jsx)(`td`,{children:e.device??`—`}),(0,J.jsx)(`td`,{className:`num`,children:xc(e.durationMs)}),(0,J.jsx)(`td`,{children:(0,J.jsx)(`span`,{className:e.isError?`status-err`:`status-ok`,children:e.isError?`error`:`ok`})}),(0,J.jsx)(`td`,{className:`preview`,children:e.isError?e.error??`error`:e.output||`—`})]},e.id))})]})})]})]}),C&&(0,J.jsx)(OY,{event:C,onClose:()=>w(null)})]})}var tX=document.getElementById(`root`);tX&&(0,y.createRoot)(tX).render((0,J.jsx)(eX,{}));
|
|
82
82
|
</script>
|
|
83
83
|
<style>
|
|
84
|
-
@import "https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap";@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:var(--mt-sans);--font-mono:var(--mt-mono)}}@layer base,components;@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.isolate{isolation:isolate}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.table{display:table}.shrink{flex-shrink:1}.grow{flex-grow:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.border{border-style:var(--tw-border-style);border-width:1px}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}}:root{--background:#0a0a0a;--foreground:#ededed;--card:#111;--card-foreground:#ededed;--popover:#111;--popover-foreground:#ededed;--primary:#ededed;--primary-foreground:#0a0a0a;--secondary:#1a1a1a;--secondary-foreground:#ededed;--muted:#1a1a1a;--muted-foreground:#a1a1a1;--accent:var(--page-accent,#3291ff);--accent-foreground:#0a0a0a;--destructive:#ff5c5c;--destructive-foreground:#fff;--success:#2dd4a7;--warning:#f5a623;--border:#2a2a2a;--input:#2a2a2a;--ring:var(--page-accent,#3291ff);--radius:.5rem}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}: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}.btn.is-active{border-color:var(--mt-accent);color:var(--mt-accent)}.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:"JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;--mt-mono:"JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;--mt-display:"JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;--mt-bg:#0a0a0a;--mt-surface:#111;--mt-surface-2:#1a1a1a;--mt-border:#2a2a2a;--mt-border-strong:#3a3a3a;--mt-text:#ededed;--mt-text-dim:#a1a1a1;--mt-text-faint:#6e6e6e;--mt-good:#d4d4d8;--mt-warn:#a1a1a1;--mt-bad:#ff5c5c;--mt-radius:12px;--mt-radius-sm:8px;--sky:#3291ff;--teal:#2dd4bf;--cyan:#45d4ee;--lime:#69d05a;--coral:#ff5c5c;--amber:#f5a623;--pink:#ff4d8d;--violet:#9d7bff;--page-accent:#3291ff;--page-accent-2:#2dd4bf;--mt-accent:var(--page-accent);--grad:linear-gradient(115deg, var(--page-accent), var(--page-accent-2) 90%);--ease:cubic-bezier(.22, 1, .36, 1);--glass:#111111b8}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:after{content:"";z-index:-2;background-image:linear-gradient(color-mix(in srgb, var(--mt-border) 55%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, var(--mt-border) 55%, transparent) 1px, transparent 1px);opacity:.32;pointer-events:none;background-size:52px 52px;position:fixed;inset:0;-webkit-mask-image:radial-gradient(circle at 50% 0,#000,#0000 72%);mask-image:radial-gradient(circle at 50% 0,#000,#0000 72%)}.main:before{content:"";z-index:-1;background:radial-gradient(52% 38% at 18% -8%, color-mix(in srgb, var(--page-accent) 14%, transparent), transparent 70%), radial-gradient(46% 36% at 100% 4%, color-mix(in srgb, var(--page-accent-2) 11%, transparent), transparent 68%);opacity:.9;transition:background .5s var(--ease);pointer-events:none;position:fixed;inset:0}.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 6px 20px color-mix(in srgb, var(--page-accent) 34%, transparent);transition:background .4s var(--ease), box-shadow .4s var(--ease);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{font-family:var(--mt-display);letter-spacing:-.03em;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(--mt-text) 7%, transparent)}.nav__item.is-active{color:var(--mt-text);border-color:color-mix(in srgb, var(--page-accent) 36%, var(--mt-border));background:linear-gradient(100deg, color-mix(in srgb, var(--page-accent) 16%, transparent), color-mix(in srgb, var(--page-accent-2) 7%, transparent))}.nav__item.is-active svg{opacity:1;color:var(--page-accent)}.nav__item.is-active:before{content:"";background:var(--page-accent);width:3px;box-shadow:0 0 12px color-mix(in srgb, var(--page-accent) 70%, transparent);border-radius:999px;position:absolute;top:18%;bottom:18%;left:-1px}.nav__item{position:relative}.nav__badge{font-family:var(--mt-mono);background:color-mix(in srgb, var(--page-accent) 24%, transparent);color:var(--mt-text);border-radius:999px;margin-left:auto;padding:1px 7px;font-size:10px;animation:.4s cubic-bezier(.19,1,.22,1) nav-badge-pop;display:inline-block}@keyframes nav-badge-pop{0%{transform:scale(1)}35%{background:color-mix(in srgb, var(--page-accent) 60%, transparent);transform:scale(1.35)}to{transform:scale(1)}}@media (prefers-reduced-motion:reduce){.nav__badge{animation:none}}.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{font-family:var(--mt-display);letter-spacing:-.03em;background:linear-gradient(100deg, var(--mt-text) 30%, color-mix(in srgb, var(--page-accent) 78%, var(--mt-text)));-webkit-text-fill-color:transparent;transition:background .4s var(--ease);-webkit-background-clip:text;background-clip:text;margin:0;font-size:clamp(20px,2.4vw,28px);font-weight:700;line-height:1.15}.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}.help-toggle{border:1px solid var(--mt-border);background:var(--mt-surface);color:var(--mt-text-dim);font-family:var(--mt-sans);cursor:pointer;transition:color .18s, border-color .18s, background .18s, transform .12s var(--ease);border-radius:999px;align-items:center;gap:7px;padding:7px 13px 7px 10px;font-size:12.5px;font-weight:500;display:inline-flex}.help-toggle:hover{color:var(--mt-text);border-color:var(--mt-border-strong);transform:translateY(-1px)}.help-toggle.is-on{color:var(--page-accent);border-color:color-mix(in srgb, var(--page-accent) 45%, var(--mt-border));background:color-mix(in srgb, var(--page-accent) 12%, var(--mt-surface))}.help-toggle__q{background:color-mix(in srgb, var(--page-accent) 22%, transparent);width:17px;height:17px;color:var(--page-accent);border-radius:999px;place-items:center;font-size:11px;font-weight:700;display:grid}.pagehelp{border-radius:var(--mt-radius);border:1px solid color-mix(in srgb, var(--page-accent) 28%, var(--mt-border));background:linear-gradient(120deg, color-mix(in srgb, var(--page-accent) 9%, var(--mt-surface)), var(--mt-surface) 60%);gap:14px;padding:16px 18px;display:flex}.pagehelp__icon{color:#0a0a0a;background:var(--grad);width:30px;height:30px;box-shadow:0 4px 14px color-mix(in srgb, var(--page-accent) 35%, transparent);border-radius:9px;flex:none;place-items:center;font-size:15px;font-weight:700;display:grid}.pagehelp__body{min-width:0}.pagehelp__what{color:var(--mt-text);margin:2px 0 8px;font-size:13.5px;line-height:1.55}.pagehelp__tips{gap:4px;margin:0;padding-left:18px;display:grid}.pagehelp__tips li{color:var(--mt-text-dim);font-size:12.5px;line-height:1.5}.pagehelp__tips li::marker{color:var(--page-accent)}.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-ws{--lc:#34d399}.hero__live.is-sse{--lc:#f59e0b}.hero__live.is-on{color:var(--mt-text);border-color:color-mix(in srgb, var(--lc,var(--mt-good)) 40%, var(--mt-border))}.hero__live.is-on .dot{background:var(--lc,var(--mt-good));box-shadow:0 0 0 4px color-mix(in srgb, var(--lc,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(--lc,var(--mt-good)) 55%, transparent)}70%{box-shadow:0 0 0 9px color-mix(in srgb, var(--lc,var(--mt-good)) 0%, transparent)}to{box-shadow:0 0 0 0 color-mix(in srgb, var(--lc,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(--page-accent) 42%, 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(--mt-surface);border:1px solid var(--mt-border);border-radius:var(--mt-radius);transition:border-color .25s var(--ease);padding:18px;position:relative;box-shadow:0 1px 2px #0000004d,inset 0 1px #ffffff08}.panel:hover{border-color:color-mix(in srgb, var(--mt-text) 13%, var(--mt-border))}.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);color:var(--rc,var(--mt-text-dim));border:1px solid color-mix(in srgb, var(--rc,var(--mt-border)) 45%, var(--mt-border));background:color-mix(in srgb, var(--rc,transparent) 12%, transparent);text-transform:uppercase;letter-spacing:.04em;border-radius:999px;padding:1px 8px;font-size:10px;display:inline-block}.risk-READ{--rc:#34d399}.risk-WRITE{--rc:#3291ff}.risk-WRITE_IDEMPOTENT{--rc:#2dd4bf}.risk-DESTRUCTIVE{--rc:#f59e0b}.risk-DANGEROUS{--rc:#ef4444}.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}pre.body.ros{color:#c9c9d1}pre.body.ros .ros-key{color:#7dd3fc}pre.body.ros .ros-str{color:#86efac}pre.body.ros .ros-num{color:#fcd34d}pre.body.ros .ros-ip{color:#5eead4}pre.body.ros .ros-mac{color:#c4b5fd}pre.body.ros .ros-good{color:#4ade80;font-weight:500}pre.body.ros .ros-bad{color:#f87171;font-weight:500}pre.body.ros .ros-bool{color:#d8b4fe}pre.body.ros .ros-dim{color:#8b8b94}pre.body.ros .ros-comment{color:#6b7280;font-style:italic}.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(--page-accent) 55%, var(--mt-border));background:color-mix(in srgb, var(--page-accent) 10%, var(--mt-surface-2))}.btn.is-active{border-color:color-mix(in srgb, var(--page-accent) 70%, var(--mt-border));color:var(--page-accent);background:color-mix(in srgb, var(--page-accent) 12%, var(--mt-surface-2))}:where(button,a,input,select,textarea,[tabindex]):focus-visible{outline:2px solid color-mix(in srgb, var(--page-accent) 70%, transparent);outline-offset:2px;border-radius:var(--mt-radius-sm)}.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)}.backup-path{align-items:center;gap:8px;max-width:min(60vw,520px);display:inline-flex}.backup-path>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.backup-path-input{font:11px var(--mt-mono);color:var(--mt-text);background:var(--mt-surface-2);border:1px solid color-mix(in srgb, var(--mt-accent) 45%, var(--mt-border));border-radius:var(--mt-radius-sm);width:min(48vw,380px);padding:5px 9px}.backup-opts{flex-wrap:wrap;align-items:center}.backup-opt{font:11px var(--mt-mono);color:var(--mt-text-dim);background:var(--mt-surface-2);border:1px solid var(--mt-border);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:100px;align-items:center;gap:6px;padding:5px 11px 5px 9px;transition:border-color .18s,color .18s,background .18s;display:inline-flex}.backup-opt input{accent-color:var(--mt-accent);cursor:pointer;margin:0}.backup-opt[data-on]{color:var(--mt-text);border-color:color-mix(in srgb, var(--mt-accent) 55%, var(--mt-border));background:color-mix(in srgb, var(--mt-accent) 12%, var(--mt-surface-2))}.backup-opt[data-disabled]{opacity:.45;cursor:not-allowed}.backup-opt[data-disabled] input{cursor:not-allowed}.linkish{font:inherit;color:var(--mt-accent);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0}.linkish:hover{text-decoration:underline}.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}.cfgver-cp{align-items:center;gap:8px;display:inline-flex}.cfgver{gap:2px;margin:0;padding:0 0 0 4px;list-style:none;display:grid}.cfgver__row{border-radius:var(--mt-radius-sm);transition:background .18s var(--ease);grid-template-columns:18px minmax(0,1fr) auto;align-items:start;gap:10px;padding:11px 6px;display:grid;position:relative}.cfgver__row:hover{background:color-mix(in srgb, var(--mt-text) 4%, transparent)}.cfgver__row:before{content:"";background:var(--mt-border);width:1.5px;position:absolute;top:22px;bottom:-2px;left:12px}.cfgver__row:last-child:before{display:none}.cfgver__dot{border:2px solid var(--mt-border-strong);background:var(--mt-surface);z-index:1;border-radius:999px;width:11px;height:11px;margin-top:5px}.cfgver__row.is-head .cfgver__dot{border-color:var(--page-accent);background:var(--page-accent);box-shadow:0 0 0 4px color-mix(in srgb, var(--page-accent) 22%, transparent)}.cfgver__main{min-width:0}.cfgver__line{flex-wrap:wrap;align-items:center;gap:9px;font-size:12.5px;display:flex}.cfgver__kind{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.05em;border:1px solid var(--mt-border);color:var(--mt-text-dim);border-radius:999px;padding:2px 7px;font-size:10px}.cfgver__kind--checkpoint{color:var(--amber);border-color:color-mix(in srgb, var(--amber) 45%, var(--mt-border));background:color-mix(in srgb, var(--amber) 12%, transparent)}.cfgver__label{color:var(--mt-text);font-weight:600}.cfgver__time{font-family:var(--mt-mono);color:var(--mt-text-dim);font-size:11.5px}.cfgver__cur{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.05em;color:var(--page-accent);font-size:10px}.cfgver__same{color:var(--mt-text-faint);font-size:11.5px}.cfgver__drift{font-family:var(--mt-mono);align-items:baseline;gap:7px;font-size:11.5px;display:inline-flex}.cfgver__drift .add{color:var(--mt-good)}.cfgver__drift .rem{color:var(--mt-bad)}.cfgver__diff{margin-top:9px}.cfgver__diff pre.body{max-height:280px;margin:0;overflow:auto}.cfgver__actions{flex:none;align-items:center;gap:6px;display:inline-flex}.cfgver__del{color:var(--mt-text-faint);padding-left:8px;padding-right:8px}.cfgver__del:hover{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border))}.fguide{gap:18px;display:grid}.fguide__sechd{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.09em;color:var(--page-accent);margin:0 0 9px;font-size:11px}.fguide__list{gap:9px;display:grid}.fguide__item{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:color-mix(in srgb, var(--mt-surface-2) 45%, transparent);transition:border-color .18s, transform .12s var(--ease);padding:11px 13px}.fguide__item:hover{border-color:color-mix(in srgb, var(--page-accent) 40%, var(--mt-border));transform:translate(2px)}.fguide__top{flex-wrap:wrap;align-items:baseline;gap:10px;display:flex}.fguide__path{font-family:var(--mt-mono);color:var(--mt-text);font-size:12.5px;font-weight:500}.fguide__type{font-family:var(--mt-mono);color:var(--page-accent);background:color-mix(in srgb, var(--page-accent) 13%, transparent);border-radius:999px;padding:1px 7px;font-size:10.5px}.fguide__req{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.05em;color:var(--mt-bad);font-size:10px}.fguide__def{color:var(--mt-text-faint);font-size:11px}.fguide__def code{color:var(--mt-text-dim)}.fguide__desc{color:var(--mt-text-dim);margin:6px 0 0;font-size:12.5px;line-height:1.5}.fguide__enum{flex-wrap:wrap;gap:6px;margin-top:7px;display:flex}.fguide__enum code{font-family:var(--mt-mono);border:1px solid var(--mt-border);color:var(--mt-text-dim);border-radius:6px;padding:1px 7px;font-size:11px}.copybtn{position:relative}.copybtn.is-copied{color:var(--page-accent);border-color:color-mix(in srgb, var(--page-accent) 55%, var(--mt-border))}.copybtn__tip{background:var(--page-accent);color:#0a0a0a;font-family:var(--mt-sans);white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .16s, transform .16s var(--ease);z-index:50;border-radius:6px;padding:3px 9px;font-size:11px;font-weight:600;position:absolute;bottom:calc(100% + 8px);left:50%;transform:translate(-50%,4px)}.copybtn__tip:after{content:"";border:4px solid #0000;border-top-color:var(--page-accent);position:absolute;top:100%;left:50%;transform:translate(-50%)}.copybtn.is-copied .copybtn__tip{opacity:1;transform:translate(-50%)}.iconbtn{border:1px solid var(--mt-border);background:var(--mt-surface);width:26px;height:26px;color:var(--mt-text-dim);cursor:pointer;border-radius:7px;place-items:center;padding:0;transition:color .16s,border-color .16s,background .16s;display:inline-grid}.iconbtn:hover{color:var(--page-accent);border-color:color-mix(in srgb, var(--page-accent) 50%, var(--mt-border));background:color-mix(in srgb, var(--page-accent) 10%, var(--mt-surface))}.copybtn--icon.is-copied{color:var(--page-accent);border-color:color-mix(in srgb, var(--page-accent) 55%, var(--mt-border))}.sheet__tool{align-items:center;gap:9px;display:inline-flex}.chart{width:100%;font-family:var(--mt-mono)}.chart .recharts-cartesian-grid line{stroke:color-mix(in srgb, var(--mt-border) 80%, transparent)}.chart .recharts-text,.chart text{fill:var(--mt-text-faint)}.chart .recharts-surface:focus,.chart svg:focus{outline:none}.chart-tip{background:color-mix(in srgb, var(--mt-surface) 96%, transparent);border:1px solid var(--mt-border-strong);border-radius:var(--mt-radius-sm);font-family:var(--mt-mono);min-width:120px;padding:8px 10px;font-size:11.5px;box-shadow:0 8px 24px #00000073}.chart-tip__label{color:var(--mt-text-dim);margin-bottom:5px;font-size:10.5px}.chart-tip__row{align-items:center;gap:7px;display:flex}.chart-tip__dot{border-radius:3px;flex:none;width:9px;height:9px}.chart-tip__name{color:var(--mt-text-dim);text-transform:capitalize}.chart-tip__val{color:var(--mt-text);font-variant-numeric:tabular-nums;margin-left:auto;font-weight:500}.chart--donut{flex-direction:column;flex:1;justify-content:center;align-items:center;gap:12px;display:flex}.chart-donut__svg{width:100%;max-width:200px}.chart-legend{font-family:var(--mt-mono);color:var(--mt-text-dim);flex-wrap:wrap;justify-content:center;gap:6px 14px;font-size:11px;display:flex}.chart-legend span{align-items:center;gap:6px;display:inline-flex}.chart-legend i{border-radius:3px;width:9px;height:9px}.chart-legend b{color:var(--mt-text);font-weight:500}.chart--spark{position:relative}.chart-spark__last{font-family:var(--mt-mono);z-index:1;font-size:10px;font-weight:600;position:absolute;top:0;right:2px}.gauge__radial{place-items:center;width:72px;height:72px;display:grid;position:relative}.gauge__pct{font-family:var(--mt-mono);pointer-events:none;place-items:center;font-size:13px;font-weight:700;display:grid;position:absolute;inset:0}.geist-btn{font-family:var(--mt-sans);border-radius:var(--mt-radius-sm);border:1px solid var(--mt-border-strong);background:var(--mt-surface);color:var(--mt-text);cursor:pointer;white-space:nowrap;transition:background .16s var(--ease), border-color .16s, color .16s, transform .1s var(--ease), box-shadow .16s;justify-content:center;align-items:center;gap:7px;font-size:13px;font-weight:500;line-height:1;display:inline-flex}.geist-btn--sm{padding:6px 11px;font-size:12px}.geist-btn--md{padding:9px 15px}.geist-btn:hover{border-color:var(--mt-text);transform:translateY(-1px)}.geist-btn:active{transform:translateY(0)}.geist-btn:disabled{opacity:.5;cursor:not-allowed;transform:none}.geist-btn__icon{display:inline-flex}.geist-btn--accent{background:var(--page-accent);border-color:var(--page-accent);color:#0a0a0a;font-weight:600}.geist-btn--accent:hover{box-shadow:0 4px 16px color-mix(in srgb, var(--page-accent) 40%, transparent);border-color:var(--page-accent)}.geist-btn--success{border-color:color-mix(in srgb, var(--mt-good) 55%, var(--mt-border));color:var(--mt-good)}.geist-btn--warning{border-color:color-mix(in srgb, var(--mt-warn) 55%, var(--mt-border));color:var(--mt-warn)}.geist-btn--error{border-color:color-mix(in srgb, var(--mt-bad) 55%, var(--mt-border));color:var(--mt-bad)}.geist-btn--error:hover{background:color-mix(in srgb, var(--mt-bad) 14%, var(--mt-surface));border-color:var(--mt-bad)}.geist-btn--secondary{color:var(--mt-text-dim);border-color:var(--mt-border)}.geist-btn--ghost{background:0 0;border-color:#0000}.geist-btn--ghost:hover{background:color-mix(in srgb, var(--mt-text) 7%, transparent);border-color:var(--mt-border)}.geist-note{border-radius:var(--mt-radius-sm);border:1px solid var(--mt-border);background:var(--mt-surface);color:var(--mt-text-dim);padding:11px 14px;font-size:13px;line-height:1.55}.geist-note__label{color:var(--mt-text);text-transform:capitalize}.geist-note--secondary{border-color:color-mix(in srgb, var(--page-accent) 30%, var(--mt-border));background:color-mix(in srgb, var(--page-accent) 7%, var(--mt-surface))}.geist-note--success{border-color:color-mix(in srgb, var(--mt-good) 40%, var(--mt-border));background:color-mix(in srgb, var(--mt-good) 8%, var(--mt-surface))}.geist-note--success .geist-note__label{color:var(--mt-good)}.geist-note--warning{border-color:color-mix(in srgb, var(--mt-warn) 40%, var(--mt-border));background:color-mix(in srgb, var(--mt-warn) 8%, var(--mt-surface))}.geist-note--warning .geist-note__label{color:var(--mt-warn)}.geist-note--error{border-color:color-mix(in srgb, var(--mt-bad) 40%, var(--mt-border));background:color-mix(in srgb, var(--mt-bad) 8%, var(--mt-surface))}.geist-note--error .geist-note__label{color:var(--mt-bad)}.geist-badge{font-family:var(--mt-mono);border:1px solid var(--mt-border);background:var(--mt-surface-2);color:var(--mt-text-dim);border-radius:999px;align-items:center;gap:5px;padding:2px 8px;font-size:10.5px;font-weight:500;display:inline-flex}.geist-badge--accent{color:var(--page-accent);border-color:color-mix(in srgb, var(--page-accent) 40%, var(--mt-border));background:color-mix(in srgb, var(--page-accent) 12%, transparent)}.geist-badge--success{color:var(--mt-good);border-color:color-mix(in srgb, var(--mt-good) 40%, var(--mt-border));background:color-mix(in srgb, var(--mt-good) 10%, transparent)}.geist-badge--warning{color:var(--mt-warn);border-color:color-mix(in srgb, var(--mt-warn) 40%, var(--mt-border))}.geist-badge--error{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 40%, var(--mt-border));background:color-mix(in srgb, var(--mt-bad) 10%, transparent)}.geist-dot{background:var(--mt-text-faint);border-radius:999px;flex:none;width:9px;height:9px;display:inline-block}.geist-dot--success{background:var(--mt-good)}.geist-dot--warning{background:var(--mt-warn)}.geist-dot--error{background:var(--mt-bad)}.geist-dot--accent{background:var(--page-accent)}.geist-dot.is-pulse{box-shadow:0 0 0 0 color-mix(in srgb, currentColor 60%, transparent);animation:1.8s infinite geist-pulse}@keyframes geist-pulse{0%{box-shadow:0 0 0 0 color-mix(in srgb, var(--mt-good) 55%, transparent)}70%{box-shadow:0 0 0 7px #0000}to{box-shadow:0 0 #0000}}.geist-spinner{width:16px;height:16px;display:inline-block;position:relative}.geist-spinner--sm{width:13px;height:13px}.geist-spinner span{transform-origin:50% 178%;opacity:.15;background:currentColor;border-radius:2px;width:8%;height:28%;animation:1.2s linear infinite geist-spin;position:absolute;top:0;left:46%}@keyframes geist-spin{0%{opacity:1}to{opacity:.15}}.geist-input,.geist-select{font-family:var(--mt-mono);color:var(--mt-text);background:var(--mt-surface);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);padding:8px 11px;font-size:12.5px;transition:border-color .16s,box-shadow .16s}.geist-input:focus,.geist-select:focus{border-color:var(--page-accent);box-shadow:0 0 0 3px color-mix(in srgb, var(--page-accent) 22%, transparent);outline:none}.geist-card{border:1px solid var(--mt-border);border-radius:var(--mt-radius);background:var(--mt-surface);padding:16px}.geist-card.is-hoverable{transition:border-color .18s, transform .18s var(--ease)}.geist-card.is-hoverable:hover{border-color:color-mix(in srgb, var(--page-accent) 40%, var(--mt-border));transform:translateY(-2px)}.geist-tooltip{display:inline-flex;position:relative}.geist-tooltip__pop{border-radius:var(--mt-radius-sm);background:var(--mt-surface);border:1px solid var(--mt-border-strong);color:var(--mt-text);font-family:var(--mt-mono);white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .16s, transform .16s var(--ease);z-index:60;padding:6px 10px;font-size:11.5px;position:absolute;bottom:calc(100% + 8px);left:50%;transform:translate(-50%,4px);box-shadow:0 8px 24px #00000073}.geist-tooltip:hover .geist-tooltip__pop,.geist-tooltip:focus-visible .geist-tooltip__pop{opacity:1;transform:translate(-50%)}.clients-toolbar{align-items:center;gap:8px;display:flex}.clients-toolbar .geist-input{width:200px}.clients-counts{margin-bottom:10px;font-size:12px}.clients-error{margin-bottom:10px}.clients-table{flex-direction:column;gap:2px;display:flex}.clients-row{border-radius:var(--mt-radius-sm);cursor:pointer;grid-template-columns:120px 1.3fr 150px 80px 130px minmax(280px,auto);align-items:center;gap:10px;padding:8px 10px;font-size:13px;display:grid}.clients-row--head{cursor:default;color:var(--mt-text-faint);text-transform:uppercase;letter-spacing:.04em;font-size:11px}.clients-row:not(.clients-row--head):hover{background:var(--mt-surface-2)}.clients-row.is-selected{background:color-mix(in srgb, var(--page-accent) 14%, var(--mt-surface-2))}.clients-row.is-blocked .clients-ip,.clients-row.is-blocked .clients-name{color:var(--mt-text-faint);text-decoration:line-through}.clients-ip,.clients-name{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.clients-mac{color:var(--mt-text-dim);font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:11.5px}.clients-badges{align-items:center;gap:6px;display:flex;overflow:hidden}.clients-actions,.clients-actions-h{justify-content:flex-end;gap:6px;display:flex}.clients-detail{border-top:1px solid var(--mt-border);margin-top:14px;padding-top:14px}.clients-detail__hd{flex-wrap:wrap;align-items:baseline;gap:12px;margin-bottom:10px;display:flex}.clients-detail__title{font-size:14px;font-weight:600}.clients-rates{font-variant-numeric:tabular-nums;gap:18px;margin-bottom:8px;display:flex}.clients-rates .rate{font-weight:600}.clients-rates .rate.rx{color:var(--lime)}.clients-rates .rate.tx{color:var(--amber)}.clients-chart{background:var(--mt-surface);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);width:100%;max-width:520px;height:150px;display:block}.clients-chart .line{fill:none;stroke-width:2px}.clients-chart .rx.line{stroke:var(--lime)}.clients-chart .tx.line{stroke:var(--amber)}.clients-chart .rx.area{fill:color-mix(in srgb, var(--lime) 18%, transparent);stroke:none}.clients-chart .tx.area{fill:color-mix(in srgb, var(--amber) 14%, transparent);stroke:none}.clients-totals{font-variant-numeric:tabular-nums;margin-top:8px;font-size:12px}.clients-edit{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:var(--mt-surface-2);flex-wrap:wrap;align-items:center;gap:8px;margin-top:14px;padding:10px 12px;display:flex}.clients-edit__label{color:var(--mt-text-dim);font-size:13px}.clients-edit .geist-input{width:220px}.aaa-tabs{border-bottom:1px solid var(--mt-border);flex-wrap:wrap;gap:4px;margin-bottom:14px;display:flex}.aaa-tab{appearance:none;color:var(--mt-text-dim);font:inherit;cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;margin-bottom:-1px;padding:7px 12px;font-size:13px}.aaa-tab:hover{color:var(--mt-text)}.aaa-tab.is-active{color:var(--mt-text);border-bottom-color:var(--page-accent)}.aaa-entity__toolbar{align-items:center;gap:10px;margin-bottom:12px;display:flex}.aaa-filter{width:200px}.aaa-error{margin-bottom:10px}.aaa-empty{padding:20px 4px}.aaa-table{flex-direction:column;gap:2px;display:flex}.aaa-table--scroll{overflow-x:auto}.aaa-row{border-radius:var(--mt-radius-sm);align-items:center;gap:10px;padding:8px 10px;font-size:13px;display:grid}.aaa-row--head{color:var(--mt-text-faint);text-transform:uppercase;letter-spacing:.04em;font-size:11px}.aaa-row:not(.aaa-row--head):hover{background:var(--mt-surface-2)}.aaa-row.is-off .aaa-cell{color:var(--mt-text-faint)}.aaa-row--sessions{grid-template-columns:repeat(9,minmax(90px,1fr));min-width:900px}.aaa-cell{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.aaa-actions,.aaa-actions-h{justify-content:flex-end;gap:6px;display:flex}.aaa-form,.aaa-singleton{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:var(--mt-surface-2);margin-bottom:14px;padding:14px}.aaa-form__title,.aaa-singleton__title{margin-bottom:10px;font-size:13px;font-weight:600}.aaa-singleton__current{margin-bottom:12px;font-size:12px}.aaa-form__grid{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px 14px;display:grid}.aaa-field{flex-direction:column;gap:4px;display:flex}.aaa-field__label{color:var(--mt-text-dim);font-size:11.5px}.aaa-form__actions{align-items:center;gap:10px;margin-top:14px;display:flex}.aaa-settings{flex-direction:column;gap:8px;display:flex}.clients-limits{border-top:1px solid var(--mt-border);max-width:520px;margin-top:14px;padding-top:12px}.clients-limits__hd{align-items:baseline;gap:12px;margin-bottom:8px;display:flex}.clients-limits__label{font-size:13px;font-weight:600}.clients-limits__row{flex-wrap:wrap;align-items:flex-end;gap:8px;display:flex}.clients-limits__field{flex-direction:column;gap:3px;font-size:11.5px;display:flex}.clients-limits__field .geist-input{width:150px}.clients-limits__msg{margin-top:6px;font-size:12px}.usage-chart__empty{padding:16px 4px;font-size:12.5px}.usage-chart__legend{font-variant-numeric:tabular-nums;align-items:baseline;gap:14px;margin-bottom:6px;font-size:12px;display:flex}.usage-chart__legend .rate{font-weight:600}.usage-chart__legend .rate.rx{color:var(--lime)}.usage-chart__legend .rate.tx{color:var(--amber)}.usage-chart__svg{background:var(--mt-surface);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);width:100%;height:160px;display:block}.usage-chart__svg .line{fill:none;stroke-width:2px;vector-effect:non-scaling-stroke}.usage-chart__svg .rx.line{stroke:var(--lime)}.usage-chart__svg .tx.line{stroke:var(--amber)}.usage-chart__svg .rx.area{fill:color-mix(in srgb, var(--lime) 18%, transparent);stroke:none}.usage-chart__svg .tx.area{fill:color-mix(in srgb, var(--amber) 14%, transparent);stroke:none}.clients-history,.aaa-usage__section{margin-top:16px}.clients-history__hd,.aaa-usage__title{color:var(--mt-text-dim);margin-bottom:8px;font-size:12px;font-weight:600}.aaa-usage__bar{align-items:center;gap:8px;margin-bottom:12px;display:flex}.heatmap__hd{align-items:baseline;gap:10px;margin-bottom:6px;display:flex}.heatmap__title{font-size:12.5px;font-weight:600}.heatmap__scroll{padding-bottom:4px;overflow-x:auto}.heatmap__svg{display:block}.heatmap__month,.heatmap__wd{fill:var(--mt-text-dim);font-family:inherit;font-size:9px}.heatmap{position:relative}.heatmap__cell{stroke:color-mix(in srgb, var(--mt-border) 60%, transparent);stroke-width:1px;cursor:pointer;transition:stroke .1s}.heatmap__cell.is-hover{stroke:var(--mt-text);stroke-width:1.5px}.heatmap__tip{z-index:30;background:linear-gradient(180deg, color-mix(in srgb, var(--mt-surface-2) 92%, var(--lime)), var(--mt-surface-2));border:1px solid color-mix(in srgb, var(--lime) 35%, var(--mt-border));pointer-events:none;min-width:150px;animation:heatmap-tip-in .12s var(--ease);border-radius:10px;padding:8px 10px;position:absolute;transform:translate(-50%,calc(-100% - 9px));box-shadow:0 8px 24px #00000073}.heatmap__tip:after{content:"";background:var(--mt-surface-2);border-right:1px solid color-mix(in srgb, var(--lime) 35%, var(--mt-border));border-bottom:1px solid color-mix(in srgb, var(--lime) 35%, var(--mt-border));width:11px;height:11px;position:absolute;bottom:-6px;left:50%;transform:translate(-50%)rotate(45deg)}@keyframes heatmap-tip-in{0%{opacity:0;transform:translate(-50%,calc(-100% - 3px))}to{opacity:1;transform:translate(-50%,calc(-100% - 9px))}}.heatmap__tip-head{align-items:center;gap:7px;font-size:13px;display:flex}.heatmap__tip-head b{font-weight:600}.heatmap__tip-head .heatmap__swatch{width:12px;height:12px}.heatmap__tip-date{color:var(--mt-text-dim);font-variant-numeric:tabular-nums;margin-top:3px;font-size:11.5px}.heatmap__cell.lvl0,.heatmap__swatch.lvl0{fill:var(--mt-surface-2);background:var(--mt-surface-2)}.heatmap__cell.lvl1,.heatmap__swatch.lvl1{fill:color-mix(in srgb, var(--lime) 30%, var(--mt-surface-2));background:color-mix(in srgb, var(--lime) 30%, var(--mt-surface-2))}.heatmap__cell.lvl2,.heatmap__swatch.lvl2{fill:color-mix(in srgb, var(--lime) 50%, var(--mt-surface-2));background:color-mix(in srgb, var(--lime) 50%, var(--mt-surface-2))}.heatmap__cell.lvl3,.heatmap__swatch.lvl3{fill:color-mix(in srgb, var(--lime) 72%, transparent);background:color-mix(in srgb, var(--lime) 72%, transparent)}.heatmap__cell.lvl4,.heatmap__swatch.lvl4{fill:var(--lime);background:var(--lime)}.heatmap__legend{align-items:center;gap:4px;margin-top:8px;font-size:11px;display:flex}.heatmap__swatch{border-radius:2px;width:11px;height:11px;display:inline-block}.input{font:12px var(--mt-mono);color:var(--mt-text);background:var(--mt-surface-2);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);width:min(52vw,420px);padding:6px 10px}.input:focus{border-color:color-mix(in srgb, var(--mt-accent) 55%, var(--mt-border));outline:none}.btn-sm{padding:3px 8px;font-size:10px}.mod-toolbar{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:6px;display:flex}.mod-groups{flex-direction:column;gap:18px;margin-top:12px;display:flex}.mod-group__hd{border-bottom:1px solid var(--mt-border);align-items:center;gap:10px;margin-bottom:8px;padding-bottom:6px;display:flex}.mod-group__title{color:var(--mt-text);margin:0;font-size:13px;font-weight:600}.mod-list{grid-template-columns:repeat(auto-fill,minmax(min(100%,360px),1fr));gap:8px;display:grid}.mod-row{background:var(--mt-surface-2);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);cursor:pointer;-webkit-user-select:none;user-select:none;align-items:flex-start;gap:10px;padding:9px 11px;transition:border-color .18s,background .18s;display:flex}.mod-row:hover{border-color:color-mix(in srgb, var(--mt-accent) 35%, var(--mt-border))}.mod-row[data-on]{border-color:color-mix(in srgb, var(--mt-accent) 50%, var(--mt-border));background:color-mix(in srgb, var(--mt-accent) 10%, var(--mt-surface-2))}.mod-row input{accent-color:var(--mt-accent);cursor:pointer;margin:2px 0 0}.mod-row__main{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.mod-row__name{color:var(--mt-text);flex-wrap:wrap;align-items:center;gap:7px;font-size:12px;font-weight:600;display:flex}.mod-row__slug{font:10px var(--mt-mono);color:var(--mt-text-dim);background:var(--mt-bg);border:1px solid var(--mt-border);border-radius:4px;padding:1px 5px}.mod-row__desc{font-size:11px;line-height:1.35}.mod-row__count{font:10px var(--mt-mono);white-space:nowrap;padding-top:1px}
|
|
84
|
+
@import "https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&display=swap";@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-border-style:solid;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial}}}@layer theme{:root,:host{--font-sans:var(--mt-sans);--font-mono:var(--mt-mono)}}@layer base,components;@layer utilities{.visible{visibility:visible}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.isolate{isolation:isolate}.container{width:100%}@media (width>=40rem){.container{max-width:40rem}}@media (width>=48rem){.container{max-width:48rem}}@media (width>=64rem){.container{max-width:64rem}}@media (width>=80rem){.container{max-width:80rem}}@media (width>=96rem){.container{max-width:96rem}}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.table{display:table}.shrink{flex-shrink:1}.grow{flex-grow:1}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.border{border-style:var(--tw-border-style);border-width:1px}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}}:root{--background:#0a0a0a;--foreground:#ededed;--card:#111;--card-foreground:#ededed;--popover:#111;--popover-foreground:#ededed;--primary:#ededed;--primary-foreground:#0a0a0a;--secondary:#1a1a1a;--secondary-foreground:#ededed;--muted:#1a1a1a;--muted-foreground:#a1a1a1;--accent:var(--page-accent,#3291ff);--accent-foreground:#0a0a0a;--destructive:#ff5c5c;--destructive-foreground:#fff;--success:#2dd4a7;--warning:#f5a623;--border:#2a2a2a;--input:#2a2a2a;--ring:var(--page-accent,#3291ff);--radius:.5rem}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-blur{syntax:"*";inherits:false}@property --tw-brightness{syntax:"*";inherits:false}@property --tw-contrast{syntax:"*";inherits:false}@property --tw-grayscale{syntax:"*";inherits:false}@property --tw-hue-rotate{syntax:"*";inherits:false}@property --tw-invert{syntax:"*";inherits:false}@property --tw-opacity{syntax:"*";inherits:false}@property --tw-saturate{syntax:"*";inherits:false}@property --tw-sepia{syntax:"*";inherits:false}@property --tw-drop-shadow{syntax:"*";inherits:false}@property --tw-drop-shadow-color{syntax:"*";inherits:false}@property --tw-drop-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-drop-shadow-size{syntax:"*";inherits:false}: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}.btn.is-active{border-color:var(--mt-accent);color:var(--mt-accent)}.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:"JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;--mt-mono:"JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;--mt-display:"JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;--mt-bg:#0a0a0a;--mt-surface:#111;--mt-surface-2:#1a1a1a;--mt-border:#2a2a2a;--mt-border-strong:#3a3a3a;--mt-text:#ededed;--mt-text-dim:#a1a1a1;--mt-text-faint:#6e6e6e;--mt-good:#d4d4d8;--mt-warn:#a1a1a1;--mt-bad:#ff5c5c;--mt-radius:12px;--mt-radius-sm:8px;--sky:#3291ff;--teal:#2dd4bf;--cyan:#45d4ee;--lime:#69d05a;--coral:#ff5c5c;--amber:#f5a623;--pink:#ff4d8d;--violet:#9d7bff;--page-accent:#3291ff;--page-accent-2:#2dd4bf;--mt-accent:var(--page-accent);--grad:linear-gradient(115deg, var(--page-accent), var(--page-accent-2) 90%);--ease:cubic-bezier(.22, 1, .36, 1);--glass:#111111b8}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:after{content:"";z-index:-2;background-image:linear-gradient(color-mix(in srgb, var(--mt-border) 55%, transparent) 1px, transparent 1px), linear-gradient(90deg, color-mix(in srgb, var(--mt-border) 55%, transparent) 1px, transparent 1px);opacity:.32;pointer-events:none;background-size:52px 52px;position:fixed;inset:0;-webkit-mask-image:radial-gradient(circle at 50% 0,#000,#0000 72%);mask-image:radial-gradient(circle at 50% 0,#000,#0000 72%)}.main:before{content:"";z-index:-1;background:radial-gradient(52% 38% at 18% -8%, color-mix(in srgb, var(--page-accent) 14%, transparent), transparent 70%), radial-gradient(46% 36% at 100% 4%, color-mix(in srgb, var(--page-accent-2) 11%, transparent), transparent 68%);opacity:.9;transition:background .5s var(--ease);pointer-events:none;position:fixed;inset:0}.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 6px 20px color-mix(in srgb, var(--page-accent) 34%, transparent);transition:background .4s var(--ease), box-shadow .4s var(--ease);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{font-family:var(--mt-display);letter-spacing:-.03em;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(--mt-text) 7%, transparent)}.nav__item.is-active{color:var(--mt-text);border-color:color-mix(in srgb, var(--page-accent) 36%, var(--mt-border));background:linear-gradient(100deg, color-mix(in srgb, var(--page-accent) 16%, transparent), color-mix(in srgb, var(--page-accent-2) 7%, transparent))}.nav__item.is-active svg{opacity:1;color:var(--page-accent)}.nav__item.is-active:before{content:"";background:var(--page-accent);width:3px;box-shadow:0 0 12px color-mix(in srgb, var(--page-accent) 70%, transparent);border-radius:999px;position:absolute;top:18%;bottom:18%;left:-1px}.nav__item{position:relative}.nav__badge{font-family:var(--mt-mono);background:color-mix(in srgb, var(--page-accent) 24%, transparent);color:var(--mt-text);border-radius:999px;margin-left:auto;padding:1px 7px;font-size:10px;animation:.4s cubic-bezier(.19,1,.22,1) nav-badge-pop;display:inline-block}@keyframes nav-badge-pop{0%{transform:scale(1)}35%{background:color-mix(in srgb, var(--page-accent) 60%, transparent);transform:scale(1.35)}to{transform:scale(1)}}@media (prefers-reduced-motion:reduce){.nav__badge{animation:none}}.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{font-family:var(--mt-display);letter-spacing:-.03em;background:linear-gradient(100deg, var(--mt-text) 30%, color-mix(in srgb, var(--page-accent) 78%, var(--mt-text)));-webkit-text-fill-color:transparent;transition:background .4s var(--ease);-webkit-background-clip:text;background-clip:text;margin:0;font-size:clamp(20px,2.4vw,28px);font-weight:700;line-height:1.15}.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}.help-toggle{border:1px solid var(--mt-border);background:var(--mt-surface);color:var(--mt-text-dim);font-family:var(--mt-sans);cursor:pointer;transition:color .18s, border-color .18s, background .18s, transform .12s var(--ease);border-radius:999px;align-items:center;gap:7px;padding:7px 13px 7px 10px;font-size:12.5px;font-weight:500;display:inline-flex}.help-toggle:hover{color:var(--mt-text);border-color:var(--mt-border-strong);transform:translateY(-1px)}.help-toggle.is-on{color:var(--page-accent);border-color:color-mix(in srgb, var(--page-accent) 45%, var(--mt-border));background:color-mix(in srgb, var(--page-accent) 12%, var(--mt-surface))}.help-toggle__q{background:color-mix(in srgb, var(--page-accent) 22%, transparent);width:17px;height:17px;color:var(--page-accent);border-radius:999px;place-items:center;font-size:11px;font-weight:700;display:grid}.pagehelp{border-radius:var(--mt-radius);border:1px solid color-mix(in srgb, var(--page-accent) 28%, var(--mt-border));background:linear-gradient(120deg, color-mix(in srgb, var(--page-accent) 9%, var(--mt-surface)), var(--mt-surface) 60%);gap:14px;padding:16px 18px;display:flex}.pagehelp__icon{color:#0a0a0a;background:var(--grad);width:30px;height:30px;box-shadow:0 4px 14px color-mix(in srgb, var(--page-accent) 35%, transparent);border-radius:9px;flex:none;place-items:center;font-size:15px;font-weight:700;display:grid}.pagehelp__body{min-width:0}.pagehelp__what{color:var(--mt-text);margin:2px 0 8px;font-size:13.5px;line-height:1.55}.pagehelp__tips{gap:4px;margin:0;padding-left:18px;display:grid}.pagehelp__tips li{color:var(--mt-text-dim);font-size:12.5px;line-height:1.5}.pagehelp__tips li::marker{color:var(--page-accent)}.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-ws{--lc:#34d399}.hero__live.is-sse{--lc:#f59e0b}.hero__live.is-on{color:var(--mt-text);border-color:color-mix(in srgb, var(--lc,var(--mt-good)) 40%, var(--mt-border))}.hero__live.is-on .dot{background:var(--lc,var(--mt-good));box-shadow:0 0 0 4px color-mix(in srgb, var(--lc,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(--lc,var(--mt-good)) 55%, transparent)}70%{box-shadow:0 0 0 9px color-mix(in srgb, var(--lc,var(--mt-good)) 0%, transparent)}to{box-shadow:0 0 0 0 color-mix(in srgb, var(--lc,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(--page-accent) 42%, 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(--mt-surface);border:1px solid var(--mt-border);border-radius:var(--mt-radius);transition:border-color .25s var(--ease);padding:18px;position:relative;box-shadow:0 1px 2px #0000004d,inset 0 1px #ffffff08}.panel:hover{border-color:color-mix(in srgb, var(--mt-text) 13%, var(--mt-border))}.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);color:var(--rc,var(--mt-text-dim));border:1px solid color-mix(in srgb, var(--rc,var(--mt-border)) 45%, var(--mt-border));background:color-mix(in srgb, var(--rc,transparent) 12%, transparent);text-transform:uppercase;letter-spacing:.04em;border-radius:999px;padding:1px 8px;font-size:10px;display:inline-block}.risk-READ{--rc:#34d399}.risk-WRITE{--rc:#3291ff}.risk-WRITE_IDEMPOTENT{--rc:#2dd4bf}.risk-DESTRUCTIVE{--rc:#f59e0b}.risk-DANGEROUS{--rc:#ef4444}.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}pre.body.ros{color:#c9c9d1}pre.body.ros .ros-key{color:#7dd3fc}pre.body.ros .ros-str{color:#86efac}pre.body.ros .ros-num{color:#fcd34d}pre.body.ros .ros-ip{color:#5eead4}pre.body.ros .ros-mac{color:#c4b5fd}pre.body.ros .ros-good{color:#4ade80;font-weight:500}pre.body.ros .ros-bad{color:#f87171;font-weight:500}pre.body.ros .ros-bool{color:#d8b4fe}pre.body.ros .ros-dim{color:#8b8b94}pre.body.ros .ros-comment{color:#6b7280;font-style:italic}.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(--page-accent) 55%, var(--mt-border));background:color-mix(in srgb, var(--page-accent) 10%, var(--mt-surface-2))}.btn.is-active{border-color:color-mix(in srgb, var(--page-accent) 70%, var(--mt-border));color:var(--page-accent);background:color-mix(in srgb, var(--page-accent) 12%, var(--mt-surface-2))}:where(button,a,input,select,textarea,[tabindex]):focus-visible{outline:2px solid color-mix(in srgb, var(--page-accent) 70%, transparent);outline-offset:2px;border-radius:var(--mt-radius-sm)}.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)}.backup-path{align-items:center;gap:8px;max-width:min(60vw,520px);display:inline-flex}.backup-path>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.backup-path-input{font:11px var(--mt-mono);color:var(--mt-text);background:var(--mt-surface-2);border:1px solid color-mix(in srgb, var(--mt-accent) 45%, var(--mt-border));border-radius:var(--mt-radius-sm);width:min(48vw,380px);padding:5px 9px}.backup-opts{flex-wrap:wrap;align-items:center}.backup-opt{font:11px var(--mt-mono);color:var(--mt-text-dim);background:var(--mt-surface-2);border:1px solid var(--mt-border);cursor:pointer;-webkit-user-select:none;user-select:none;border-radius:100px;align-items:center;gap:6px;padding:5px 11px 5px 9px;transition:border-color .18s,color .18s,background .18s;display:inline-flex}.backup-opt input{accent-color:var(--mt-accent);cursor:pointer;margin:0}.backup-opt[data-on]{color:var(--mt-text);border-color:color-mix(in srgb, var(--mt-accent) 55%, var(--mt-border));background:color-mix(in srgb, var(--mt-accent) 12%, var(--mt-surface-2))}.backup-opt[data-disabled]{opacity:.45;cursor:not-allowed}.backup-opt[data-disabled] input{cursor:not-allowed}.linkish{font:inherit;color:var(--mt-accent);cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0}.linkish:hover{text-decoration:underline}.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}.cfgver-cp{align-items:center;gap:8px;display:inline-flex}.cfgver{gap:2px;margin:0;padding:0 0 0 4px;list-style:none;display:grid}.cfgver__row{border-radius:var(--mt-radius-sm);transition:background .18s var(--ease);grid-template-columns:18px minmax(0,1fr) auto;align-items:start;gap:10px;padding:11px 6px;display:grid;position:relative}.cfgver__row:hover{background:color-mix(in srgb, var(--mt-text) 4%, transparent)}.cfgver__row:before{content:"";background:var(--mt-border);width:1.5px;position:absolute;top:22px;bottom:-2px;left:12px}.cfgver__row:last-child:before{display:none}.cfgver__dot{border:2px solid var(--mt-border-strong);background:var(--mt-surface);z-index:1;border-radius:999px;width:11px;height:11px;margin-top:5px}.cfgver__row.is-head .cfgver__dot{border-color:var(--page-accent);background:var(--page-accent);box-shadow:0 0 0 4px color-mix(in srgb, var(--page-accent) 22%, transparent)}.cfgver__main{min-width:0}.cfgver__line{flex-wrap:wrap;align-items:center;gap:9px;font-size:12.5px;display:flex}.cfgver__kind{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.05em;border:1px solid var(--mt-border);color:var(--mt-text-dim);border-radius:999px;padding:2px 7px;font-size:10px}.cfgver__kind--checkpoint{color:var(--amber);border-color:color-mix(in srgb, var(--amber) 45%, var(--mt-border));background:color-mix(in srgb, var(--amber) 12%, transparent)}.cfgver__label{color:var(--mt-text);font-weight:600}.cfgver__time{font-family:var(--mt-mono);color:var(--mt-text-dim);font-size:11.5px}.cfgver__cur{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.05em;color:var(--page-accent);font-size:10px}.cfgver__same{color:var(--mt-text-faint);font-size:11.5px}.cfgver__drift{font-family:var(--mt-mono);align-items:baseline;gap:7px;font-size:11.5px;display:inline-flex}.cfgver__drift .add{color:var(--mt-good)}.cfgver__drift .rem{color:var(--mt-bad)}.cfgver__diff{margin-top:9px}.cfgver__diff pre.body{max-height:280px;margin:0;overflow:auto}.cfgver__actions{flex:none;align-items:center;gap:6px;display:inline-flex}.cfgver__del{color:var(--mt-text-faint);padding-left:8px;padding-right:8px}.cfgver__del:hover{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 45%, var(--mt-border))}.fguide{gap:18px;display:grid}.fguide__sechd{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.09em;color:var(--page-accent);margin:0 0 9px;font-size:11px}.fguide__list{gap:9px;display:grid}.fguide__item{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:color-mix(in srgb, var(--mt-surface-2) 45%, transparent);transition:border-color .18s, transform .12s var(--ease);padding:11px 13px}.fguide__item:hover{border-color:color-mix(in srgb, var(--page-accent) 40%, var(--mt-border));transform:translate(2px)}.fguide__top{flex-wrap:wrap;align-items:baseline;gap:10px;display:flex}.fguide__path{font-family:var(--mt-mono);color:var(--mt-text);font-size:12.5px;font-weight:500}.fguide__type{font-family:var(--mt-mono);color:var(--page-accent);background:color-mix(in srgb, var(--page-accent) 13%, transparent);border-radius:999px;padding:1px 7px;font-size:10.5px}.fguide__req{font-family:var(--mt-mono);text-transform:uppercase;letter-spacing:.05em;color:var(--mt-bad);font-size:10px}.fguide__def{color:var(--mt-text-faint);font-size:11px}.fguide__def code{color:var(--mt-text-dim)}.fguide__desc{color:var(--mt-text-dim);margin:6px 0 0;font-size:12.5px;line-height:1.5}.fguide__enum{flex-wrap:wrap;gap:6px;margin-top:7px;display:flex}.fguide__enum code{font-family:var(--mt-mono);border:1px solid var(--mt-border);color:var(--mt-text-dim);border-radius:6px;padding:1px 7px;font-size:11px}.copybtn{position:relative}.copybtn.is-copied{color:var(--page-accent);border-color:color-mix(in srgb, var(--page-accent) 55%, var(--mt-border))}.copybtn__tip{background:var(--page-accent);color:#0a0a0a;font-family:var(--mt-sans);white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .16s, transform .16s var(--ease);z-index:50;border-radius:6px;padding:3px 9px;font-size:11px;font-weight:600;position:absolute;bottom:calc(100% + 8px);left:50%;transform:translate(-50%,4px)}.copybtn__tip:after{content:"";border:4px solid #0000;border-top-color:var(--page-accent);position:absolute;top:100%;left:50%;transform:translate(-50%)}.copybtn.is-copied .copybtn__tip{opacity:1;transform:translate(-50%)}.iconbtn{border:1px solid var(--mt-border);background:var(--mt-surface);width:26px;height:26px;color:var(--mt-text-dim);cursor:pointer;border-radius:7px;place-items:center;padding:0;transition:color .16s,border-color .16s,background .16s;display:inline-grid}.iconbtn:hover{color:var(--page-accent);border-color:color-mix(in srgb, var(--page-accent) 50%, var(--mt-border));background:color-mix(in srgb, var(--page-accent) 10%, var(--mt-surface))}.copybtn--icon.is-copied{color:var(--page-accent);border-color:color-mix(in srgb, var(--page-accent) 55%, var(--mt-border))}.sheet__tool{align-items:center;gap:9px;display:inline-flex}.chart{width:100%;font-family:var(--mt-mono)}.chart .recharts-cartesian-grid line{stroke:color-mix(in srgb, var(--mt-border) 80%, transparent)}.chart .recharts-text,.chart text{fill:var(--mt-text-faint)}.chart .recharts-surface:focus,.chart svg:focus{outline:none}.chart-tip{background:color-mix(in srgb, var(--mt-surface) 96%, transparent);border:1px solid var(--mt-border-strong);border-radius:var(--mt-radius-sm);font-family:var(--mt-mono);min-width:120px;padding:8px 10px;font-size:11.5px;box-shadow:0 8px 24px #00000073}.chart-tip__label{color:var(--mt-text-dim);margin-bottom:5px;font-size:10.5px}.chart-tip__row{align-items:center;gap:7px;display:flex}.chart-tip__dot{border-radius:3px;flex:none;width:9px;height:9px}.chart-tip__name{color:var(--mt-text-dim);text-transform:capitalize}.chart-tip__val{color:var(--mt-text);font-variant-numeric:tabular-nums;margin-left:auto;font-weight:500}.chart--donut{flex-direction:column;flex:1;justify-content:center;align-items:center;gap:12px;display:flex}.chart-donut__svg{width:100%;max-width:200px}.chart-legend{font-family:var(--mt-mono);color:var(--mt-text-dim);flex-wrap:wrap;justify-content:center;gap:6px 14px;font-size:11px;display:flex}.chart-legend span{align-items:center;gap:6px;display:inline-flex}.chart-legend i{border-radius:3px;width:9px;height:9px}.chart-legend b{color:var(--mt-text);font-weight:500}.chart--spark{position:relative}.chart-spark__last{font-family:var(--mt-mono);z-index:1;font-size:10px;font-weight:600;position:absolute;top:0;right:2px}.gauge__radial{place-items:center;width:72px;height:72px;display:grid;position:relative}.gauge__pct{font-family:var(--mt-mono);pointer-events:none;place-items:center;font-size:13px;font-weight:700;display:grid;position:absolute;inset:0}.geist-btn{font-family:var(--mt-sans);border-radius:var(--mt-radius-sm);border:1px solid var(--mt-border-strong);background:var(--mt-surface);color:var(--mt-text);cursor:pointer;white-space:nowrap;transition:background .16s var(--ease), border-color .16s, color .16s, transform .1s var(--ease), box-shadow .16s;justify-content:center;align-items:center;gap:7px;font-size:13px;font-weight:500;line-height:1;display:inline-flex}.geist-btn--sm{padding:6px 11px;font-size:12px}.geist-btn--md{padding:9px 15px}.geist-btn:hover{border-color:var(--mt-text);transform:translateY(-1px)}.geist-btn:active{transform:translateY(0)}.geist-btn:disabled{opacity:.5;cursor:not-allowed;transform:none}.geist-btn__icon{display:inline-flex}.geist-btn--accent{background:var(--page-accent);border-color:var(--page-accent);color:#0a0a0a;font-weight:600}.geist-btn--accent:hover{box-shadow:0 4px 16px color-mix(in srgb, var(--page-accent) 40%, transparent);border-color:var(--page-accent)}.geist-btn--success{border-color:color-mix(in srgb, var(--mt-good) 55%, var(--mt-border));color:var(--mt-good)}.geist-btn--warning{border-color:color-mix(in srgb, var(--mt-warn) 55%, var(--mt-border));color:var(--mt-warn)}.geist-btn--error{border-color:color-mix(in srgb, var(--mt-bad) 55%, var(--mt-border));color:var(--mt-bad)}.geist-btn--error:hover{background:color-mix(in srgb, var(--mt-bad) 14%, var(--mt-surface));border-color:var(--mt-bad)}.geist-btn--secondary{color:var(--mt-text-dim);border-color:var(--mt-border)}.geist-btn--ghost{background:0 0;border-color:#0000}.geist-btn--ghost:hover{background:color-mix(in srgb, var(--mt-text) 7%, transparent);border-color:var(--mt-border)}.geist-note{border-radius:var(--mt-radius-sm);border:1px solid var(--mt-border);background:var(--mt-surface);color:var(--mt-text-dim);padding:11px 14px;font-size:13px;line-height:1.55}.geist-note__label{color:var(--mt-text);text-transform:capitalize}.geist-note--secondary{border-color:color-mix(in srgb, var(--page-accent) 30%, var(--mt-border));background:color-mix(in srgb, var(--page-accent) 7%, var(--mt-surface))}.geist-note--success{border-color:color-mix(in srgb, var(--mt-good) 40%, var(--mt-border));background:color-mix(in srgb, var(--mt-good) 8%, var(--mt-surface))}.geist-note--success .geist-note__label{color:var(--mt-good)}.geist-note--warning{border-color:color-mix(in srgb, var(--mt-warn) 40%, var(--mt-border));background:color-mix(in srgb, var(--mt-warn) 8%, var(--mt-surface))}.geist-note--warning .geist-note__label{color:var(--mt-warn)}.geist-note--error{border-color:color-mix(in srgb, var(--mt-bad) 40%, var(--mt-border));background:color-mix(in srgb, var(--mt-bad) 8%, var(--mt-surface))}.geist-note--error .geist-note__label{color:var(--mt-bad)}.geist-badge{font-family:var(--mt-mono);border:1px solid var(--mt-border);background:var(--mt-surface-2);color:var(--mt-text-dim);border-radius:999px;align-items:center;gap:5px;padding:2px 8px;font-size:10.5px;font-weight:500;display:inline-flex}.geist-badge--accent{color:var(--page-accent);border-color:color-mix(in srgb, var(--page-accent) 40%, var(--mt-border));background:color-mix(in srgb, var(--page-accent) 12%, transparent)}.geist-badge--success{color:var(--mt-good);border-color:color-mix(in srgb, var(--mt-good) 40%, var(--mt-border));background:color-mix(in srgb, var(--mt-good) 10%, transparent)}.geist-badge--warning{color:var(--mt-warn);border-color:color-mix(in srgb, var(--mt-warn) 40%, var(--mt-border))}.geist-badge--error{color:var(--mt-bad);border-color:color-mix(in srgb, var(--mt-bad) 40%, var(--mt-border));background:color-mix(in srgb, var(--mt-bad) 10%, transparent)}.geist-dot{background:var(--mt-text-faint);border-radius:999px;flex:none;width:9px;height:9px;display:inline-block}.geist-dot--success{background:var(--mt-good)}.geist-dot--warning{background:var(--mt-warn)}.geist-dot--error{background:var(--mt-bad)}.geist-dot--accent{background:var(--page-accent)}.geist-dot.is-pulse{box-shadow:0 0 0 0 color-mix(in srgb, currentColor 60%, transparent);animation:1.8s infinite geist-pulse}@keyframes geist-pulse{0%{box-shadow:0 0 0 0 color-mix(in srgb, var(--mt-good) 55%, transparent)}70%{box-shadow:0 0 0 7px #0000}to{box-shadow:0 0 #0000}}.geist-spinner{width:16px;height:16px;display:inline-block;position:relative}.geist-spinner--sm{width:13px;height:13px}.geist-spinner span{transform-origin:50% 178%;opacity:.15;background:currentColor;border-radius:2px;width:8%;height:28%;animation:1.2s linear infinite geist-spin;position:absolute;top:0;left:46%}@keyframes geist-spin{0%{opacity:1}to{opacity:.15}}.geist-input,.geist-select{font-family:var(--mt-mono);color:var(--mt-text);background:var(--mt-surface);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);padding:8px 11px;font-size:12.5px;transition:border-color .16s,box-shadow .16s}.geist-input:focus,.geist-select:focus{border-color:var(--page-accent);box-shadow:0 0 0 3px color-mix(in srgb, var(--page-accent) 22%, transparent);outline:none}.geist-card{border:1px solid var(--mt-border);border-radius:var(--mt-radius);background:var(--mt-surface);padding:16px}.geist-card.is-hoverable{transition:border-color .18s, transform .18s var(--ease)}.geist-card.is-hoverable:hover{border-color:color-mix(in srgb, var(--page-accent) 40%, var(--mt-border));transform:translateY(-2px)}.geist-tooltip{display:inline-flex;position:relative}.geist-tooltip__pop{border-radius:var(--mt-radius-sm);background:var(--mt-surface);border:1px solid var(--mt-border-strong);color:var(--mt-text);font-family:var(--mt-mono);white-space:nowrap;opacity:0;pointer-events:none;transition:opacity .16s, transform .16s var(--ease);z-index:60;padding:6px 10px;font-size:11.5px;position:absolute;bottom:calc(100% + 8px);left:50%;transform:translate(-50%,4px);box-shadow:0 8px 24px #00000073}.geist-tooltip:hover .geist-tooltip__pop,.geist-tooltip:focus-visible .geist-tooltip__pop{opacity:1;transform:translate(-50%)}.clients-toolbar{align-items:center;gap:8px;display:flex}.clients-toolbar .geist-input{width:200px}.clients-counts{margin-bottom:10px;font-size:12px}.clients-error{margin-bottom:10px}.clients-table{flex-direction:column;gap:2px;display:flex}.clients-row{border-radius:var(--mt-radius-sm);cursor:pointer;grid-template-columns:120px 1.3fr 150px 80px 130px minmax(280px,auto);align-items:center;gap:10px;padding:8px 10px;font-size:13px;display:grid}.clients-row--head{cursor:default;color:var(--mt-text-faint);text-transform:uppercase;letter-spacing:.04em;font-size:11px}.clients-row:not(.clients-row--head):hover{background:var(--mt-surface-2)}.clients-row.is-selected{background:color-mix(in srgb, var(--page-accent) 14%, var(--mt-surface-2))}.clients-row.is-blocked .clients-ip,.clients-row.is-blocked .clients-name{color:var(--mt-text-faint);text-decoration:line-through}.clients-ip,.clients-name{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.clients-mac{color:var(--mt-text-dim);font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:11.5px}.clients-badges{align-items:center;gap:6px;display:flex;overflow:hidden}.clients-actions,.clients-actions-h{justify-content:flex-end;gap:6px;display:flex}.clients-detail{border-top:1px solid var(--mt-border);margin-top:14px;padding-top:14px}.clients-detail__hd{flex-wrap:wrap;align-items:baseline;gap:12px;margin-bottom:10px;display:flex}.clients-detail__title{font-size:14px;font-weight:600}.clients-rates{font-variant-numeric:tabular-nums;gap:18px;margin-bottom:8px;display:flex}.clients-rates .rate{font-weight:600}.clients-rates .rate.rx{color:var(--lime)}.clients-rates .rate.tx{color:var(--amber)}.clients-chart{background:var(--mt-surface);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);width:100%;max-width:520px;height:150px;display:block}.clients-chart .line{fill:none;stroke-width:2px}.clients-chart .rx.line{stroke:var(--lime)}.clients-chart .tx.line{stroke:var(--amber)}.clients-chart .rx.area{fill:color-mix(in srgb, var(--lime) 18%, transparent);stroke:none}.clients-chart .tx.area{fill:color-mix(in srgb, var(--amber) 14%, transparent);stroke:none}.clients-totals{font-variant-numeric:tabular-nums;margin-top:8px;font-size:12px}.clients-edit{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:var(--mt-surface-2);flex-wrap:wrap;align-items:center;gap:8px;margin-top:14px;padding:10px 12px;display:flex}.clients-edit__label{color:var(--mt-text-dim);font-size:13px}.clients-edit .geist-input{width:220px}.aaa-tabs{border-bottom:1px solid var(--mt-border);flex-wrap:wrap;gap:4px;margin-bottom:14px;display:flex}.aaa-tab{appearance:none;color:var(--mt-text-dim);font:inherit;cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;margin-bottom:-1px;padding:7px 12px;font-size:13px}.aaa-tab:hover{color:var(--mt-text)}.aaa-tab.is-active{color:var(--mt-text);border-bottom-color:var(--page-accent)}.aaa-entity__toolbar{align-items:center;gap:10px;margin-bottom:12px;display:flex}.aaa-filter{width:200px}.aaa-error{margin-bottom:10px}.aaa-empty{padding:20px 4px}.aaa-table{flex-direction:column;gap:2px;display:flex}.aaa-table--scroll{overflow-x:auto}.aaa-row{border-radius:var(--mt-radius-sm);align-items:center;gap:10px;padding:8px 10px;font-size:13px;display:grid}.aaa-row--head{color:var(--mt-text-faint);text-transform:uppercase;letter-spacing:.04em;font-size:11px}.aaa-row:not(.aaa-row--head):hover{background:var(--mt-surface-2)}.aaa-row.is-off .aaa-cell{color:var(--mt-text-faint)}.aaa-row--sessions{grid-template-columns:repeat(9,minmax(90px,1fr));min-width:900px}.aaa-cell{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.aaa-actions,.aaa-actions-h{justify-content:flex-end;gap:6px;display:flex}.aaa-form,.aaa-singleton{border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);background:var(--mt-surface-2);margin-bottom:14px;padding:14px}.aaa-form__title,.aaa-singleton__title{margin-bottom:10px;font-size:13px;font-weight:600}.aaa-singleton__current{margin-bottom:12px;font-size:12px}.aaa-form__grid{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:10px 14px;display:grid}.aaa-field{flex-direction:column;gap:4px;display:flex}.aaa-field__label{color:var(--mt-text-dim);font-size:11.5px}.aaa-form__actions{align-items:center;gap:10px;margin-top:14px;display:flex}.aaa-settings{flex-direction:column;gap:8px;display:flex}.clients-limits{border-top:1px solid var(--mt-border);max-width:520px;margin-top:14px;padding-top:12px}.clients-limits__hd{align-items:baseline;gap:12px;margin-bottom:8px;display:flex}.clients-limits__label{font-size:13px;font-weight:600}.clients-limits__row{flex-wrap:wrap;align-items:flex-end;gap:8px;display:flex}.clients-limits__field{flex-direction:column;gap:3px;font-size:11.5px;display:flex}.clients-limits__field .geist-input{width:150px}.clients-limits__msg{margin-top:6px;font-size:12px}.usage-chart__empty{padding:16px 4px;font-size:12.5px}.usage-chart__legend{font-variant-numeric:tabular-nums;align-items:baseline;gap:14px;margin-bottom:6px;font-size:12px;display:flex}.usage-chart__legend .rate{font-weight:600}.usage-chart__legend .rate.rx{color:var(--lime)}.usage-chart__legend .rate.tx{color:var(--amber)}.usage-chart__svg{background:var(--mt-surface);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);width:100%;height:160px;display:block}.usage-chart__svg .line{fill:none;stroke-width:2px;vector-effect:non-scaling-stroke}.usage-chart__svg .rx.line{stroke:var(--lime)}.usage-chart__svg .tx.line{stroke:var(--amber)}.usage-chart__svg .rx.area{fill:color-mix(in srgb, var(--lime) 18%, transparent);stroke:none}.usage-chart__svg .tx.area{fill:color-mix(in srgb, var(--amber) 14%, transparent);stroke:none}.clients-history,.aaa-usage__section{margin-top:16px}.clients-history__hd,.aaa-usage__title{color:var(--mt-text-dim);margin-bottom:8px;font-size:12px;font-weight:600}.aaa-usage__bar{align-items:center;gap:8px;margin-bottom:12px;display:flex}.heatmap__hd{align-items:baseline;gap:10px;margin-bottom:6px;display:flex}.heatmap__title{font-size:12.5px;font-weight:600}.heatmap__scroll{padding-bottom:4px;overflow-x:auto}.heatmap__svg{display:block}.heatmap__month,.heatmap__wd{fill:var(--mt-text-dim);font-family:inherit;font-size:9px}.heatmap{position:relative}.heatmap__cell{stroke:color-mix(in srgb, var(--mt-border) 60%, transparent);stroke-width:1px;cursor:pointer;transition:stroke .1s}.heatmap__cell.is-hover{stroke:var(--mt-text);stroke-width:1.5px}.heatmap__tip{z-index:30;background:linear-gradient(180deg, color-mix(in srgb, var(--mt-surface-2) 92%, var(--lime)), var(--mt-surface-2));border:1px solid color-mix(in srgb, var(--lime) 35%, var(--mt-border));pointer-events:none;min-width:150px;animation:heatmap-tip-in .12s var(--ease);border-radius:10px;padding:8px 10px;position:absolute;transform:translate(-50%,calc(-100% - 9px));box-shadow:0 8px 24px #00000073}.heatmap__tip:after{content:"";background:var(--mt-surface-2);border-right:1px solid color-mix(in srgb, var(--lime) 35%, var(--mt-border));border-bottom:1px solid color-mix(in srgb, var(--lime) 35%, var(--mt-border));width:11px;height:11px;position:absolute;bottom:-6px;left:50%;transform:translate(-50%)rotate(45deg)}@keyframes heatmap-tip-in{0%{opacity:0;transform:translate(-50%,calc(-100% - 3px))}to{opacity:1;transform:translate(-50%,calc(-100% - 9px))}}.heatmap__tip-head{align-items:center;gap:7px;font-size:13px;display:flex}.heatmap__tip-head b{font-weight:600}.heatmap__tip-head .heatmap__swatch{width:12px;height:12px}.heatmap__tip-date{color:var(--mt-text-dim);font-variant-numeric:tabular-nums;margin-top:3px;font-size:11.5px}.heatmap__cell.lvl0,.heatmap__swatch.lvl0{fill:var(--mt-surface-2);background:var(--mt-surface-2)}.heatmap__cell.lvl1,.heatmap__swatch.lvl1{fill:color-mix(in srgb, var(--lime) 30%, var(--mt-surface-2));background:color-mix(in srgb, var(--lime) 30%, var(--mt-surface-2))}.heatmap__cell.lvl2,.heatmap__swatch.lvl2{fill:color-mix(in srgb, var(--lime) 50%, var(--mt-surface-2));background:color-mix(in srgb, var(--lime) 50%, var(--mt-surface-2))}.heatmap__cell.lvl3,.heatmap__swatch.lvl3{fill:color-mix(in srgb, var(--lime) 72%, transparent);background:color-mix(in srgb, var(--lime) 72%, transparent)}.heatmap__cell.lvl4,.heatmap__swatch.lvl4{fill:var(--lime);background:var(--lime)}.heatmap__legend{align-items:center;gap:4px;margin-top:8px;font-size:11px;display:flex}.heatmap__swatch{border-radius:2px;width:11px;height:11px;display:inline-block}.input{font:12px var(--mt-mono);color:var(--mt-text);background:var(--mt-surface-2);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);width:min(52vw,420px);padding:6px 10px}.input:focus{border-color:color-mix(in srgb, var(--mt-accent) 55%, var(--mt-border));outline:none}.btn-sm{padding:3px 8px;font-size:10px}.mod-toolbar{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:6px;display:flex}.mod-groups{flex-direction:column;gap:18px;margin-top:12px;display:flex}.mod-group__hd{border-bottom:1px solid var(--mt-border);align-items:center;gap:10px;margin-bottom:8px;padding-bottom:6px;display:flex}.mod-group__title{color:var(--mt-text);margin:0;font-size:13px;font-weight:600}.mod-list{grid-template-columns:repeat(auto-fill,minmax(min(100%,360px),1fr));gap:8px;display:grid}.mod-row{background:var(--mt-surface-2);border:1px solid var(--mt-border);border-radius:var(--mt-radius-sm);cursor:pointer;-webkit-user-select:none;user-select:none;align-items:flex-start;gap:10px;padding:9px 11px;transition:border-color .18s,background .18s;display:flex}.mod-row:hover{border-color:color-mix(in srgb, var(--mt-accent) 35%, var(--mt-border))}.mod-row[data-on]{border-color:color-mix(in srgb, var(--mt-accent) 50%, var(--mt-border));background:color-mix(in srgb, var(--mt-accent) 10%, var(--mt-surface-2))}.mod-row input{accent-color:var(--mt-accent);cursor:pointer;margin:2px 0 0}.mod-row__main{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.mod-row__name{color:var(--mt-text);flex-wrap:wrap;align-items:center;gap:7px;font-size:12px;font-weight:600;display:flex}.mod-row__slug{font:10px var(--mt-mono);color:var(--mt-text-dim);background:var(--mt-bg);border:1px solid var(--mt-border);border-radius:4px;padding:1px 5px}.mod-row__desc{font-size:11px;line-height:1.35}.mod-row__count{font:10px var(--mt-mono);white-space:nowrap;padding-top:1px}.conn-tunnel-halo{fill:none;stroke-width:8px;stroke-linecap:round;opacity:.1}.conn-tunnel{fill:none;stroke-width:2.4px;stroke-linecap:round;stroke-dasharray:6 6;animation:.9s linear infinite conn-tunnel-flow}@keyframes conn-tunnel-flow{to{stroke-dashoffset:-24px}}.conn-tunnel-lock{filter:drop-shadow(0 0 4px #f59e0bcc)}.conn-tunnel-sat{stroke-width:1.5px;filter:drop-shadow(0 0 5px #f59e0b88)}.conn-tunnel-badge{fill:#30220a;stroke:color-mix(in srgb, #f59e0b 60%, var(--mt-border));stroke-width:1px}.conn-tunnel-badge-tx{fill:#fbbf24;font-family:var(--mt-mono);letter-spacing:.02em;font-weight:700}.jump-route{background:color-mix(in srgb, #f59e0b 7%, var(--mt-surface-2));border:1px solid color-mix(in srgb, #f59e0b 28%, var(--mt-border));font:10px var(--mt-mono);border-radius:10px;align-items:center;gap:6px;margin:10px 0 2px;padding:7px 9px;display:flex;overflow:hidden}.jump-route__hop{background:var(--mt-bg);border:1px solid var(--mt-border);color:var(--mt-text);white-space:nowrap;border-radius:7px;align-items:center;gap:5px;padding:3px 8px;display:inline-flex}.jump-route__hop--src{padding:3px 7px;font-size:12px}.jump-route__hop--bastion{border-color:color-mix(in srgb, #f59e0b 60%, var(--mt-border));color:#fbbf24;box-shadow:0 0 10px -3px #f59e0b99}.jump-route__tag{text-transform:uppercase;letter-spacing:.08em;color:#0a0a0a;background:#f59e0b;border-radius:4px;padding:1px 4px;font-size:7px;font-style:normal}.jump-route__wire{background-image:linear-gradient(90deg, var(--mt-border) 0 50%, transparent 50% 100%);background-size:8px 2px;border-radius:2px;flex:1;min-width:18px;height:2px;animation:.7s linear infinite jump-wire}.jump-route__wire--enc{background-image:linear-gradient(90deg,#f59e0b 0 55%,#0000 55% 100%);background-size:10px 3px;min-width:34px;height:3px;position:relative;box-shadow:0 0 8px -1px #f59e0b88}.jump-route__lock{filter:drop-shadow(0 0 3px #f59e0b);font-size:11px;animation:2.2s linear infinite jump-lock;position:absolute;top:50%}@keyframes jump-wire{to{background-position:8px 0}}@keyframes jump-lock{0%{left:-4px;transform:translateY(-50%)scale(.9)}50%{transform:translateY(-50%)scale(1.1)}to{left:calc(100% - 8px);transform:translateY(-50%)scale(.9)}}@media (prefers-reduced-motion:reduce){.conn-tunnel,.jump-route__wire,.jump-route__wire--enc,.jump-route__lock{animation:none}.conn-tunnel-lock animatemotion{display:none}}
|
|
85
85
|
/*$vite$:1*/
|
|
86
86
|
</style>
|
|
87
87
|
</head>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usex/mikrotik-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.34.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",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
},
|
|
83
83
|
"devDependencies": {
|
|
84
84
|
"@resvg/resvg-js": "^2.6.2",
|
|
85
|
-
"@tailwindcss/vite": "^4.3.
|
|
85
|
+
"@tailwindcss/vite": "^4.3.2",
|
|
86
86
|
"@types/bun": "latest",
|
|
87
87
|
"@types/node": "^26.0.1",
|
|
88
88
|
"@types/react": "^19.2.17",
|
|
@@ -93,13 +93,13 @@
|
|
|
93
93
|
"class-variance-authority": "^0.7.1",
|
|
94
94
|
"clsx": "^2.1.1",
|
|
95
95
|
"gsap": "^3.15.0",
|
|
96
|
-
"prettier": "^3.9.
|
|
96
|
+
"prettier": "^3.9.3",
|
|
97
97
|
"react": "^19.2.7",
|
|
98
98
|
"react-dom": "^19.2.7",
|
|
99
99
|
"recharts": "^3.9.0",
|
|
100
100
|
"release-it": "^20.2.1",
|
|
101
101
|
"tailwind-merge": "^3.6.0",
|
|
102
|
-
"tailwindcss": "^4.3.
|
|
102
|
+
"tailwindcss": "^4.3.2",
|
|
103
103
|
"vite": "npm:@voidzero-dev/vite-plus-core@latest",
|
|
104
104
|
"vite-plus": "latest"
|
|
105
105
|
},
|