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>
102 lines
2.9 KiB
JavaScript
102 lines
2.9 KiB
JavaScript
// integrations/atlas/checkStore.js
|
|
// /atlasdiag orchestrator: resolve → tunnel → AZM report → markdown
|
|
|
|
import { openAuthenticatedTunnel } from './uiSession.js';
|
|
import { fetchAzmCheckReport, formatCheckMarkdown } from './azmWs.js';
|
|
import {
|
|
resolveStoreDevice,
|
|
isOffline,
|
|
deviceStatus,
|
|
} from './resolveStoreDevice.js';
|
|
import { getAtlasDeviceList, getAtlasDeviceDetail } from './devices.js';
|
|
import { XyteHttpError } from './errors.js';
|
|
|
|
/**
|
|
* Run a full store check against Atlas Hub + live AZM tunnel.
|
|
*
|
|
* @param {string|number} storeOrName
|
|
* @param {{ pollSeconds?: number }} [opts]
|
|
*/
|
|
export async function checkStore(storeOrName, { pollSeconds = 90 } = {}) {
|
|
const devices = await getAtlasDeviceList();
|
|
const resolved = resolveStoreDevice(devices, storeOrName);
|
|
if (!resolved.device) {
|
|
return {
|
|
ok: false,
|
|
markdown: `❌ ${resolved.error || 'Device not found'}`,
|
|
error: resolved.error,
|
|
code: resolved.code,
|
|
matches: resolved.matches,
|
|
storePadded: resolved.storePadded,
|
|
};
|
|
}
|
|
|
|
let device = resolved.device;
|
|
const storeDisplay = resolved.storePadded
|
|
? String(Number(resolved.storePadded))
|
|
: storeOrName;
|
|
|
|
try {
|
|
const fresh = await getAtlasDeviceDetail(device.id);
|
|
if (fresh) device = fresh;
|
|
} catch {
|
|
// Fall back to cached device if detail fetch fails.
|
|
}
|
|
|
|
if (isOffline(device)) {
|
|
const st = deviceStatus(device) || 'offline';
|
|
return {
|
|
ok: false,
|
|
markdown: `❌ **Store ${storeDisplay}** device **${device.name}** is ${st} — cannot open tunnel.`,
|
|
error: `Device offline (${st})`,
|
|
code: 'OFFLINE',
|
|
device,
|
|
storePadded: resolved.storePadded,
|
|
};
|
|
}
|
|
|
|
try {
|
|
const tunnel = await openAuthenticatedTunnel(device.id, { pollSeconds });
|
|
const result = await fetchAzmCheckReport(tunnel.wsUrl, {
|
|
cookie: tunnel.cookie,
|
|
origin: tunnel.redirectUrl,
|
|
sessionName: 'check',
|
|
storeNumber: storeDisplay,
|
|
deviceStatus: deviceStatus(device),
|
|
});
|
|
|
|
const markdown = result.markdown || formatCheckMarkdown(result.report, {
|
|
storeNumber: storeDisplay,
|
|
deviceStatus: deviceStatus(device),
|
|
monitorDisable: result.monitorDisable,
|
|
});
|
|
|
|
return {
|
|
ok: true,
|
|
markdown,
|
|
device,
|
|
storePadded: resolved.storePadded,
|
|
report: result.report,
|
|
monitorDisable: result.monitorDisable,
|
|
deviceLogin: result.deviceLogin,
|
|
tunnel: {
|
|
redirectUrl: tunnel.redirectUrl,
|
|
wsUrl: tunnel.wsUrl,
|
|
connected: tunnel.status?.connected,
|
|
},
|
|
};
|
|
} catch (err) {
|
|
const message = err instanceof XyteHttpError
|
|
? `${err.message}${err.status ? ` (${err.status})` : ''}`
|
|
: (err.message || String(err));
|
|
|
|
return {
|
|
ok: false,
|
|
markdown: `❌ **Store ${storeDisplay}** (${device.name}) check failed: ${message}`,
|
|
error: message,
|
|
code: err.code || 'CHECK_FAILED',
|
|
device,
|
|
storePadded: resolved.storePadded,
|
|
};
|
|
}
|
|
}
|