Ports Atlas /check as /atlasdiag: Hub resolve, UI tunnel, WebSocket report for sources/zones/accessories, low source dB warnings, and auto-disable of the monitor speaker when left on after troubleshooting. Co-authored-by: Cursor <cursoragent@cursor.com>
134 lines
6.5 KiB
JavaScript
134 lines
6.5 KiB
JavaScript
// src/commands/registry.js
|
|
//
|
|
// Single source of truth for every bot command.
|
|
//
|
|
// Both dispatch paths in index.js consume this registry:
|
|
// - Webex chat: framework.hears(/.*/, …) parses the text, then runs the
|
|
// entry whose name (or alias) matches the first token.
|
|
// - HTTP API: app.get('/:command', …) looks up the same registry and runs
|
|
// the entry's handler against a captured-output mock bot.
|
|
//
|
|
// Adding a new command means appending one entry below. Both Webex and HTTP
|
|
// pick it up automatically (subject to `mutating` / `http` flags).
|
|
|
|
import { handleHelp } from './help.js';
|
|
import { handleAvStatus } from './avStatus.js';
|
|
import { handleVoiceStatus } from './voiceStatus.js';
|
|
import { handleWanStatus } from './wanStatus.js';
|
|
import { handlePhoneDiag } from './phoneDiag.js';
|
|
import { handleDectDiag } from './dectDiag.js';
|
|
import { handleProvisionDect } from './provisionDect.js';
|
|
import { handleWoHistory } from './woHistory.js';
|
|
import { handleWoSummary } from './woSummary.js';
|
|
import { handleWoAttachments } from './woAttachments.js';
|
|
import { handleJiraHistory } from './jiraHistory.js';
|
|
import { handleJiraTicket } from './jiraTicket.js';
|
|
import { handleJiraPoll } from './jiraPoll.js';
|
|
import { handleProvisionVc } from './vcProvision.js';
|
|
import { handleVcMonitor } from './vcMonitor.js';
|
|
import { handleOffboardUser } from './offboardUser.js';
|
|
import { handleWebexHost } from './webexHost.js';
|
|
import { handleVoiceDiag } from './voiceDiag.js';
|
|
import { handleBulkAvStatusCSV } from './bulkAvStatusCSV.js';
|
|
import { handleBulkAvSwitchCSV } from './bulkAvSwitchCSV.js';
|
|
import { handleTestDevicesByModel } from './testDevicesByModel.js';
|
|
import { handleCallTest } from './callTest.js';
|
|
import { handleCallReport } from './callReport.js';
|
|
import { handleAtlasDiag } from './atlasDiag.js';
|
|
|
|
/**
|
|
* Each entry:
|
|
* - name: canonical name (lowercase, no leading slash).
|
|
* - aliases: additional names that route to the same handler.
|
|
* - handler: async (bot, trigger) => void
|
|
* - mutating: when true, the HTTP /:command path requires HTTP_API_TOKEN.
|
|
* - http: defaults to true. Set false for commands that only make
|
|
* sense over Webex chat (e.g. `help`).
|
|
*/
|
|
export const commands = [
|
|
{ name: 'help', handler: handleHelp, mutating: false, http: false },
|
|
|
|
{ name: 'avstatus', aliases: ['wostatus'], handler: handleAvStatus, mutating: false },
|
|
{ name: 'voicestatus', handler: handleVoiceStatus, mutating: false },
|
|
{ name: 'wanstatus', handler: handleWanStatus, mutating: false },
|
|
{ name: 'atlasdiag', handler: handleAtlasDiag, mutating: false },
|
|
{ name: 'phonediag', handler: handlePhoneDiag, mutating: false },
|
|
// /dectdiag — full DECT base dump via the on-prem relay + chat-only
|
|
// reboot/factory-reset cards. Read path is non-mutating; card submits
|
|
// only fire over Webex (see commands/dectDiag.js).
|
|
{ name: 'dectdiag', aliases: ['dect'], handler: handleDectDiag, mutating: false },
|
|
// /voicediag reads per-user Webex Calling features via
|
|
// /v1/people/{id}/features/* and offers per-issue adaptive-card
|
|
// remediation. Reads are non-mutating but the confirm buttons on
|
|
// the cards perform PUTs, so the HTTP path is gated
|
|
// (mutating: true). See commands/voiceDiag.js + services/voiceDiag/
|
|
// for the full behavior contract + required scopes.
|
|
{ name: 'voicediag', handler: handleVoiceDiag, mutating: true },
|
|
{ name: 'callreport', aliases: ['voicereport'], handler: handleCallReport, mutating: false },
|
|
{ name: 'wohistory', handler: handleWoHistory, mutating: false },
|
|
{ name: 'wosummary', handler: handleWoSummary, mutating: false },
|
|
{ name: 'woattachments', handler: handleWoAttachments, mutating: false },
|
|
{ name: 'jirahistory', handler: handleJiraHistory, mutating: false },
|
|
{ name: 'jiraticket', handler: handleJiraTicket, mutating: false },
|
|
// /jirapoll [prime] — trigger the hourly Jira poller on demand. Writes
|
|
// Jira comments + labels, so mutating: true (HTTP path gated by
|
|
// HTTP_API_TOKEN). See commands/jiraPoll.js for the behavior contract.
|
|
{ name: 'jirapoll', aliases: ['pollnow'], handler: handleJiraPoll, mutating: true },
|
|
|
|
{ name: 'provision-dect', aliases: ['provisiondect'], handler: handleProvisionDect, mutating: true },
|
|
{ name: 'provision-vc', aliases: ['vcprovision'], handler: handleProvisionVc, mutating: true },
|
|
{ name: 'vcmonitor', handler: handleVcMonitor, mutating: true },
|
|
// /calltest places real Twilio PSTN calls (store AA path or direct dial).
|
|
{ name: 'calltest', handler: handleCallTest, mutating: true },
|
|
{ name: 'offboarduser', handler: handleOffboardUser, mutating: true },
|
|
{ name: 'webexhost', handler: handleWebexHost, mutating: true },
|
|
// bulkavstatuscsv delivers a CSV attachment via BotClient.sendWithAttachment,
|
|
// which requires a Webex roomId. The HTTP mock trigger has no roomId, so
|
|
// the command is chat-only; the runtime guard in the handler backstops this.
|
|
{ name: 'bulkavstatuscsv', handler: handleBulkAvStatusCSV, mutating: true, http: false },
|
|
{ name: 'bulkavswitchcsv', handler: handleBulkAvSwitchCSV, mutating: true },
|
|
{ name: 'devicesbymodel', handler: handleTestDevicesByModel, mutating: true },
|
|
];
|
|
|
|
const byName = new Map();
|
|
for (const cmd of commands) {
|
|
const all = [cmd.name, ...(cmd.aliases || [])].map(n => n.toLowerCase());
|
|
for (const key of all) {
|
|
if (byName.has(key)) {
|
|
// Fail loud on misconfiguration — silent alias overwrite is the kind of
|
|
// drift this registry is designed to prevent.
|
|
throw new Error(`Duplicate command registration: "${key}"`);
|
|
}
|
|
byName.set(key, cmd);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Look up a command by canonical name or alias. Case-insensitive.
|
|
* @returns {{name: string, handler: Function, mutating: boolean, http?: boolean, aliases?: string[]} | null}
|
|
*/
|
|
export function getCommand(name) {
|
|
if (!name) return null;
|
|
return byName.get(String(name).toLowerCase().trim()) || null;
|
|
}
|
|
|
|
/** Names + aliases of commands exposed over HTTP (used for /:command 404 hints). */
|
|
export const ALL_HTTP_COMMAND_KEYS = (() => {
|
|
const keys = [];
|
|
for (const cmd of commands) {
|
|
if (cmd.http === false) continue;
|
|
keys.push(cmd.name, ...(cmd.aliases || []));
|
|
}
|
|
return keys.sort();
|
|
})();
|
|
|
|
/** Set of names + aliases that require HTTP_API_TOKEN on the HTTP path. */
|
|
export const MUTATING_COMMAND_KEYS = (() => {
|
|
const keys = new Set();
|
|
for (const cmd of commands) {
|
|
if (!cmd.mutating) continue;
|
|
keys.add(cmd.name);
|
|
for (const a of cmd.aliases || []) keys.add(a);
|
|
}
|
|
return keys;
|
|
})();
|