What This Tool Is
A real-time operations console for ConnectWise Automate — surfaces agent health, patch compliance, active alerts, and API latency in a single fixed-layout dashboard built for MSP NOC use.
The ConnectWise Automate Ops Console is a single-page HTML dashboard that connects to the ConnectWise Automate REST API and presents a unified view of the managed endpoint environment. It is purpose-built for MSP NOC technicians, dispatch coordinators, engineers, and on-call staff who need situational awareness across all monitored clients without navigating the native Automate interface.
The design is read-first with action capability — the full environment state is visible at a glance, and operators can act immediately via remote commands, script execution, alert acknowledgment, and ticket creation, all without leaving the console. Every metric card links to the API call that produced it.
This tool is part of the KrawTech Stack of purpose-built operations dashboards alongside the SPX Console, ROC Leaders Board, and IVR Triage Console. In production it fetches live data through a configured proxy. Until that proxy is active it runs in demo mode using deterministic mock data so all interface and interaction patterns can be fully validated before go-live.
NOC Technicians — Primary users. Monitor live agent status, triage incoming alerts, and execute remote commands without leaving the console. The alert feed and agent drawer are the main touch points during incident response.
Dispatch / Coordinators — Use the client breakdown and agent table to answer status questions instantly — "how many machines at Cascade Health are offline right now?" — without needing an Automate login.
Engineers — Script runner and API endpoints panel expose the underlying REST calls for testing, latency monitoring, and batch operations. Engineers can queue patch runs, verify API health, and test script targeting from a single pane.
Management — Six-metric KPI strip provides a summary view suitable for wall-board display or a morning briefing. No system access required to interpret the numbers.
| Capability | Current Status | Notes |
|---|---|---|
| Live agent inventory — search, filter, sort | Demo-ready | Activates on proxy go-live |
| Six KPI cards with 12-point sparkline history | Demo-ready | Activates on proxy go-live |
| API endpoint health monitoring + latency | Demo-ready | Activates on proxy go-live |
| Patch compliance donut with state breakdown | Demo-ready | Activates on proxy go-live |
| Active alert feed — Ack / Ticket / Dismiss | Demo-ready | Ticket creation needs separate integration |
| 24-hour agent check-in histogram | Demo-ready | Activates on proxy go-live |
| Remote commands (ping, sysinfo, reboot, isolate) | Simulated | Requires proxy + agent command permissions |
| Script runner with scope targeting | Simulated | Requires proxy + script execution rights |
| ConnectWise Control handoff | Link only | Requires Control URL config after proxy go-live |
| Ticket creation from alert | Simulated | Requires CW Manage API or Automate Service module |
| 30-second auto-refresh + countdown timer | Active | Works in demo and live mode |
| Simulate Agent Event / Outage (demo tools) | Active | Demo-only — removed or hidden in production |
Integration Status
Current state of each integration layer. Live data flows end-to-end only after a valid proxy with injected authentication is wired to apiFetch().
apiFetch() needs a real URL and Bearer token injection at the proxy.
| Layer | Status | What Is Needed |
|---|---|---|
| Dashboard HTML / JS | ✓ Ready | No changes required. All render logic, API path references, error handling, and UI states are fully implemented. |
| Proxy / Middleware | Not configured | Needs a reverse proxy or Express middleware that injects a valid Automate Bearer token and forwards requests to the Automate server URL. |
| Automate REST API | Not reachable | REST API must be enabled on the Automate server (default HTTPS). API user requires Computer (read), Alert (read/write), Script (read/execute), Patch (read), Client (read). |
| Auth / Token | Not configured | Bearer token obtained via POST /automate/api/v1/apitoken. Expires every 24 hours. Proxy must handle refresh. |
| Remote Commands | Not active | Activates when proxy is live AND the API user has Command execution permissions on target agents. |
| Script Execution | Not active | Activates when proxy is live AND the API user has Script execute rights. |
| Ticket Creation | Not active | Requires CW Manage API integration or Automate's built-in Service module with its own auth scope. |
| Function | What It Produces | Replaces |
|---|---|---|
| buildAgents() | 70–112 agents across 7 fixed clients with randomized OS, status, patch state, resource metrics, and timeline events | GET /automate/api/v1/computers |
| buildAgentTimeline() | 3–5 ordered events per agent (offline event, alert trigger, patch notice, script run, check-in) | Agent event history |
| mApiHealth() | 6 endpoint health records with simulated latency (22–260ms) and computed status thresholds | GET /automate/api/v1/health |
| mAlerts(agents) | Up to 7 active alerts, filtered from agents where alerts > 0, further filtered by ackedAlerts Set | GET /automate/api/v1/alerts |
| mPatches(agents) | current / pending / critical / outdated counts by iterating a.patch across all agents | GET /automate/api/v1/patches |
| mActivity() | 24-element array — hours 8–17 use base 180–480, off-hours use 10–80, all with ±30 jitter | Agent check-in event log |
| mMonitors() | 6 hard-coded monitor rules with randomized trigger counts each refresh | GET /automate/api/v1/monitors |
| mClients(agents) | Client roster with agent counts, sorted descending by count | GET /automate/api/v1/clients |
Architecture
A single self-contained HTML file. No build step, no npm dependencies, no server-side rendering. All logic runs in the browser. A proxy layer is the only external dependency needed for live data.
Single-file, zero deps
CORS bridging
⚠ NOT CONFIGURED
Bearer auth, HTTPS
SQL + agent database
refresh() is called on page load and every 30 seconds via setInterval. A parallel 1-second interval ticks the countdown label. The dot pulses green at rest, turns amber during the fetch window, returns to green on completion.
apiFetch(url, mock) wraps every call in a 5-second AbortSignal.timeout. On any error or non-200 response it silently returns mock(), so the dashboard never renders an empty state.
| Variable | Type | Purpose & Scope |
|---|---|---|
| agents | Array<Object> | Master agent roster. Built by buildAgents() on first refresh, mutated by simulate functions and the random status-mutation pass in refresh(). Persists across refresh cycles. |
| agentFilter | string | Active filter chip value: 'all' | 'online' | 'offline' | 'alert' | 'patch'. Drives renderAgents(). |
| agentSort | { field, dir } | Active sort column name and direction (1 = asc, −1 = desc). Defaults to { field:'_idx', dir:1 } (insertion order). Persists across refreshes. |
| selectedAgent | number | null | ID of the agent whose row is highlighted and whose drawer is open. Set by openAgentDrawer(id), cleared by closeDrawer(). |
| ackedAlerts | Set<string> | Set of acknowledged alert IDs (format ALT-NNNN). Items in this Set are filtered out of the alert list on every render. Not persisted across page loads. |
| hist | Object of arrays | Rolling 12-point history per KPI metric: total · online · offline · alerts · patches · scripts. Each entry appended on every rKPI() call; oldest dropped when length exceeds 12. Drives all sparkline SVGs. |
| execHistory | Array<Object> | Last 8 script execution records. Each has { t, name, ok }. Prepended by logExec(); sliced to 8 max. Rendered into #execLog. |
Header Bar
44px fixed bar across the top of the dashboard (below the demo notice). Contains the product identity, demo simulation controls, and the live refresh indicator.
| Element | ID / Class | Behavior |
|---|---|---|
| Logo mark | .logo-mark | Text "CW" — Orbitron, cyan with glow shadow. Purely decorative. |
| Title | .htitle | "Automate / Ops Console" — Orbitron, muted subtitle. Static. |
| + Agent Event button | .hdr-btn.primary | Calls simulateAgent(). Picks a random offline agent, sets it to online, flashes the row with row-new animation, toasts success. Demo only. |
| ⚡ Simulate Outage button | .hdr-btn.danger | Calls simulateOutage(). Picks a random client, sets all its agents to offline, re-renders table and KPIs. Toasts warning. Demo only. |
| Refresh dot | #rdot | Pulsing green (pd animation) at rest. Solid amber during fetch. Click triggers refresh() immediately. |
| Refresh label | #rlbl | Shows "Live" at rest, "fetching…" during poll, countdown ("29s"…"1s") between polls. |
| Timestamp | #rtime | Shows "@ HH:MM:SS" of the last completed refresh. Updated at end of each refresh() call. |
A 36px fixed bar pinned above the main header (top: 0, z-index: 999999). Contains a HOME link (→ command.html) and a centered amber notice: "⚠ DEMO VERSION — DATA IS NOT LIVE". The body has padding-top: 36px to compensate. Remove or relabel this bar once live data is confirmed.
KPI Strip
Six equal-width metric cards spanning the full dashboard width. Each shows a current value, sparkline trend, delta label, and the backing API endpoint on hover. Clicking any card opens the detail drawer.
| Element | CSS | Behavior |
|---|---|---|
| Left accent bar | border-left: 3px solid var(--ka) | Color-coded per metric via --ka CSS variable set inline on each card. |
| Value | .kpi-val — Orbitron 18px bold | Updated on every rKPI() call. |
| Sparkline SVG | 52×20px inline SVG | Polygon area fill + polyline. Drawn from last 12 values in hist[metric]. |
| Label | .kpi-lbl — Rajdhani 11px | Static uppercase descriptor. |
| Delta | .kpi-d.up / .dn / .neu | Green for positive, red for negative, muted for neutral. Class toggled by rKPI() logic. |
| Endpoint tooltip | .kpi-endpoint — opacity 0→1 on hover | Shows the exact API path backing this metric. Fades in on hover. |
| Click handler | onclick="showKpiDrawer(type)" | Opens the detail drawer with API call block, full-width trend sparkline, and sample JSON response. |
| Section | Content |
|---|---|
| API Call | The exact GET path with a simulated response time (20–120ms random). |
| Trend — last 12 polls | Full-width (360px viewBox) SVG sparkline of hist[type]. Color matches the KPI accent. |
| Sample Response | Formatted JSON block showing real field names from the Automate API response shape for that endpoint. Values use live agents state for accuracy. |
API Endpoints Panel
Left column, top slot. Monitors the health and response latency of the six core Automate REST endpoints. Polled every 30 seconds alongside all other data.
rApi(list) from mApiHealth(). Each row: endpoint name (mono), latency in ms, status pill. Rows are clickable and open the API drawer. The panel header shows "polling…" until the first render completes.| Pill | Range | CSS Class | Meaning |
|---|---|---|---|
| ok | < 80ms | .p-ok | Endpoint responding normally |
| warn | 80–159ms | .p-warn | Elevated latency — monitor |
| deg | 160–279ms | .p-deg | Degraded — investigate |
| dn | ≥ 280ms or error | .p-dn | Down or unresponsive |
| Section | Content |
|---|---|
| Endpoint Health | Current latency in color-coded mono, P50 / P95 / P99 estimates (×0.7 / ×1.5 / ×2 of current ms), status pill. |
| Last 10 Requests | 10-bar mini histogram. Each bar height = min(100, v/400 × 100)%. Bar color follows the same thresholds (green/amber/orange/red). |
| Auth | Formatted code block showing the full POST /apitoken call, request body shape, and the "→ 200 Bearer token (expires 24h)" response note. |
Patch Compliance Panel
Left column, middle slot. SVG donut chart showing the distribution of patch states across all agents. Header shows overall compliance percentage.
rPatch(p) from mPatches(agents). 72×72px SVG donut (r=28, stroke-width=10, four arcs drawn by drawDonut(segs)). Right-side legend: four rows with color dot, label, count. Header: "N% compliant".| State | Color Token | CSS Badge Class | Meaning | Action |
|---|---|---|---|---|
| Current | --green | .pb-ok | All patches applied | None required |
| Pending | --hi (orange) | .pb-pend | Approved, not yet installed | Schedule maintenance window |
| Critical | --red | .pb-crit | Security-critical patches outstanding | Immediate remediation |
| Outdated | --amber | .pb-warn | Data stale or scan not run | Trigger patch scan via Script Runner |
Compliance % formula: (current / total) × 100, rounded to nearest integer. Displayed in both the panel header and the donut center text.
Clients Panel
Left column, bottom slot. Horizontal bar chart showing agent count per managed client, sorted descending. Bar width is proportional to the maximum client count.
rClients(list) from mClients(agents). Each row: fixed 70px label (truncated), proportional cyan fill bar, numeric count. Bar width = (count / max) × 100% with a 0.6s CSS transition.Seven clients defined in the CLIENTS constant. Each receives 5–15 randomly assigned agents on every buildAgents() call. In production, this list and counts come from GET /clients.
Agent Inventory
Center panel — the primary operational table. All managed agents in a sortable, filterable, searchable table. Clicking any row opens the agent detail drawer.
renderAgents(). Applies agentFilter, search query from #agentSearch, and agentSort before writing rows to #agentTbody. Full tbody replacement on every call. Agent count badge updates to the filtered row count.| Column | Field | Sortable | Render Detail |
|---|---|---|---|
| Agent | a.name | Yes | Bold white. Format: {client_prefix}-{ws|srv|laptop|desktop|vm}-{10–99} |
| Client | a.client | Yes | Rajdhani body font, muted color class .tsmall2 |
| OS | a.os | No | Abbreviated: "Win 11", "Win 10", "Mac", "Ub. 22.04". Class .tsmall (mono) |
| Status | a.status | Yes | Colored status dot + text. online=green/glow, alert=amber/glow, offline=red. |
| Patch | a.patch | No | Colored .patch-badge. current=green, pending=orange, critical=red, outdated=amber. |
| Last Seen | a.lastSeen | Yes | Human string: online="0–4m ago", offline="1–71h ago". Class .tsmall. |
| Chip | data-f | Filter Condition | Active Style |
|---|---|---|---|
| All | all | No filter applied | Cyan border + text |
| Online | online | a.status !== 'offline' — includes alert state | Green border + text |
| Offline | offline | a.status === 'offline' | Red border + text |
| Alert | alert | a.status === 'alert' only | Amber border + text |
| Patch | patch | a.patch !== 'current' — any non-current patch state | Orange border + text |
Input #agentSearch, placeholder "search agents, clients, OS…". Fires renderAgents() on every keystroke via oninput. Case-insensitive substring match across a.name, a.client, and a.os simultaneously. No debounce in the current implementation — see Limitations §17.
| Field | Type | Demo Range / Values |
|---|---|---|
| _idx / id | number | Sequential starting at 1001. _idx used as default sort key. |
| name | string | {client.split(' ')[0].toLowerCase()}-{type}-{10–98} |
| client | string | One of 7 CLIENTS values |
| os | string | Win 11 / Win 10 / Server 2022 / Server 2019 / macOS 14 / Ubuntu 22.04 |
| status | string | 'online' | 'alert' | 'offline'. 82% chance online; 22% of online become alert. |
| patch | string | 'current' | 'pending' | 'critical' | 'outdated'. Uniform random. |
| lastSeen | string | Online: "0–4m ago". Offline: "1–71h ago". |
| lastSeenMin | number | Integer minutes. Online: 0–4. Offline: 60–4319. Used for future sort support. |
| ip | string | 192.168.{1–9}.{10–253} |
| uptime | string | Online: "{1–89}d {0–22}h". Offline: "—" |
| cpu / ram | number | Online: random in realistic range. Offline: 0. |
| disk | number | 25–94 (always set, even offline) |
| agentVer | string | "23.{1–11}.{100–998}" |
| location | string | Site A / Site B / HQ / Remote / Branch |
| scripts | number | 0–11 (unused in current render) |
| alerts | number | 0 for non-alert agents. 1–3 for agents with alert status. |
| timeline | Array | 3–5 events from buildAgentTimeline(online, hasAlert, patch) |
Agent Check-in Chart
Bottom sub-panel inside the center column, below the agent table. 24-bar histogram of check-in volume by hour. Business-hours peaks are highlighted.
rActivity(data) from mActivity(). 24 div bars in a flex row, height proportional to volume percentage of max. Hover shows a tooltip with hour and count. X-axis labels: 00:00 · 06:00 · 12:00 · 18:00 · now. Header shows peak hour and count.| Condition | Background |
|---|---|
v > max × 0.7 | rgba(0,212,255,.35) — dim cyan highlight |
v ≤ max × 0.7 | var(--border2) — white-alpha 14% |
| Hover (any bar) | var(--cyan) — full cyan accent |
Tooltip class .act-tip is absolutely positioned above the hovered bar, shown via display:none → block on parent hover. Content: "{i}:00 — {v} check-ins".
Script Runner
Right column, top slot. Select a script and a target scope, then execute or schedule. Logs the last 8 executions in an inline history below the controls.
POST /automate/api/v1/scripts/{id}/run.
<select> dropdowns (script, target scope), two action buttons (Run, Schedule), and a scrollable execution log (#execLog, max-height 110px). Also accessible from the agent drawer via the "Run Script" footer button, which pre-fills the target with the selected agent.| Value | Script Name | Typical Use |
|---|---|---|
| 1 | Disk Cleanup | Free disk space on Windows agents |
| 2 | Restart Spooler | Resolve print queue issues |
| 3 | Flush DNS Cache | Clear stale DNS entries |
| 4 | Run Windows Updates | Trigger immediate Windows Update cycle |
| 5 | Clear Event Logs | Purge Windows event log files |
| 6 | Check AV Status | Verify antivirus definitions and service state |
| 7 | Restart Agent Service | Recover an unresponsive Automate agent |
| 8 | CPU / Memory Snapshot | Capture resource usage at a point in time |
| Option Value | Label | Live Behavior |
|---|---|---|
| all | All Online Agents | Pass no computerId; API runs script against all online computers |
| client | Selected Client | Pass clientId parameter to the script run endpoint |
| one | Selected Agent | Pass single computerId to the script run endpoint |
Managed by logExec(name, ok). Each entry has three columns: timestamp (HH:MM), script name (truncated), status. Status values: .exec-s.run amber "running…" → replaced by .exec-s.ok green "✓ ok" or .exec-s.fail red "✗ fail". Failure probability in demo: ~10% (R() > 0.1).
Alert Feed
Right column, middle slot. Scrollable list of open alerts derived from agents with non-zero alert count. Three inline action buttons revealed on hover.
POST /service/tickets) requires a separate CW Manage API integration or Automate's Service module. Dismiss is always client-side only.
rAlerts(list). Alert count badge (#alertCnt) updates to the current list length. Items with ackedAlerts.has(id) render with class .acked (opacity 0.4). Empty state: centered mono "No active alerts". Left border color set via --aa CSS variable = sevColor(sev).| Field | Display | Source |
|---|---|---|
| title | Top row, bold, truncated | "Monitor alert — {a.name}" or "Agent offline — {a.name}" |
| ago | Top row, right, mono muted | Same as a.lastSeen |
| body | Second row, muted, truncated | "{a.client} · {a.os} · {a.lastSeen}" |
| sev | Left border color via --aa | 1 = red (critical patch), 2 = orange (alert status), 3 = amber (other) |
| id | Used by all action buttons | "ALT-{3000+i}" — incremented per alert in the pool |
| Button | Function | Status | Effect |
|---|---|---|---|
| Ack | ackAlert(id, e) | Active (demo) | Adds to ackedAlerts; adds .acked class to item. Toasts: "Alert acknowledged — PATCH /alerts/{id}" |
| → Ticket | createTicket(id, e) | Simulated | Generates random ticket number; toasts "Ticket #NNNNN created — POST /service/tickets"; also acks the alert. |
| Dismiss | dismissAlert(id, e) | Active (demo) | Adds opacity:0; transition then removes element from DOM after 300ms. Client-side only. |
All three action buttons call e.stopPropagation() to prevent the click from bubbling to the alert item row click handler (which would trigger an agent drawer open).
Top Triggered Monitors
Right column, bottom slot. Six monitor rules ranked by trigger count. Each row shows a label, a proportional fill bar, and a numeric count.
rMonitors(list) from mMonitors(). Bar width = (n / max) × 100% with 0.6s CSS transition. Each monitor has its own accent color. Not individually clickable in the current implementation.| Monitor Name | Color Variable | Demo Range |
|---|---|---|
| CPU > 85% sustained | --red | 8–21 triggers |
| Disk > 90% full | --hi (orange) | 5–13 triggers |
| Agent heartbeat lost | --amber | 4–10 triggers |
| Windows Update failed | --purple | 3–8 triggers |
| AV definition outdated | --cyan | 2–7 triggers |
| Event log errors >50 | --blue | 1–5 triggers |
Detail Drawer
440px slide-in panel from the right. Three distinct content contexts: agent detail, KPI drill-down, and API endpoint health. Closes on overlay click or the × button.
| Trigger | Function | Header Title / Sub | Footer |
|---|---|---|---|
| Click agent table row | openAgentDrawer(id) | Agent name / "Client · OS · IP" | Repair Agent or Isolate · Run Script · Open in Control |
| Click KPI card | showKpiDrawer(type) | Metric title / endpoint path | Close button only |
| Click API endpoint row | showApiDrawer(name, ms, st, path) | Endpoint name / full path | Close button only |
Eight fields in a 2-column grid (.d-grid). Each is a .d-field block with a mono uppercase label and a value. CPU and RAM values are color-coded:
| Field | Color Logic |
|---|---|
| Status, Uptime, Agent Ver, Location | Plain text, no color logic |
| CPU | >80% red · 60–80% amber · <60% green |
| RAM | >85% red · 70–85% amber · <70% green |
| Disk | No color logic — percentage only |
| Patch | Rendered as .patch-badge with full state styling |
Six buttons in .d-cmd-row. Each calls remoteCmd(agentId, cmd). Output renders in #cmdOut as a .cmd-output terminal block. The initial state shows "POST /automate/api/v1/commands → agent {id}" then resolves after 900–1500ms.
| Command | Style | Simulated Output |
|---|---|---|
| Ping | .d-cmd.grn | ICMP reply with agent IP, TTL, and packet loss stats |
| Get Services | .d-cmd.grn | Spooler, W32Time, WinDefend, wuauserv status list |
| Screenshot | .d-cmd | Capture dimensions + storage path |
| Sys Info | .d-cmd | CPU%, RAM%, Disk%, OS, Uptime from live agent object |
| Reboot | .d-cmd.red | Warning + "Agent will reconnect in ~90 seconds" |
| Isolate | .d-cmd.red | Isolation confirmation + "POST /computers/{id}/isolate → 200 OK" |
| Button | Condition | Function | Status |
|---|---|---|---|
| Repair Agent | Agent is offline | repairAgent(id) — toasts info, after 3.2s sets status=online + toasts success, re-renders table | Simulated |
| Isolate | Agent is online or alert | isolateAgent(id) — toasts "POST /computers/{id}/isolate" warning | Simulated |
| Run Script | Always visible | runScriptOn(id) — logs to execHistory, toasts "POST /scripts/1/run" | Simulated |
| Open in Control | Always visible | openControl(id) — toasts "Opening ConnectWise Control…" | Link only |
A vertical timeline at the bottom of the agent drawer. Each event is a .d-tl-row with a dot, connecting line, HH:MM timestamp, and description string. Events are built by buildAgentTimeline(online, hasAlert, patch) at agent-creation time and include up to five entries: offline heartbeat loss, alert trigger, critical patch notice, scheduled script execution, and routine check-in. The most recent live event uses .d-tl-dot.live (cyan with glow).
All API Endpoints
Complete reference of every Automate REST endpoint called or referenced anywhere in the dashboard. All paths are relative to the Automate server base URL.
{ "UserName":"…", "Password":"…", "TwoFactorPasscode":"…" }. Returns a token valid for 24 hours. Proxy must refresh before expiry.
Proxy Required
agents array on every refresh. Drives KPI, table, patch donut, client bars, alerts, and activity chart.Proxy#scriptSelect element dynamically on page load.ProxymApiHealth() mock fills in.ProxyrunScriptOn() from drawer. Body includes computerId or scope. Currently simulated.SimulatedackedAlerts Set — no real API call is made.Demo OnlyConfig & Fields Reference
All configurable constants, threshold values, and timing settings in the dashboard script. These can be changed without touching any render or interaction logic.
| Constant | Current Value | Effect of Changing |
|---|---|---|
| CLIENTS | 7 client name strings | Controls the client roster and agent name prefixes in demo mode. |
| OSS | 6 OS strings | Operating system pool for demo agents. Update to match your environment. |
| PATCH | ['current','pending','critical','outdated'] | Must match the exact values the Automate API returns for patch status. |
| setInterval (refresh) | 30000ms (30s) | Lower to 15000ms for faster updates; raise to 60000ms to reduce API load. |
| AbortSignal.timeout | 5000ms (5s) | Increase for slow on-prem Automate servers before falling back to mock. |
| hist max length | 12 points | Sparkline and trend window size. Increase for longer trend views. |
| execHistory max | 8 entries | Rows visible in the script execution log. |
| mActivity() base range | 180–480 (peak hours), 10–80 (off-hours) | Controls the shape of the 24-hour check-in histogram. |
| Threshold | Value | Location in Code |
|---|---|---|
| Agent online probability | R() > 0.18 | buildAgents() — 82% online, 18% offline |
| Agent alert probability (if online) | R() > 0.78 | buildAgents() — 22% of online agents become alert |
| Random status mutation on refresh | R() > 0.6 | refresh() — 40% of cycles mutate one agent status |
| API latency: OK | < 80ms | mApiHealth() — ms < 80 ? 'ok' |
| API latency: WARN | 80–159ms | mApiHealth() — ms < 160 ? 'warn' |
| API latency: DEG | 160–279ms | mApiHealth() — ms < 280 ? 'deg' |
| CPU red | > 80% | openAgentDrawer() — cpuColor logic |
| CPU amber | 60–80% | openAgentDrawer() — cpuColor logic |
| RAM red | > 85% | openAgentDrawer() — ramColor logic |
| RAM amber | 70–85% | openAgentDrawer() — ramColor logic |
| Activity bar highlight | > 70% of peak | rActivity() — v > max * 0.7 |
| Script execution failure rate | ~10% | logExec() — ok = R() > 0.1 |
apiFetch(url, mock) is the single function that makes all real API calls. To activate live data, this is the only function that needs changing:
Documented Limitations
Known constraints in the current implementation. Each is documented with its root cause and the recommended resolution path.
| ID | Limitation | Root Cause | Resolution |
|---|---|---|---|
| L-01 | All data is simulated | Proxy not configured — apiFetch() always enters catch and returns mock output. | Configure proxy with Bearer token injection. See §18. |
| L-02 | Agent count varies per page load (~70–112) | buildAgents() calls rnd(5,16) per client on every first load. Not deterministic. | Replaced by real GET /computers on proxy activation. |
| L-03 | No search debounce | Search fires renderAgents() on every keypress via oninput. Can cause perceptible lag with 500+ agents. | Add 150ms debounce before production deployment. |
| L-04 | Acknowledged alert state lost on page reload | ackedAlerts is an in-memory Set — not persisted to localStorage or API. | Wire PATCH /alerts/{id} on proxy go-live so ack state persists server-side. |
| L-05 | Remote commands fully simulated | remoteCmd() uses a static outputs object — no POST /commands call is made. | Replace static outputs with real API call after proxy + permission validation. |
| L-06 | Script dropdown is hard-coded | 8 scripts in HTML <option> elements — not fetched from GET /scripts. | Populate dynamically from the Automate scripts API on page load. |
| L-07 | Ticket creation needs separate integration | POST /service/tickets requires CW Manage API or Automate Service module — separate auth scope from the dashboard API user. | Configure dedicated Manage API user and wire endpoint. |
| L-08 | No RBAC — all buttons visible to all users | Static HTML file — any user who can load the page has full button access including Reboot and Isolate. | Enforce at the proxy layer: restrict write endpoints by authenticated role. |
| L-09 | Patch counts computed client-side from agent data | mPatches(agents) iterates the agent array. Accurate only if GET /computers includes patch state per agent. | Replace with direct GET /patches call for accurate totals in production. |
| L-10 | Filter and sort state not persisted | agentFilter and agentSort reset to defaults on every page load. | Add localStorage read/write in setFilter() and sortBy() if persistence is needed. |
| L-11 | Full table re-render on every refresh + keypress | renderAgents() replaces entire #agentTbody innerHTML on every call. Causes full reflow. | Add pagination (100-row pages + "Load more") or virtual scrolling for large environments. |
| L-12 | Open in Control is a stub | openControl(id) only shows a toast — no URL construction or window.open call. | Add CWC server URL to config and build the Control session URL post-proxy. |
Proxy Activation Checklist
Step-by-step process to move the dashboard from demo mode to live production data. Complete all steps in order before declaring the integration active.
https://your-automate-server/automate/api/v1/apitoken should return HTTP 200 or 401, not a connection error. If you get a timeout or TLS error, resolve server-side first.
POST /automate/api/v1/apitoken with body {"UserName":"…","Password":"…","TwoFactorPasscode":""}. Confirm a Bearer token is in the response. Note the 24-hour expiry.proxy_set_header Authorization "Bearer $token"; (b) Node/Express middleware that wraps calls and handles refresh; (c) Cloudflare Worker if Automate is internet-accessible. The proxy must add CORS headers allowing the dashboard origin and inject Authorization: Bearer {token} on every forwarded request.BASE_URL constant pointing to your proxy and update apiFetch() to prefix all paths. If the proxy handles auth injection, no Authorization header is needed in the browser-side fetch call. See §16 for the exact code change./computers fetch should return HTTP 200 with a real JSON array. The KPI "Total Agents" count should match your actual managed environment.POST /commands with the Ping command against an isolated test agent. Verify the real terminal output returns in the drawer. Only then enable destructive commands.#ktc-demo-bar: change the amber notice to reflect the environment (e.g., "PRODUCTION — LIVE DATA"), or remove the element and its corresponding body { padding-top: 36px } rule entirely.- Dedicated Automate API user created with minimum required permissions
- apitoken endpoint validated — HTTP 200 + Bearer token confirmed via Postman or curl
- Proxy / middleware deployed with CORS configured for dashboard origin
- Token auto-refresh implemented — cron or 401-triggered re-authentication
- apiFetch() updated with BASE_URL prefix in dashboard script block
- GET /computers returns real agents — confirmed in DevTools Network tab
- All 6 API Endpoints panel rows show real, consistent latency values
- Alert feed shows real Automate alert names — not demo agent names
- Patch compliance donut reflects real environment patch state distribution
- Write endpoints tested on isolated test agent before production enable
- Script dropdown populated dynamically from GET /scripts
- Demo bar updated or removed — body padding adjusted if removed
- Ticket creation integration configured (CW Manage API or Automate Service module)
- RBAC enforced at proxy layer — destructive commands restricted by role
Troubleshooting
Common issues during deployment and daily operation. Always open DevTools Console and Network tabs first — they will identify 90% of issues before any other diagnostic step is needed.
/automate/api/v1/…) but the proxy is running on a different origin or port. Open DevTools → Network and look at the actual request URL. Verify the proxy is receiving the request — check proxy access logs. Also confirm the Authorization: Bearer … header is present in the outgoing request details. If the server returns any non-200 response code, apiFetch() silently falls back to mock. Add a temporary console.log inside the catch block to surface the real error.name, client, os, status, patch, lastSeen. The real Automate API returns computerName, clientName, etc. Add a normalization function between the apiFetch result and the agents array that maps Automate response fields to the dashboard schema. Also verify that buildAgents() is no longer being called — if agents.length === 0 after a failed or empty fetch, the mock is still running.AbortSignal.timeout prevents infinite hangs, but if every poll is timing out the dot will cycle amber repeatedly. Check Network tab for requests stuck in "pending". Verify the Automate server is reachable from the device's network — if behind a VPN, confirm connectivity. Consider temporarily increasing the timeout to 10000ms to rule out slow server startup. If only one endpoint is timing out, check that path specifically in the proxy routing config.agents.find(x => x.id === id) will fail a strict equality check in that case, so cast with Number(a.id) in the normalization layer; (3) a syntax error introduced during customization that broke the click handler binding.#apiLdr — it is only cleared when rApi() runs. Even in demo mode, mApiHealth() should populate the panel. If it remains empty, open Console and verify there is no error in rApi(). If the Automate instance does not expose a /health endpoint, replace that fetch in refresh() with a call to /computers and derive latency from the response time.ackedAlerts is an in-memory Set that persists within the session. mAlerts(agents) filters out IDs in that Set on every render, so acknowledged items should stay hidden within the same page session. However, if the agent data changes enough that a new alert gets a different ID, it will reappear. In production, PATCH /alerts/{id} must update the server-side state so the API stops returning the acked alert on subsequent polls.renderAgents() replaces the complete #agentTbody innerHTML every 30 seconds and on every search keypress. For environments with 500+ agents this causes measurable layout reflow. Short-term: add pagination (show 100 rows, load-more button). Also add a 150ms debounce to the search oninput handler. Medium-term: switch to a DOM-diff approach that only updates changed rows rather than replacing all of them.Access-Control-Allow-Origin: https://your-dashboard-domain (or * for development) to every proxied response. If using nginx: add_header 'Access-Control-Allow-Origin' '$http_origin'. If using a Cloudflare Worker, set the header in the Worker response. Verify with DevTools → Network → response headers for the failing request.simulateAgent() and simulateOutage() are bound to the "+ Agent Event" and "⚡ Simulate Outage" header buttons. These are harmless in production since they only mutate the in-memory agents array (real data will overwrite on the next 30-second refresh), but they are confusing to end users. Remove or hide both .hdr-btn elements in the header HTML, and optionally delete their corresponding functions from the script block.