collabSupport/scripts/lib/botPhoneApi.js
jmcqueen f7953b8eb5 Add MPP desk phone diagnostics follow-up to /phonestatus via relay.
Wire CP-78xx probe discovery, relay phone-probe commands, and a chat follow-up message so store desk phones get registration, switch, and provisioning detail alongside DECT and WAN diagnostics.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-28 18:01:01 -04:00

75 lines
2.4 KiB
JavaScript

// scripts/lib/botPhoneApi.js
//
// Route phone probes through the running bot's HTTP API when the CLI
// process has no attached relay WebSocket (normal for local scripts).
export function botApiBase() {
return (process.env.PROBE_PHONE_API_BASE
|| process.env.BOT_API_BASE
|| `http://localhost:${process.env.SERVER_PORT || 1800}`).replace(/\/$/, '');
}
export function localRelayConnected(getDectRelayHub) {
try {
return getDectRelayHub().isConnected();
} catch {
return false;
}
}
export async function botApiFetch(path) {
const token = process.env.HTTP_API_TOKEN;
const headers = token ? { Authorization: `Bearer ${token}` } : {};
const url = `${botApiBase()}${path}`;
let res;
try {
res = await fetch(url, { headers });
} catch (err) {
const hint = 'Start the bot (node index.js) so the relay WebSocket is attached, or set PROBE_PHONE_API_BASE to a running instance.';
const e = new Error(`Cannot reach bot API at ${url}${err.message}. ${hint}`);
e.code = 'BOT_API_UNREACHABLE';
e.cause = err;
throw e;
}
const body = await res.json().catch(() => ({}));
if (!res.ok) {
const err = new Error(body?.message || `bot API ${res.status} ${path}`);
err.code = body?.code || 'BOT_API_ERROR';
err.body = body;
throw err;
}
return body;
}
export function mapApiPhoneToProbeResult(target, phoneRow) {
return {
phone: target,
ok: !!phoneRow?.ok,
probes: phoneRow?.probes || null,
statusJson: phoneRow?.statusJson || null,
statusXml: phoneRow?.statusXml || null,
parsed: phoneRow?.parsed || null,
verdict: phoneRow?.verdict || null,
summary: phoneRow?.summary || null,
cfgParsed: phoneRow?.cfgParsed || null,
byteLength: phoneRow?.byteLength || null,
elapsedMs: phoneRow?.elapsedMs || null,
error: phoneRow?.error || null,
};
}
export async function probeTargetsViaBotApi(targets) {
const results = [];
let relay = { connected: false };
for (const t of targets) {
const data = await botApiFetch(`/api/phone/probe-direct/${encodeURIComponent(t.ip)}`);
relay = data.relay || relay;
results.push(mapApiPhoneToProbeResult(t, data.phones?.[0]));
}
return { results, relay };
}
export async function captureStoreViaBotApi(store, { ip } = {}) {
const qs = ip ? `?ip=${encodeURIComponent(ip)}` : '';
return botApiFetch(`/api/phone/probe/${store}${qs}`);
}