// src/services/voiceDiag/checks/index.js // // Ordered registry of every /voicediag check. Order matters — the // renderer walks this list to produce the output, so put the checks a // human operator would notice first (DND, forwarding) at the top, // then the deeper feature-config ones, then finally the online-device // summary which is often the last thing they want to see. // // Adding a new check // 1. Drop a new file next to this one (e.g. `myNewCheck.js`) that // exports the check descriptor as its default export or a named // export. // 2. Add it to CHECKS below in the position you want it to render. // 3. If it exports a `remediations: {actionId: handler}` map, the // voiceDiagService remediation registry will pick it up // automatically. Action ids must be globally unique across all // checks — a collision throws at startup. // // Every check must export an object matching: // // { // id: string, // stable, snake_case, globally unique // label: string, // human-readable heading // requires: string[], // subset of ['personId','phoneStatus', // // 'telephonyProfile'] — runner skips // // the check if any are missing on ctx // scope: string, // primary Webex scope needed; surfaced // // in the skipped message on 401/403/404 // run: async (ctx) => CheckResult, // remediations?: { [actionId: string]: async (bot, data, requester) => void } // } import { dndCheck } from './dnd.js'; import { callForwardingCheck } from './callForwarding.js'; import { callWaitingCheck } from './callWaiting.js'; import { voicemailCheck } from './voicemail.js'; import { callInterceptCheck } from './callIntercept.js'; import { hotelingCheck } from './hoteling.js'; import { executiveAssistantCheck } from './executiveAssistant.js'; import { outgoingPermissionCheck } from './outgoingPermission.js'; import { phoneOnlineCheck } from './phoneOnline.js'; import { portTypeCheck } from './port/portType.js'; import { portVlanCheck } from './port/portVlan.js'; import { portPoeCheck } from './port/portPoe.js'; import { portEnabledCheck } from './port/portEnabled.js'; import { wanSiteCheck } from './wan/wanSite.js'; import { wanHealthscoreCheck } from './wan/wanHealthscore.js'; import { wanLinkStateCheck } from './wan/wanLinkState.js'; import { wanLatencyCheck } from './wan/wanLatency.js'; import { wanJitterCheck } from './wan/wanJitter.js'; import { wanLossCheck } from './wan/wanLoss.js'; import { wanMosCheck } from './wan/wanMos.js'; import { wanAppRtpMosCheck } from './wan/wanAppRtpMos.js'; import { wanAppRtpLossCheck } from './wan/wanAppRtpLoss.js'; import { wanAppRtpJitterCheck } from './wan/wanAppRtpJitter.js'; import { wanAlarmsCheck } from './wan/wanAlarms.js'; import { wanTunnelsCheck } from './wan/wanTunnels.js'; import { mppDeskPhoneRelayCheck } from './mppDeskPhoneRelay.js'; import { dectBaseRelayCheck } from './dectBaseRelay.js'; // Order: user-facing feature signals first (things an operator can // see from the phone UI), then LAN-side port hygiene, then the WAN // bucket (Prisma SD-WAN — the "network edge" one hop out from the // LAN), then the broad-brush online summary last so it acts as a // reachability closer. export const CHECKS = [ dndCheck, callForwardingCheck, callInterceptCheck, callWaitingCheck, voicemailCheck, hotelingCheck, executiveAssistantCheck, outgoingPermissionCheck, portTypeCheck, portVlanCheck, portPoeCheck, portEnabledCheck, wanSiteCheck, wanHealthscoreCheck, wanLinkStateCheck, wanTunnelsCheck, wanLatencyCheck, wanJitterCheck, wanLossCheck, wanMosCheck, // Per-app (DPI) audio-quality checks. These are graded against the // WORST 5-min window in the series rather than the average, so they // catch transient degradation the LQM per-link averages smooth over. // Feature-gated on the PRISMA_APP_ID_VOICE env var (with // PRISMA_APP_ID_RTP_BASE honored for backwards compat) — skipped // with an actionable message when not configured. wanAppRtpMosCheck, wanAppRtpLossCheck, wanAppRtpJitterCheck, wanAlarmsCheck, mppDeskPhoneRelayCheck, dectBaseRelayCheck, phoneOnlineCheck, ]; const _byId = new Map(); for (const c of CHECKS) { if (!c || typeof c !== 'object') { throw new Error('voicediag: encountered non-object check in registry'); } if (!c.id || typeof c.id !== 'string') { throw new Error('voicediag: check is missing required "id" string'); } const key = c.id.toLowerCase(); if (_byId.has(key)) { throw new Error(`voicediag: duplicate check id "${c.id}"`); } _byId.set(key, c); } /** Case-insensitive lookup so `--only DND` and `--only dnd` both work. */ export function getCheckById(id) { if (!id) return null; return _byId.get(String(id).toLowerCase().trim()) || null; }