import type { Id } from "@/convex/_generated/dataModel" import { DEVICE_INVENTORY_COLUMN_METADATA, type DeviceInventoryColumnConfig } from "@/lib/device-inventory-columns" import { buildXlsxWorkbook, type WorksheetConfig } from "@/lib/xlsx" type DeviceCustomField = { fieldId: Id<"deviceFields"> fieldKey: string label: string type: string value: unknown displayValue?: string } type LinkedUser = { id: string email: string | null name: string | null } export type MachineInventoryRecord = { id: Id<"machines"> tenantId: string hostname: string displayName: string | null deviceType: string | null devicePlatform: string | null deviceProfile?: Record | null managementMode: string | null companyId: Id<"companies"> | null companySlug: string | null companyName: string | null status: string | null isActive: boolean lastHeartbeatAt: number | null persona: string | null assignedUserName: string | null assignedUserEmail: string | null authEmail: string | null osName: string osVersion: string | null architecture: string | null macAddresses: string[] serialNumbers: string[] registeredBy: string | null createdAt: number updatedAt: number token: { expiresAt: number; usageCount: number; lastUsedAt: number | null } | null inventory: Record | null metrics?: Record | null postureAlerts?: Array> | null lastPostureAt?: number | null remoteAccess?: unknown linkedUsers?: LinkedUser[] customFields?: DeviceCustomField[] usbPolicy?: string | null usbPolicyStatus?: string | null ticketCount?: number | null } type WorkbookOptions = { tenantId: string generatedBy?: string | null companyFilterLabel?: string | null generatedAt?: Date columns?: DeviceInventoryColumnConfig[] } type SoftwareEntry = { hostname: string name: string version: string | null source: string | null publisher: string | null installedOn: string | null } type InventoryColumnDefinition = { key: string label: string width: number getValue: (machine: MachineInventoryRecord, derived: MachineDerivedData) => unknown } type MachineDerivedData = { inventory: Record hardware: ReturnType gpuNames: string[] labels: string[] primaryIp: string | null publicIp: string | null softwareEntries: SoftwareEntry[] linkedUsersLabel: string | null systemInfo: ReturnType collaborator: ReturnType remoteAccessCount: number fleetInfo: ReturnType customFieldByKey: Record customFieldById: Record } const COLUMN_VALUE_RESOLVERS: Record unknown> = { displayName: (machine) => machine.displayName ?? machine.hostname, hostname: (machine) => machine.hostname, deviceType: (machine) => describeDeviceType(machine.deviceType), devicePlatform: (machine) => machine.devicePlatform ?? null, company: (machine) => machine.companyName ?? null, status: (machine) => describeStatus(machine.status), persona: (machine) => describePersona(machine.persona), active: (machine) => yesNo(machine.isActive), lastHeartbeat: (machine) => formatDateTime(machine.lastHeartbeatAt), assignedUser: (machine) => machine.assignedUserName ?? machine.assignedUserEmail ?? null, assignedEmail: (machine) => machine.assignedUserEmail ?? null, linkedUsers: (_machine, derived) => derived.linkedUsersLabel, authEmail: (machine) => machine.authEmail ?? null, osName: (machine) => machine.osName, osVersion: (machine) => machine.osVersion ?? null, architecture: (machine) => machine.architecture ?? null, hardwareVendor: (_machine, derived) => derived.hardware.vendor ?? derived.systemInfo.systemManufacturer ?? null, hardwareModel: (_machine, derived) => derived.hardware.model ?? derived.systemInfo.systemModel ?? null, hardwareSerial: (_machine, derived) => derived.hardware.serial ?? derived.systemInfo.boardSerial ?? null, cpu: (_machine, derived) => derived.hardware.cpuType ?? null, physicalCores: (_machine, derived) => derived.hardware.physicalCores ?? null, logicalCores: (_machine, derived) => derived.hardware.logicalCores ?? null, memoryGiB: (_machine, derived) => derived.hardware.memoryGiB ?? null, gpus: (_machine, derived) => (derived.gpuNames.length > 0 ? derived.gpuNames.join(", ") : null), labels: (_machine, derived) => (derived.labels.length > 0 ? derived.labels.join(", ") : null), macs: (machine) => (machine.macAddresses.length > 0 ? machine.macAddresses.join(", ") : null), serials: (machine) => (machine.serialNumbers.length > 0 ? machine.serialNumbers.join(", ") : null), primaryIp: (_machine, derived) => derived.primaryIp ?? null, publicIp: (_machine, derived) => derived.publicIp ?? null, registeredBy: (machine) => machine.registeredBy ?? null, tokenExpiresAt: (machine) => (machine.token?.expiresAt ? formatDateTime(machine.token.expiresAt) : null), tokenLastUsedAt: (machine) => (machine.token?.lastUsedAt ? formatDateTime(machine.token.lastUsedAt) : null), tokenUsageCount: (machine) => machine.token?.usageCount ?? 0, createdAt: (machine) => formatDateTime(machine.createdAt), updatedAt: (machine) => formatDateTime(machine.updatedAt), softwareCount: (_machine, derived) => derived.softwareEntries.length, osBuild: (_machine, derived) => derived.systemInfo.osBuild ?? null, osLicense: (_machine, derived) => derived.systemInfo.license ?? null, osExperience: (_machine, derived) => derived.systemInfo.experience ?? null, domain: (_machine, derived) => derived.systemInfo.domain ?? null, workgroup: (_machine, derived) => derived.systemInfo.workgroup ?? null, deviceName: (_machine, derived) => derived.systemInfo.deviceName ?? null, boardSerial: (_machine, derived) => derived.systemInfo.boardSerial ?? derived.hardware.serial ?? null, collaboratorName: (_machine, derived) => derived.collaborator?.name ?? null, collaboratorEmail: (_machine, derived) => derived.collaborator?.email ?? null, remoteAccessCount: (_machine, derived) => derived.remoteAccessCount, fleetId: (_machine, derived) => derived.fleetInfo?.id ?? null, fleetTeam: (_machine, derived) => derived.fleetInfo?.team ?? null, fleetUpdatedAt: (_machine, derived) => derived.fleetInfo?.updatedAt ? formatDateTime(derived.fleetInfo.updatedAt) : null, managementMode: (machine) => describeManagementMode(machine.managementMode), usbPolicy: (machine) => describeUsbPolicy(machine.usbPolicy), usbPolicyStatus: (machine) => describeUsbPolicyStatus(machine.usbPolicyStatus), ticketCount: (machine) => machine.ticketCount ?? 0, } const DEFAULT_COLUMN_CONFIG: DeviceInventoryColumnConfig[] = DEVICE_INVENTORY_COLUMN_METADATA.filter((meta) => meta.default !== false).map( (meta) => ({ key: meta.key }) ) const INVENTORY_COLUMN_DEFINITIONS: InventoryColumnDefinition[] = DEVICE_INVENTORY_COLUMN_METADATA.map((meta) => ({ key: meta.key, label: meta.label, width: meta.width, getValue: COLUMN_VALUE_RESOLVERS[meta.key] ?? (() => null), })) const INVENTORY_COLUMN_MAP: Record = Object.fromEntries( INVENTORY_COLUMN_DEFINITIONS.map((column) => [column.key, column]), ) const CUSTOM_FIELD_KEY_PREFIX = "custom:" const CUSTOM_FIELD_ID_PREFIX = "custom#" function deriveMachineData(machine: MachineInventoryRecord): MachineDerivedData { const inventory = toRecord(machine.inventory) ?? {} const hardware = extractHardware(inventory) const gpuNames = extractGpuNames(inventory) const labels = extractLabels(inventory) const primaryIp = extractPrimaryIp(inventory) const publicIp = extractPublicIp(inventory) const softwareEntries = extractSoftwareEntries(machine.hostname, inventory) const linkedUsersLabel = summarizeLinkedUsers(machine.linkedUsers) const systemInfo = extractSystemInfo(inventory) const collaborator = extractCollaborator(machine, inventory) const remoteAccessCount = collectRemoteAccessEntries(machine).length const fleetInfo = extractFleetInfo(inventory) const customFieldByKey: Record = {} const customFieldById: Record = {} for (const field of machine.customFields ?? []) { customFieldByKey[field.fieldKey] = field customFieldById[String(field.fieldId)] = field } return { inventory, hardware, gpuNames, labels, primaryIp, publicIp, softwareEntries, linkedUsersLabel, systemInfo, collaborator, remoteAccessCount, fleetInfo, customFieldByKey, customFieldById, } } export function normalizeInventoryColumnConfig(columns?: DeviceInventoryColumnConfig[]): DeviceInventoryColumnConfig[] { if (!columns || columns.length === 0) { return [...DEFAULT_COLUMN_CONFIG] } const seen = new Set() const normalized: DeviceInventoryColumnConfig[] = [] for (const column of columns) { const key = column.key.trim() if (!key) continue if (seen.has(key)) continue if (!isCustomFieldKey(key) && !INVENTORY_COLUMN_MAP[key]) continue seen.add(key) normalized.push({ key, label: column.label?.trim() || undefined, }) } return normalized.length > 0 ? normalized : [...DEFAULT_COLUMN_CONFIG] } function resolveColumnLabel(column: DeviceInventoryColumnConfig, derivedList: MachineDerivedData[]): string { if (column.label) return column.label const definition = INVENTORY_COLUMN_MAP[column.key] if (definition) return definition.label if (isCustomFieldKey(column.key)) { for (const derived of derivedList) { const field = lookupCustomField(column.key, derived) if (field) return field.label } return "Campo personalizado" } return column.key } function resolveColumnWidth(key: string): number { const definition = INVENTORY_COLUMN_MAP[key] if (definition) return definition.width if (isCustomFieldKey(key)) return 28 return 20 } function isCustomFieldKey(key: string): boolean { return key.startsWith(CUSTOM_FIELD_KEY_PREFIX) || key.startsWith(CUSTOM_FIELD_ID_PREFIX) } function lookupCustomField(key: string, derived: MachineDerivedData): DeviceCustomField | null { if (key.startsWith(CUSTOM_FIELD_KEY_PREFIX)) { const fieldKey = key.slice(CUSTOM_FIELD_KEY_PREFIX.length) return derived.customFieldByKey[fieldKey] ?? null } if (key.startsWith(CUSTOM_FIELD_ID_PREFIX)) { const fieldId = key.slice(CUSTOM_FIELD_ID_PREFIX.length) return derived.customFieldById[fieldId] ?? null } return null } function formatCustomFieldValue(field: DeviceCustomField): string { if (field.value === null || field.value === undefined) { return "—" } if (field.displayValue !== undefined && field.displayValue !== null) { const text = String(field.displayValue).trim() if (text.length > 0) return text } switch (field.type) { case "boolean": return yesNo(Boolean(field.value)) case "number": { const num = typeof field.value === "number" ? field.value : Number(field.value) return Number.isFinite(num) ? String(num) : String(field.value) } case "date": { const date = new Date(field.value as string | number) if (Number.isNaN(date.getTime())) return String(field.value) return date.toISOString().slice(0, 10) } default: return String(field.value) } } function resolveColumnValue( key: string, machine: MachineInventoryRecord, derived: MachineDerivedData ): unknown { if (isCustomFieldKey(key)) { const field = lookupCustomField(key, derived) if (!field) return "—" return formatCustomFieldValue(field) } const definition = INVENTORY_COLUMN_MAP[key] if (!definition) return "—" return definition.getValue(machine, derived) } function formatInventoryCell(value: unknown): unknown { if (value === null || value === undefined) return "—" if (typeof value === "string") { const trimmed = value.trim() return trimmed.length > 0 ? trimmed : "—" } return value } function describeDeviceType(type: string | null | undefined): string { const normalized = (type ?? "").toLowerCase() switch (normalized) { case "desktop": return "Desktop" case "mobile": return "Celular" case "tablet": return "Tablet" default: return "Desconhecido" } } function describeManagementMode(mode: string | null | undefined): string { const normalized = (mode ?? "").toLowerCase() switch (normalized) { case "agent": return "Agente" case "manual": return "Manual" default: return "—" } } function describeUsbPolicy(policy: string | null | undefined): string { if (!policy) return "—" const normalized = policy.toUpperCase() switch (normalized) { case "ALLOW": return "Permitido" case "BLOCK_ALL": return "Bloqueado" case "READONLY": return "Somente leitura" default: return policy } } function describeUsbPolicyStatus(status: string | null | undefined): string { if (!status) return "—" const normalized = status.toUpperCase() switch (normalized) { case "PENDING": return "Pendente" case "APPLYING": return "Aplicando" case "APPLIED": return "Aplicado" case "FAILED": return "Falhou" default: return status } } const SOFTWARE_HEADERS = ["Hostname", "Aplicativo", "Versão", "Origem", "Publicador", "Instalado em"] as const const SOFTWARE_COLUMN_WIDTHS = [22, 36, 18, 18, 22, 20] as const const PARTITION_HEADERS = [ "Hostname", "Nome", "Montagem", "FS", "Capacidade", "Livre", "Utilizado", "Interface", "Serial", "Origem", ] as const const PARTITION_COLUMN_WIDTHS = [22, 24, 18, 12, 16, 16, 16, 16, 20, 18] as const const PHYSICAL_DISK_HEADERS = ["Hostname", "Modelo", "Tamanho", "Interface", "Tipo", "Serial", "Origem"] as const const PHYSICAL_DISK_COLUMN_WIDTHS = [22, 32, 18, 16, 16, 22, 16] as const const NETWORK_HEADERS = ["Hostname", "Interface", "MAC", "IP", "Origem"] as const const NETWORK_COLUMN_WIDTHS = [22, 28, 22, 24, 18] as const const REMOTE_ACCESS_HEADERS = [ "Hostname", "Provedor", "Identificador", "Usuário", "Senha", "URL", "Notas", "Última verificação", "Origem", "Metadados", ] as const const REMOTE_ACCESS_COLUMN_WIDTHS = [22, 22, 24, 24, 20, 32, 28, 22, 16, 36] as const const SERVICE_HEADERS = ["Hostname", "Nome", "Exibição", "Status", "Origem"] as const const SERVICE_COLUMN_WIDTHS = [22, 28, 36, 18, 18] as const const ALERT_HEADERS = ["Hostname", "Tipo", "Mensagem", "Severidade", "Criado em"] as const const ALERT_COLUMN_WIDTHS = [22, 18, 44, 14, 22] as const const METRICS_HEADERS = [ "Hostname", "Capturado em", "CPU (%)", "Memória usada", "Memória total", "Memória (%)", "Disco usado", "Disco total", "Disco (%)", "GPU (%)", ] as const const METRICS_COLUMN_WIDTHS = [22, 24, 14, 20, 20, 14, 20, 20, 14, 14] as const const LABEL_HEADERS = ["Hostname", "Label"] as const const LABEL_COLUMN_WIDTHS = [22, 30] as const const SYSTEM_HEADERS = ["Hostname", "Categoria", "Campo", "Valor"] as const const SYSTEM_COLUMN_WIDTHS = [22, 24, 32, 44] as const const STATUS_LABELS: Record = { online: "Online", offline: "Offline", stale: "Sem sinal", maintenance: "Manutenção", blocked: "Bloqueada", deactivated: "Desativada", unknown: "Desconhecido", } const PERSONA_LABELS: Record = { collaborator: "Colaborador", manager: "Gestor", machine: "Dispositivo", } const SUMMARY_STATUS_ORDER = ["Online", "Sem sinal", "Offline", "Manutenção", "Bloqueada", "Desativada", "Desconhecido"] type WorksheetRow = Array export function buildMachinesInventoryWorkbook( machines: MachineInventoryRecord[], options: WorkbookOptions, ): Buffer { const generatedAt = options.generatedAt ?? new Date() const summaryRows = buildSummaryRows(machines, options, generatedAt) const inventorySheet = buildInventoryWorksheet(machines, options.columns) const linksRows = buildLinkedUsersRows(machines) const softwareRows = buildSoftwareRows(machines) const partitionRows = buildPartitionRows(machines) const physicalDiskRows = buildPhysicalDiskRows(machines) const networkRows = buildNetworkRows(machines) const remoteAccessRows = buildRemoteAccessRows(machines) const serviceRows = buildServiceRows(machines) const alertRows = buildAlertRows(machines) const metricsRows = buildMetricsRows(machines) const labelRows = buildLabelRows(machines) const systemRows = buildSystemRows(machines) const sheets: WorksheetConfig[] = [] sheets.push({ name: "Resumo", headers: ["Item", "Valor"], rows: summaryRows, columnWidths: [28, 48], }) sheets.push(inventorySheet) sheets.push({ name: "Vínculos", headers: ["Hostname", "Empresa", "Usuário", "E-mail"], rows: linksRows.length > 0 ? linksRows : [["—", "—", "—", "—"]], columnWidths: [22, 26, 26, 28], freezePane: { rowSplit: 1 }, autoFilter: true, }) sheets.push({ name: "Softwares", headers: [...SOFTWARE_HEADERS], rows: softwareRows.length > 0 ? softwareRows : [["—", "—", "—", "—", "—", "—"]], columnWidths: [...SOFTWARE_COLUMN_WIDTHS], freezePane: { rowSplit: 1 }, autoFilter: softwareRows.length > 0, }) if (partitionRows.length > 0) { sheets.push({ name: "Partições", headers: [...PARTITION_HEADERS], rows: partitionRows, columnWidths: [...PARTITION_COLUMN_WIDTHS], freezePane: { rowSplit: 1 }, autoFilter: true, }) } if (physicalDiskRows.length > 0) { sheets.push({ name: "Discos físicos", headers: [...PHYSICAL_DISK_HEADERS], rows: physicalDiskRows, columnWidths: [...PHYSICAL_DISK_COLUMN_WIDTHS], freezePane: { rowSplit: 1 }, autoFilter: true, }) } if (networkRows.length > 0) { sheets.push({ name: "Rede", headers: [...NETWORK_HEADERS], rows: networkRows, columnWidths: [...NETWORK_COLUMN_WIDTHS], freezePane: { rowSplit: 1 }, autoFilter: true, }) } if (remoteAccessRows.length > 0) { sheets.push({ name: "Acessos remotos", headers: [...REMOTE_ACCESS_HEADERS], rows: remoteAccessRows, columnWidths: [...REMOTE_ACCESS_COLUMN_WIDTHS], freezePane: { rowSplit: 1 }, autoFilter: true, }) } if (serviceRows.length > 0) { sheets.push({ name: "Serviços", headers: [...SERVICE_HEADERS], rows: serviceRows, columnWidths: [...SERVICE_COLUMN_WIDTHS], freezePane: { rowSplit: 1 }, autoFilter: true, }) } if (alertRows.length > 0) { sheets.push({ name: "Alertas", headers: [...ALERT_HEADERS], rows: alertRows, columnWidths: [...ALERT_COLUMN_WIDTHS], freezePane: { rowSplit: 1 }, autoFilter: true, }) } if (metricsRows.length > 0) { sheets.push({ name: "Métricas", headers: [...METRICS_HEADERS], rows: metricsRows, columnWidths: [...METRICS_COLUMN_WIDTHS], freezePane: { rowSplit: 1 }, autoFilter: true, }) } if (labelRows.length > 0) { sheets.push({ name: "Labels", headers: [...LABEL_HEADERS], rows: labelRows, columnWidths: [...LABEL_COLUMN_WIDTHS], freezePane: { rowSplit: 1 }, autoFilter: true, }) } if (systemRows.length > 0) { sheets.push({ name: "Sistema", headers: [...SYSTEM_HEADERS], rows: systemRows, columnWidths: [...SYSTEM_COLUMN_WIDTHS], freezePane: { rowSplit: 1 }, autoFilter: true, }) } return buildXlsxWorkbook(sheets) } export function buildInventoryWorksheet( machines: MachineInventoryRecord[], columns?: DeviceInventoryColumnConfig[], sheetName = "Inventário", ): WorksheetConfig { const columnConfig = normalizeInventoryColumnConfig(columns) const derivedList = machines.map((machine) => deriveMachineData(machine)) const headers = columnConfig.map((column) => resolveColumnLabel(column, derivedList)) const columnWidths = columnConfig.map((column) => resolveColumnWidth(column.key)) const inventoryRows = machines.map((machine, index) => { const derived = derivedList[index] return columnConfig.map((column) => formatInventoryCell(resolveColumnValue(column.key, machine, derived))) }) const fallbackRows = inventoryRows.length > 0 ? inventoryRows : [columnConfig.map(() => "—")] return { name: sheetName, headers, rows: fallbackRows, columnWidths, freezePane: { rowSplit: 1 }, autoFilter: inventoryRows.length > 0, } } function buildSummaryRows( machines: MachineInventoryRecord[], options: WorkbookOptions, generatedAt: Date, ): Array<[string, unknown]> { const rows: Array<[string, unknown]> = [ ["Tenant", options.tenantId], ["Gerado em", formatDateTime(generatedAt.getTime()) ?? generatedAt.toISOString()], ] if (options.generatedBy) { rows.push(["Solicitado por", options.generatedBy]) } if (options.companyFilterLabel) { rows.push(["Filtro de empresa", options.companyFilterLabel]) } rows.push(["Total de dispositivos", machines.length]) const activeCount = machines.filter((machine) => machine.isActive).length rows.push(["Dispositivos ativos", activeCount]) rows.push(["Dispositivos inativos", machines.length - activeCount]) const typeCounts = new Map() machines.forEach((machine) => { const label = describeDeviceType(machine.deviceType) typeCounts.set(label, (typeCounts.get(label) ?? 0) + 1) }) const DEVICE_TYPE_ORDER = ["Desktop", "Celular", "Tablet", "Desconhecido"] Array.from(typeCounts.entries()) .sort((a, b) => { const idxA = DEVICE_TYPE_ORDER.indexOf(a[0]) const idxB = DEVICE_TYPE_ORDER.indexOf(b[0]) if (idxA === -1 && idxB === -1) return a[0].localeCompare(b[0], "pt-BR") if (idxA === -1) return 1 if (idxB === -1) return -1 return idxA - idxB }) .forEach(([label, total]) => rows.push([`Tipo: ${label}`, total])) const statusCounts = new Map() machines.forEach((machine) => { const label = describeStatus(machine.status) statusCounts.set(label, (statusCounts.get(label) ?? 0) + 1) }) const sortedStatuses = Array.from(statusCounts.entries()).sort((a, b) => { const indexA = SUMMARY_STATUS_ORDER.indexOf(a[0]) const indexB = SUMMARY_STATUS_ORDER.indexOf(b[0]) if (indexA === -1 && indexB === -1) return a[0].localeCompare(b[0], "pt-BR") if (indexA === -1) return 1 if (indexB === -1) return -1 return indexA - indexB }) sortedStatuses.forEach(([status, total]) => { rows.push([`Status: ${status}`, total]) }) const uniqueCompanies = new Set(machines.map((machine) => machine.companyName).filter(Boolean) as string[]) if (uniqueCompanies.size > 0) { rows.push(["Empresas no resultado", Array.from(uniqueCompanies).sort((a, b) => a.localeCompare(b, "pt-BR")).join(", ")]) } const totalRemoteAccess = machines.reduce((acc, machine) => acc + collectRemoteAccessEntries(machine).length, 0) if (totalRemoteAccess > 0) { rows.push(["Acessos remotos configurados", totalRemoteAccess]) } const totalAlerts = machines.reduce((acc, machine) => acc + (machine.postureAlerts?.length ?? 0), 0) if (totalAlerts > 0) { rows.push(["Alertas de postura ativos", totalAlerts]) } return rows } function buildLinkedUsersRows(machines: MachineInventoryRecord[]): Array<[string, string | null, string | null, string | null]> { const rows: Array<[string, string | null, string | null, string | null]> = [] machines.forEach((machine) => { machine.linkedUsers?.forEach((user) => { rows.push([machine.hostname, machine.companyName ?? null, user.name ?? user.email ?? null, user.email ?? null]) }) }) return rows } function buildSoftwareRows(machines: MachineInventoryRecord[]): WorksheetRow[] { const rows = new Map() machines.forEach((machine) => { const inventory = toRecord(machine.inventory) const entries = extractSoftwareEntries(machine.hostname, inventory) entries.forEach((entry) => { const key = [ entry.hostname.toLowerCase(), (entry.name ?? "").toLowerCase(), (entry.version ?? "").toLowerCase(), (entry.source ?? "").toLowerCase(), (entry.publisher ?? "").toLowerCase(), (entry.installedOn ?? "").toLowerCase(), ].join("|") if (!rows.has(key)) { rows.set(key, [ entry.hostname, entry.name, entry.version ?? "—", entry.source ?? "—", entry.publisher ?? "—", entry.installedOn ?? "—", ]) } }) }) return Array.from(rows.values()).sort((a, b) => { const hostCompare = String(a[0]).localeCompare(String(b[0]), "pt-BR") if (hostCompare !== 0) return hostCompare return String(a[1]).localeCompare(String(b[1]), "pt-BR") }) } function buildPartitionRows(machines: MachineInventoryRecord[]): WorksheetRow[] { const rows: WorksheetRow[] = [] machines.forEach((machine) => { const inventory = toRecord(machine.inventory) const partitions = extractPartitionEntries(machine.hostname, inventory) partitions.forEach((partition) => { rows.push([ partition.hostname, partition.name ?? "—", partition.mount ?? "—", partition.fs ?? "—", formatBytesValue(partition.capacityBytes), formatBytesValue(partition.freeBytes), partition.capacityBytes !== null && partition.freeBytes !== null ? formatBytesValue(Math.max(0, partition.capacityBytes - partition.freeBytes)) : "—", partition.interface ?? "—", partition.serial ?? "—", partition.origin, ]) }) }) return rows } function buildPhysicalDiskRows(machines: MachineInventoryRecord[]): WorksheetRow[] { const rows: WorksheetRow[] = [] machines.forEach((machine) => { const inventory = toRecord(machine.inventory) const disks = extractPhysicalDiskEntries(machine.hostname, inventory) disks.forEach((disk) => { rows.push([ disk.hostname, disk.model ?? disk.serial ?? "—", formatBytesValue(disk.sizeBytes), disk.interface ?? "—", disk.mediaType ?? "—", disk.serial ?? "—", disk.origin, ]) }) }) return rows } function buildNetworkRows(machines: MachineInventoryRecord[]): WorksheetRow[] { const rows: WorksheetRow[] = [] machines.forEach((machine) => { const inventory = toRecord(machine.inventory) const interfaces = extractNetworkEntries(machine.hostname, inventory) interfaces.forEach((iface) => { rows.push([iface.hostname, iface.name ?? "—", iface.mac ?? "—", iface.address ?? "—", iface.origin]) }) }) return rows } function buildRemoteAccessRows(machines: MachineInventoryRecord[]): WorksheetRow[] { const rows: WorksheetRow[] = [] machines.forEach((machine) => { collectRemoteAccessEntries(machine).forEach((entry) => { rows.push([ machine.hostname, entry.provider ?? "—", entry.identifier ?? "—", entry.username ?? "—", entry.password ?? "—", entry.url ?? "—", entry.notes ?? "—", entry.lastVerifiedAt ? formatDateTime(entry.lastVerifiedAt) ?? "—" : "—", entry.origin, stringifyMetadata(entry.metadata), ]) }) }) return rows } function buildServiceRows(machines: MachineInventoryRecord[]): WorksheetRow[] { const rows: WorksheetRow[] = [] machines.forEach((machine) => { const inventory = toRecord(machine.inventory) const services = extractServiceEntries(machine.hostname, inventory) services.forEach((service) => { rows.push([ service.hostname, service.name ?? "—", service.displayName ?? "—", service.status ?? "—", service.origin, ]) }) }) return rows } function buildAlertRows(machines: MachineInventoryRecord[]): WorksheetRow[] { const rows: WorksheetRow[] = [] machines.forEach((machine) => { const alerts = machine.postureAlerts ?? [] alerts.forEach((alert) => { const record = toRecord(alert) if (!record) return rows.push([ machine.hostname, pickString(record, ["kind", "Kind", "type"]) ?? "—", pickString(record, ["message", "Message", "description"]) ?? "—", pickString(record, ["severity", "Severity"]) ?? "—", formatDateTime( parseDateish(record["createdAt"]) ?? parseDateish(record["timestamp"]) ?? parseDateish(record["created"]) ?? null, ) ?? "—", ]) }) }) return rows } function buildMetricsRows(machines: MachineInventoryRecord[]): WorksheetRow[] { const rows: WorksheetRow[] = [] machines.forEach((machine) => { const inventory = toRecord(machine.inventory) const metricsRow = deriveMachineMetrics(machine, inventory) if (metricsRow) { rows.push(metricsRow) } }) return rows } function buildLabelRows(machines: MachineInventoryRecord[]): WorksheetRow[] { const rows: WorksheetRow[] = [] machines.forEach((machine) => { const inventory = toRecord(machine.inventory) const labels = extractLabels(inventory) labels.forEach((label) => rows.push([machine.hostname, label])) }) return rows } function buildSystemRows(machines: MachineInventoryRecord[]): WorksheetRow[] { const rows: WorksheetRow[] = [] machines.forEach((machine) => { const inventory = toRecord(machine.inventory) const hardware = extractHardware(inventory) const systemInfo = extractSystemInfo(inventory) const collaborator = extractCollaborator(machine, inventory) const fleetInfo = extractFleetInfo(inventory) const push = (category: string, field: string, value: unknown) => { const display = value === null || value === undefined || value === "" ? "—" : value rows.push([machine.hostname, category, field, display]) } push("Sistema", "Sistema operacional", machine.osName ?? "—") push("Sistema", "Versão", machine.osVersion ?? "—") if (systemInfo.windowsEdition) push("Sistema", "Edição", systemInfo.windowsEdition) if (systemInfo.osBuild) push("Sistema", "Build", systemInfo.osBuild) if (systemInfo.experience) push("Sistema", "Experiência", systemInfo.experience) push("Sistema", "Licença", systemInfo.license ?? "—") if (systemInfo.installDate) { push("Sistema", "Instalação", formatDateTime(systemInfo.installDate) ?? "—") } push("Dispositivo", "Nome do dispositivo", systemInfo.deviceName ?? machine.hostname ?? "—") push("Dispositivo", "Domínio", systemInfo.domain ?? "—") push("Dispositivo", "Grupo de trabalho", systemInfo.workgroup ?? "—") push("Dispositivo", "Fabricante", hardware.vendor ?? systemInfo.systemManufacturer ?? "—") push("Dispositivo", "Modelo", hardware.model ?? systemInfo.systemModel ?? "—") push("Dispositivo", "Serial hardware", hardware.serial ?? systemInfo.boardSerial ?? "—") push("Dispositivo", "MACs", machine.macAddresses.join(", ") || "—") push("Dispositivo", "Seriais", machine.serialNumbers.join(", ") || "—") push("Hardware", "Processador", hardware.cpuType ?? "—") push("Hardware", "Cores físicas", hardware.physicalCores ?? "—") push("Hardware", "Cores lógicas", hardware.logicalCores ?? "—") push("Hardware", "Memória", hardware.memoryGiB ?? "—") push("Hardware", "GPUs", extractGpuNames(inventory).join(", ") || "—") push("Acesso", "Persona", describePersona(machine.persona)) push("Acesso", "Responsável", machine.assignedUserName ?? "—") push("Acesso", "Responsável (e-mail)", machine.assignedUserEmail ?? "—") if (collaborator) { push("Acesso", "Colaborador (nome)", collaborator.name ?? "—") push("Acesso", "Colaborador (e-mail)", collaborator.email ?? "—") } push("Acesso", "Autenticação", machine.authEmail ?? "—") push("Acesso", "Registrada via", machine.registeredBy ?? "—") push("Token", "Expira em", machine.token?.expiresAt ? formatDateTime(machine.token.expiresAt) ?? "—" : "—") push("Token", "Último uso", machine.token?.lastUsedAt ? formatDateTime(machine.token.lastUsedAt) ?? "—" : "—") push("Token", "Uso total", machine.token?.usageCount ?? 0) push("Status", "Ativa", yesNo(machine.isActive)) push("Status", "Status atual", describeStatus(machine.status)) push("Status", "Último heartbeat", formatDateTime(machine.lastHeartbeatAt) ?? "—") push("Status", "Criada em", formatDateTime(machine.createdAt) ?? "—") push("Status", "Atualizada em", formatDateTime(machine.updatedAt) ?? "—") const remoteCount = collectRemoteAccessEntries(machine).length push("Acessos remotos", "Total", remoteCount) if (fleetInfo) { push("Fleet", "ID", fleetInfo.id ?? "—") push("Fleet", "Equipe", fleetInfo.team ?? "—") push("Fleet", "Atualizado em", fleetInfo.updatedAt ? formatDateTime(fleetInfo.updatedAt) ?? "—" : "—") } }) return rows } function toRecord(value: unknown): Record | null { if (value && typeof value === "object" && !Array.isArray(value)) { return value as Record } return null } function toRecordArray(value: unknown): Record[] { if (Array.isArray(value)) { return value.map(toRecord).filter((item): item is Record => Boolean(item)) } const record = toRecord(value) return record ? [record] : [] } function ensureString(value: unknown): string | null { if (typeof value === "string") { const trimmed = value.trim() return trimmed.length ? trimmed : null } if (typeof value === "number" && Number.isFinite(value)) { return String(value) } return null } function ensureNumber(value: unknown): number | null { if (typeof value === "number" && Number.isFinite(value)) { return value } if (typeof value === "string") { const parsed = Number(value) return Number.isFinite(parsed) ? parsed : null } return null } function pickValue(record: Record | null | undefined, keys: string[]): unknown { if (!record) return undefined for (const key of keys) { if (key in record) { const value = record[key] if (value !== undefined && value !== null && value !== "") { return value } } } return undefined } function pickString(record: Record | null | undefined, keys: string[]): string | null { return ensureString(pickValue(record, keys)) } function pickNumber(record: Record | null | undefined, keys: string[]): number | null { return ensureNumber(pickValue(record, keys)) } function pickRecord(record: Record | null | undefined, keys: string[]): Record | null { return toRecord(pickValue(record, keys)) } function pickRecordArray(record: Record | null | undefined, keys: string[]): Record[] { return toRecordArray(pickValue(record, keys)) } function pickArray(record: Record | null | undefined, keys: string[]): unknown[] { const value = pickValue(record, keys) if (Array.isArray(value)) return value if (value === undefined || value === null) return [] return [value] } function describeStatus(status: string | null | undefined): string { if (!status) return STATUS_LABELS.unknown const normalized = status.toLowerCase() return STATUS_LABELS[normalized] ?? status } function describePersona(persona: string | null | undefined): string { if (!persona) return "—" const normalized = persona.toLowerCase() return PERSONA_LABELS[normalized] ?? persona } function yesNo(value: boolean | null | undefined): string { if (value === undefined || value === null) return "—" return value ? "Sim" : "Não" } function formatDateTime(value: number | null | undefined): string | null { if (typeof value !== "number" || Number.isNaN(value)) return null const date = new Date(value) const yyyy = date.getUTCFullYear() const mm = `${date.getUTCMonth() + 1}`.padStart(2, "0") const dd = `${date.getUTCDate()}`.padStart(2, "0") const hh = `${date.getUTCHours()}`.padStart(2, "0") const min = `${date.getUTCMinutes()}`.padStart(2, "0") return `${yyyy}-${mm}-${dd} ${hh}:${min} UTC` } function extractHardware(inventory: Record | null) { if (!inventory) { return { vendor: null, model: null, serial: null, cpuType: null, physicalCores: null, logicalCores: null, memoryGiB: null, memoryBytes: null, } } const hardware = pickRecord(inventory, ["hardware", "Hardware"]) const vendor = pickString(hardware, ["vendor", "Vendor", "manufacturer", "Manufacturer", "systemManufacturer", "SystemManufacturer"]) ?? null const model = pickString(hardware, ["model", "Model", "product", "ProductName", "productName", "SystemProductName"]) ?? null const serial = pickString(hardware, ["serial", "SerialNumber", "Serial"]) const cpuType = pickString(hardware, ["cpuType", "cpu", "processor", "name", "model"]) const physicalCores = pickNumber(hardware, ["physicalCores", "PhysicalCores", "cores", "Cores"]) const logicalCores = pickNumber(hardware, ["logicalCores", "LogicalCores", "threads", "Threads"]) const memoryBytes = parseBytesLike(pickValue(hardware, ["memoryBytes", "MemoryBytes", "totalMemory", "TotalMemory"])) ?? parseBytesLike(pickValue(hardware, ["memory"])) ?? parseBytesLike(pickValue(hardware, ["ram"])) ?? null const memoryGiB = memoryBytes !== null ? formatBytesValue(memoryBytes) : null return { vendor, model, serial, cpuType, physicalCores, logicalCores, memoryGiB, memoryBytes, } } function extractPrimaryIp(inventory: Record | null): string | null { if (!inventory) return null const network = pickRecord(inventory, ["network", "Network"]) const direct = pickString(network, ["primaryIp", "PrimaryIp", "ip", "address", "PrimaryAddress"]) if (direct) return direct const networkArray = pickRecordArray(inventory, ["network", "Network"]) for (const entry of networkArray) { const ip = pickString(entry, ["ip", "IP", "address", "primaryIp", "PrimaryIp"]) if (ip) return ip } return null } function extractPublicIp(inventory: Record | null): string | null { if (!inventory) return null const network = pickRecord(inventory, ["network", "Network"]) return pickString(network, ["publicIp", "PublicIp", "externalIp", "ExternalIp"]) } function extractGpuNames(inventory: Record | null): string[] { if (!inventory) return [] const names = new Set() const hardware = pickRecord(inventory, ["hardware", "Hardware"]) const primaryGpu = pickRecord(hardware, ["primaryGpu", "primarygpu", "PrimaryGpu"]) if (primaryGpu) { const name = pickString(primaryGpu, ["name", "Name", "model", "Model", "GPUName", "gpuName"]) if (name) names.add(name) } const gpuArray = pickArray(hardware, ["gpus", "GPUs"]) if (gpuArray.length > 0) { gpuArray.forEach((gpu) => { const record = toRecord(gpu) const name = pickString(record, ["name", "Name", "model", "Model", "GPUName", "gpuName"]) if (name) names.add(name) }) } const extended = pickRecord(inventory, ["extended", "Extended"]) const windows = pickRecord(extended, ["windows", "Windows"]) const videoControllers = pickRecordArray(windows, ["videoControllers", "VideoControllers"]) videoControllers.forEach((controller) => { const name = pickString(controller, ["Name", "name", "Caption", "Model"]) if (name) names.add(name) }) return Array.from(names) } function extractLabels(inventory: Record | null): string[] { if (!inventory) return [] const labels = inventory["labels"] if (!labels) return [] if (Array.isArray(labels)) { return labels .map((label) => { if (typeof label === "string") return label.trim() const record = toRecord(label) if (!record) return null return pickString(record, ["name", "value", "label", "Name"]) }) .filter((item): item is string => Boolean(item)) } const record = toRecord(labels) const name = pickString(record, ["name", "label", "value"]) return name ? [name] : [] } function summarizeLinkedUsers(users: LinkedUser[] | undefined): string | null { if (!users || users.length === 0) return null const parts = users.map((user) => { const name = user.name ?? user.email ?? "" if (user.email && user.email !== name) { return `${name} <${user.email}>` } return name }) return parts.join("; ") } function clampPercent(value: number): number { if (Number.isNaN(value)) return value return Math.max(0, Math.min(100, value)) } function parseBytesLike(raw: unknown): number | null { if (raw === null || raw === undefined) return null if (typeof raw === "number" && Number.isFinite(raw)) { if (raw > 10_000 && raw < 10_000_000) { return raw * 1024 } return raw } if (typeof raw === "string") { const trimmed = raw.trim() if (!trimmed) return null const numeric = Number(trimmed) if (Number.isFinite(numeric)) { return numeric } const match = trimmed.match(/^([-+]?[0-9]*\.?[0-9]+)\s*([kmgtp]?i?b)?/i) if (match) { const value = Number(match[1]) if (!Number.isFinite(value)) return null const unit = (match[2] ?? "").toLowerCase() const multipliers: Record = { b: 1, kb: 1000, kib: 1024, mb: 1000 ** 2, mib: 1024 ** 2, gb: 1000 ** 3, gib: 1024 ** 3, tb: 1000 ** 4, tib: 1024 ** 4, pb: 1000 ** 5, pib: 1024 ** 5, } const multiplier = multipliers[unit] ?? 1 return value * multiplier } } return null } function formatBytesValue(bytes: number | null | undefined): string { if (bytes === null || bytes === undefined || Number.isNaN(bytes) || bytes < 0) return "—" const units = ["B", "KB", "MB", "GB", "TB", "PB"] let value = bytes let unitIndex = 0 while (value >= 1024 && unitIndex < units.length - 1) { value /= 1024 unitIndex += 1 } const fixed = value >= 10 || value % 1 === 0 ? value.toFixed(0) : value.toFixed(1) return `${fixed} ${units[unitIndex]}` } function formatPercentValue(value: number | null | undefined): string { if (value === null || value === undefined || Number.isNaN(value)) return "—" return `${clampPercent(value).toFixed(0)}%` } function parseDateish(value: unknown): number | null { if (!value) return null if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value.getTime() if (typeof value === "number" && Number.isFinite(value)) { if (value > 10_000_000_000) return value if (value > 1_000_000_000) return value * 1000 if (value > 0) return value * 1000 return null } if (typeof value !== "string") return null const trimmed = value.trim() if (!trimmed) return null const numeric = Number(trimmed) if (Number.isFinite(numeric)) return parseDateish(numeric) const iso = Date.parse(trimmed) if (!Number.isNaN(iso)) return iso const digits = trimmed.replace(/[^0-9]/g, "") if (digits.length >= 8) { const yyyy = Number(digits.slice(0, 4)) const mm = Number(digits.slice(4, 6)) const dd = Number(digits.slice(6, 8)) const hh = Number(digits.slice(8, 10) || "0") const mi = Number(digits.slice(10, 12) || "0") const ss = Number(digits.slice(12, 14) || "0") if (yyyy > 1900 && mm >= 1 && mm <= 12 && dd >= 1 && dd <= 31) { const parsed = Date.UTC(yyyy, mm - 1, dd, hh, mi, ss) if (!Number.isNaN(parsed)) return parsed } } return null } function stringifyMetadata(metadata: Record | null | undefined): string { if (!metadata || Object.keys(metadata).length === 0) return "—" try { return JSON.stringify(metadata) } catch { return String(metadata) } } type RemoteAccessNormalized = { provider: string | null identifier: string | null username: string | null password: string | null url: string | null notes: string | null lastVerifiedAt: number | null origin: string metadata: Record | null } function normalizeRemoteAccessEntry( value: unknown, origin: string, providerHint?: string, ): RemoteAccessNormalized | null { if (value === null || value === undefined) return null if (typeof value === "string") { const trimmed = value.trim() if (!trimmed) return null const isUrl = /^https?:\/\//i.test(trimmed) return { provider: providerHint ?? null, identifier: isUrl ? null : trimmed, username: null, password: null, url: isUrl ? trimmed : null, notes: null, lastVerifiedAt: null, origin, metadata: null, } } const record = toRecord(value) if (!record) return null const provider = ensureString(record["provider"]) ?? ensureString(record["tool"]) ?? ensureString(record["vendor"]) ?? ensureString(record["name"]) ?? providerHint ?? null const identifier = ensureString(record["identifier"]) ?? ensureString(record["code"]) ?? ensureString(record["id"]) ?? ensureString(record["accessId"]) ?? ensureString(record["value"]) ?? ensureString(record["label"]) ?? null const username = ensureString(record["username"]) ?? ensureString(record["user"]) ?? ensureString(record["login"]) ?? ensureString(record["email"]) ?? ensureString(record["account"]) ?? null const password = ensureString(record["password"]) ?? ensureString(record["pass"]) ?? ensureString(record["secret"]) ?? ensureString(record["pin"]) ?? null const url = ensureString(record["url"]) ?? ensureString(record["link"]) ?? ensureString(record["remoteUrl"]) ?? ensureString(record["console"]) ?? ensureString(record["viewer"]) ?? null const notes = ensureString(record["notes"]) ?? ensureString(record["note"]) ?? ensureString(record["description"]) ?? ensureString(record["obs"]) ?? null const lastVerifiedRaw = record["lastVerifiedAt"] ?? record["verifiedAt"] ?? record["checkedAt"] ?? record["updatedAt"] ?? record["timestamp"] const lastVerifiedAt = parseDateish(lastVerifiedRaw) const metadata = Object.keys(record).length > 0 ? record : null if (!identifier && !url && !provider) { return null } return { provider, identifier, username, password, url, notes, lastVerifiedAt, origin, metadata, } } function collectRemoteAccessEntries(machine: MachineInventoryRecord): RemoteAccessNormalized[] { const raw = machine.remoteAccess const entries: RemoteAccessNormalized[] = [] const push = (entry: RemoteAccessNormalized | null) => { if (entry) entries.push(entry) } const handleValue = (value: unknown, origin: string, providerHint?: string) => { if (Array.isArray(value)) { value.forEach((item) => push(normalizeRemoteAccessEntry(item, origin, providerHint))) return } const record = toRecord(value) if (!record) { push(normalizeRemoteAccessEntry(value, origin, providerHint)) return } const keys = Object.keys(record) const looksLikeEntry = ["provider", "identifier", "url", "notes", "id", "value"].some((key) => key in record) if (looksLikeEntry) { push(normalizeRemoteAccessEntry(record, origin, providerHint)) return } if ("entries" in record && Array.isArray(record.entries)) { record.entries.forEach((item) => push(normalizeRemoteAccessEntry(item, `${origin}.entries`, providerHint))) return } keys.forEach((key) => { handleValue(record[key], `${origin}.${key}`, key) }) } handleValue(raw, "machine.remoteAccess") return entries.filter( (entry, index, array) => array.findIndex( (other) => (other.provider ?? "").toLowerCase() === (entry.provider ?? "").toLowerCase() && (other.identifier ?? "").toLowerCase() === (entry.identifier ?? "").toLowerCase() && (other.url ?? "").toLowerCase() === (entry.url ?? "").toLowerCase(), ) === index, ) } type CollaboratorInfo = { name: string | null; email: string | null } function extractCollaborator(machine: MachineInventoryRecord, inventory: Record | null): CollaboratorInfo | null { if (machine.assignedUserEmail || machine.assignedUserName) { return { name: machine.assignedUserName ?? null, email: machine.assignedUserEmail ?? null, } } const collab = pickRecord(inventory, ["collaborator", "Collaborator"]) if (!collab) return null const email = pickString(collab, ["email", "Email"]) const name = pickString(collab, ["name", "Name"]) if (!email && !name) return null return { name: name ?? null, email: email ?? null, } } type FleetInfo = { id: string | null; team: string | null; updatedAt: number | null } function extractFleetInfo(inventory: Record | null): FleetInfo | null { const fleet = pickRecord(inventory, ["fleet", "Fleet"]) if (!fleet) return null const id = ensureString(fleet["id"]) ?? ensureString(fleet["fleetId"]) ?? ensureString(fleet["teamId"]) const team = ensureString(fleet["teamId"]) ?? ensureString(fleet["team"]) ?? ensureString(fleet["group"]) const updatedAt = parseDateish(fleet["detailUpdatedAt"] ?? fleet["updatedAt"]) if (!id && !team && !updatedAt) return null return { id: id ?? null, team: team ?? null, updatedAt: updatedAt ?? null, } } type SystemInfo = { osBuild: string | null license: string | null experience: string | null domain: string | null workgroup: string | null deviceName: string | null systemManufacturer: string | null systemModel: string | null boardSerial: string | null installDate: number | null windowsEdition: string | null } function extractSystemInfo(inventory: Record | null): SystemInfo { const extended = pickRecord(inventory, ["extended", "Extended"]) const windows = pickRecord(extended, ["windows", "Windows"]) const osInfo = pickRecord(windows, ["osInfo", "OSInfo", "OsInfo"]) const computerSystem = pickRecord(windows, ["computerSystem", "ComputerSystem"]) const baseboard = pickRecord(windows, ["baseboard", "Baseboard"]) const baseBuild = pickString(osInfo, ["CurrentBuildNumber", "currentBuildNumber", "OSBuild", "osBuild", "BuildNumber", "buildNumber"]) ?? pickString(osInfo, ["CurrentBuild", "currentBuild"]) const ubr = pickString(osInfo, ["UBR", "ubr"]) const osBuild = baseBuild ? (ubr && /^\d+$/.test(ubr) ? `${baseBuild}.${ubr}` : baseBuild) : null const licenseStatusDescription = pickString(osInfo, ["LicenseStatusDescription", "licenseStatusDescription", "StatusDescription", "statusDescription"]) ?? null const licenseStatusNumber = pickNumber(osInfo, ["LicenseStatus", "licenseStatus"]) const licenseBoolRaw = pickValue(osInfo, ["IsActivated", "isActivated", "IsLicensed", "isLicensed"]) const licenseBool = typeof licenseBoolRaw === "boolean" ? licenseBoolRaw : typeof licenseBoolRaw === "number" ? licenseBoolRaw === 1 : typeof licenseBoolRaw === "string" ? ["1", "true", "licensed", "ativado", "activated"].includes(licenseBoolRaw.toLowerCase()) : undefined const license = licenseBool === true ? "Ativada" : licenseBool === false ? "Não ativada" : licenseStatusNumber === 1 ? "Ativada" : licenseStatusDescription ?? null const experience = pickString(osInfo, ["Experience", "experience"]) ?? ((() => { const pack = pickString(osInfo, ["FeatureExperiencePack", "featureExperiencePack"]) if (pack) return `Feature Experience Pack ${pack}` return null })()) const domain = pickString(computerSystem, ["Domain", "domain"]) const workgroup = pickString(computerSystem, ["Workgroup", "workgroup"]) const deviceName = pickString(osInfo, ["ComputerName", "computerName"]) ?? pickString(computerSystem, ["DNSHostName", "dnsHostName", "Name", "name"]) const systemManufacturer = pickString(computerSystem, ["Manufacturer", "manufacturer", "SystemManufacturer", "systemManufacturer"]) ?? pickString(osInfo, ["Manufacturer", "manufacturer"]) const systemModel = pickString(computerSystem, ["Model", "model", "SystemProductName", "systemProductName"]) ?? pickString(osInfo, ["Model", "model"]) const boardSerial = pickString(baseboard, ["SerialNumber", "serialNumber", "Serial"]) ?? pickString(computerSystem, ["SystemSKUNumber", "systemSKUNumber"]) const installDate = parseDateish(osInfo?.["InstallDate"]) ?? parseDateish(osInfo?.["InstallationDate"]) ?? parseDateish(osInfo?.["InstalledOn"]) const edition = pickString(osInfo, ["ProductName", "productName"]) ?? pickString(osInfo, ["Caption", "caption"]) ?? pickString(osInfo, ["EditionID", "editionId"]) return { osBuild, license, experience, domain, workgroup, deviceName, systemManufacturer, systemModel, boardSerial, installDate, windowsEdition: edition, } } type SoftwareEntryInternal = SoftwareEntry function extractSoftwareEntries(hostname: string, inventory: Record | null): SoftwareEntryInternal[] { if (!inventory) return [] const map = new Map() const push = (record: Record | null) => { if (!record) return const name = pickString(record, ["DisplayName", "displayName", "Name", "name", "Title", "title"]) if (!name) return const version = pickString(record, ["DisplayVersion", "displayVersion", "Version", "version"]) const source = pickString(record, ["ParentDisplayName", "parent", "Source", "source", "SystemComponent", "PublisherURL"]) ?? null const publisher = pickString(record, ["Publisher", "publisher", "Vendor", "vendor"]) const installedRaw = pickValue(record, ["InstalledOn", "installedOn", "InstallDateUTC", "InstallDate", "installDate", "InstallTime"]) ?? null const installedTs = parseDateish(installedRaw) const installedOn = installedTs !== null ? formatDateTime(installedTs) ?? null : typeof installedRaw === "string" && installedRaw.trim().length > 0 ? installedRaw.trim() : null const entry: SoftwareEntryInternal = { hostname, name, version: version ?? null, source, publisher: publisher ?? null, installedOn, } const key = [ hostname.toLowerCase(), name.toLowerCase(), (version ?? "").toLowerCase(), (source ?? "").toLowerCase(), (publisher ?? "").toLowerCase(), (installedOn ?? "").toLowerCase(), ].join("|") if (!map.has(key)) { map.set(key, entry) } } const direct = inventory["software"] if (Array.isArray(direct)) { direct.map(toRecord).forEach(push) } else if (direct) { push(toRecord(direct)) } const extended = pickRecord(inventory, ["extended", "Extended"]) const windows = pickRecord(extended, ["windows", "Windows"]) if (windows) { const software = windows["software"] ?? windows["installedPrograms"] ?? windows["InstalledPrograms"] if (Array.isArray(software)) { software.map(toRecord).forEach(push) } else { push(toRecord(software)) } } const linux = pickRecord(extended, ["linux", "Linux"]) if (linux) { const packages = linux["packages"] if (Array.isArray(packages)) { packages.forEach((pkg) => { if (typeof pkg === "string") { push({ name: pkg } as Record) } else { push(toRecord(pkg)) } }) } } const macos = pickRecord(extended, ["macos", "MacOS", "macOS"]) if (macos) { const packages = macos["packages"] if (Array.isArray(packages)) { packages.forEach((pkg) => push(toRecord(pkg))) } } return Array.from(map.values()).sort((a, b) => { const nameCompare = a.name.localeCompare(b.name, "pt-BR") if (nameCompare !== 0) return nameCompare return (a.version ?? "").localeCompare(b.version ?? "", "pt-BR") }) } type PartitionEntry = { hostname: string name: string | null mount: string | null fs: string | null capacityBytes: number | null freeBytes: number | null interface: string | null serial: string | null origin: string } function extractPartitionEntries(hostname: string, inventory: Record | null): PartitionEntry[] { const entries: PartitionEntry[] = [] if (!inventory) return entries const push = (value: unknown, origin: string) => { const record = toRecord(value) if (!record) return const name = pickString(record, ["name", "Name", "VolumeName", "label", "Label"]) ?? pickString(record, ["DeviceID", "deviceId"]) const mount = pickString(record, ["mountPoint", "MountPoint", "mount", "Mount", "path", "Path"]) ?? pickString(record, ["DeviceID", "deviceId"]) const fs = pickString(record, ["fs", "FS", "filesystem", "FileSystem", "FileSystemType", "type"]) const capacityBytes = parseBytesLike(record["totalBytes"]) ?? parseBytesLike(record["sizeBytes"]) ?? parseBytesLike(record["Size"]) ?? parseBytesLike(record["TotalSpace"]) ?? parseBytesLike(record["Capacity"]) const freeBytes = parseBytesLike(record["availableBytes"]) ?? parseBytesLike(record["freeBytes"]) ?? parseBytesLike(record["FreeSpace"]) ?? parseBytesLike(record["available"]) ?? parseBytesLike(record["Free"]) const interfaceType = pickString(record, ["interface", "Interface", "interfaceType", "InterfaceType"]) const serial = pickString(record, ["serial", "Serial", "SerialNumber"]) entries.push({ hostname, name: name ?? mount ?? null, mount: mount ?? null, fs: fs ?? null, capacityBytes: capacityBytes ?? null, freeBytes: freeBytes ?? null, interface: interfaceType ?? null, serial: serial ?? null, origin, }) } const direct = inventory["disks"] if (Array.isArray(direct)) { direct.forEach((item) => push(item, "inventory.disks")) } else if (direct) { push(direct, "inventory.disks") } const extended = pickRecord(inventory, ["extended", "Extended"]) const windows = pickRecord(extended, ["windows", "Windows"]) if (windows) { const logical = windows["logicalDisks"] ?? windows["LogicalDisks"] ?? windows["volumes"] ?? windows["Volumes"] if (Array.isArray(logical)) { logical.forEach((item) => push(item, "extended.windows.logicalDisks")) } else if (logical) { push(logical, "extended.windows.logicalDisks") } } const linux = pickRecord(extended, ["linux", "Linux"]) if (linux) { const lsblk = linux["lsblk"] if (Array.isArray(lsblk)) { lsblk.forEach((item) => push(item, "extended.linux.lsblk")) } else if (lsblk) { push(lsblk, "extended.linux.lsblk") } } const macos = pickRecord(extended, ["macos", "MacOS", "macOS"]) if (macos) { const volumes = macos["volumes"] if (Array.isArray(volumes)) { volumes.forEach((item) => push(item, "extended.macos.volumes")) } else if (volumes) { push(volumes, "extended.macos.volumes") } } const dedup = new Map() entries.forEach((entry) => { const key = [ entry.hostname.toLowerCase(), (entry.name ?? "").toLowerCase(), (entry.mount ?? "").toLowerCase(), (entry.fs ?? "").toLowerCase(), ].join("|") if (!dedup.has(key)) { dedup.set(key, entry) } }) return Array.from(dedup.values()) } type PhysicalDiskEntry = { hostname: string model: string | null sizeBytes: number | null interface: string | null mediaType: string | null serial: string | null origin: string } function extractPhysicalDiskEntries(hostname: string, inventory: Record | null): PhysicalDiskEntry[] { const entries: PhysicalDiskEntry[] = [] if (!inventory) return entries const push = (value: unknown, origin: string) => { const record = toRecord(value) if (!record) return const model = pickString(record, ["Model", "model", "Caption", "DeviceID"]) const serial = pickString(record, ["SerialNumber", "serialNumber", "Serial"]) ?? pickString(record, ["DeviceID", "deviceId"]) const interfaceType = pickString(record, ["InterfaceType", "interfaceType", "BusType"]) const mediaType = pickString(record, ["MediaType", "mediaType", "Media"]) ?? pickString(record, ["Type", "type"]) const sizeBytes = parseBytesLike(record["Size"]) ?? parseBytesLike(record["sizeBytes"]) ?? parseBytesLike(record["TotalSize"]) ?? parseBytesLike(record["Capacity"]) entries.push({ hostname, model: model ?? null, sizeBytes: sizeBytes ?? null, interface: interfaceType ?? null, mediaType: mediaType ?? null, serial: serial ?? null, origin, }) } const extended = pickRecord(inventory, ["extended", "Extended"]) const windows = pickRecord(extended, ["windows", "Windows"]) if (windows) { const disks = windows["disks"] ?? windows["Disks"] if (Array.isArray(disks)) { disks.forEach((item) => push(item, "extended.windows.disks")) } else if (disks) { push(disks, "extended.windows.disks") } const drives = windows["diskDrives"] ?? windows["DiskDrives"] if (Array.isArray(drives)) { drives.forEach((item) => push(item, "extended.windows.diskDrives")) } else if (drives) { push(drives, "extended.windows.diskDrives") } } const hardware = pickRecord(inventory, ["hardware", "Hardware"]) const storage = pickArray(hardware, ["storage", "Storage"]) storage.forEach((item) => push(item, "inventory.hardware.storage")) const linux = pickRecord(extended, ["linux", "Linux"]) if (linux) { const disks = linux["disks"] if (Array.isArray(disks)) { disks.forEach((item) => push(item, "extended.linux.disks")) } else if (disks) { push(disks, "extended.linux.disks") } } const dedup = new Map() entries.forEach((entry) => { const key = [ entry.hostname.toLowerCase(), (entry.serial ?? "").toLowerCase(), (entry.model ?? "").toLowerCase(), ].join("|") if (!dedup.has(key)) { dedup.set(key, entry) } }) return Array.from(dedup.values()) } type NetworkEntry = { hostname: string; name: string | null; mac: string | null; address: string | null; origin: string } function extractNetworkEntries(hostname: string, inventory: Record | null): NetworkEntry[] { const entries: NetworkEntry[] = [] if (!inventory) return entries const push = (value: unknown, origin: string, nameHint?: string) => { const record = toRecord(value) if (!record) return const mac = pickString(record, ["mac", "MAC", "MacAddress", "MACAddress", "addressMac"]) ?? (Array.isArray(record["MACAddress"]) ? record["MACAddress"][0] : null) const ipValue = pickString(record, ["ip", "IP", "address", "Address", "ipv4", "IPv4"]) ?? (Array.isArray(record["IPAddress"]) ? (record["IPAddress"] as unknown[]).map((addr) => ensureString(addr)).filter(Boolean)?.[0] ?? null : null) const name = pickString(record, ["name", "Name", "Interface", "InterfaceDescription", "AdapterName"]) ?? nameHint ?? null entries.push({ hostname, name, mac: mac ?? null, address: ipValue ?? null, origin, }) } const network = inventory["network"] if (Array.isArray(network)) { network.forEach((item) => push(item, "inventory.network")) } else if (network && typeof network === "object") { push(network, "inventory.network.summary") const macAddresses = (network as Record)["macAddresses"] if (Array.isArray(macAddresses)) { macAddresses .map((addr) => ensureString(addr)) .filter(Boolean) .forEach((addr) => entries.push({ hostname, name: null, mac: addr ?? null, address: null, origin: "inventory.network.macAddresses", }), ) } } const extended = pickRecord(inventory, ["extended", "Extended"]) const windows = pickRecord(extended, ["windows", "Windows"]) if (windows) { const adapters = windows["networkAdapters"] ?? windows["NetworkAdapters"] ?? windows["networkInterfaces"] if (Array.isArray(adapters)) { adapters.forEach((adapter) => push(adapter, "extended.windows.networkAdapters")) } else if (adapters) { push(adapters, "extended.windows.networkAdapters") } } const linux = pickRecord(extended, ["linux", "Linux"]) if (linux) { const interfaces = linux["networkInterfaces"] if (Array.isArray(interfaces)) { interfaces.forEach((iface) => push(iface, "extended.linux.networkInterfaces")) } else if (interfaces) { push(interfaces, "extended.linux.networkInterfaces") } } const dedup = new Map() entries.forEach((entry) => { const key = [ entry.hostname.toLowerCase(), (entry.name ?? "").toLowerCase(), (entry.mac ?? "").toLowerCase(), (entry.address ?? "").toLowerCase(), entry.origin, ].join("|") if (!dedup.has(key)) { dedup.set(key, entry) } }) return Array.from(dedup.values()) } type ServiceEntry = { hostname: string; name: string | null; displayName: string | null; status: string | null; origin: string } function extractServiceEntries(hostname: string, inventory: Record | null): ServiceEntry[] { const entries: ServiceEntry[] = [] if (!inventory) return entries const push = (value: unknown, origin: string) => { const record = toRecord(value) if (!record) return const name = pickString(record, ["Name", "name", "ServiceName", "serviceName"]) const displayName = pickString(record, ["DisplayName", "displayName", "Description"]) const status = pickString(record, ["Status", "status", "State", "state"]) if (!name && !displayName) return entries.push({ hostname, name: name ?? displayName ?? null, displayName: displayName ?? null, status: status ?? null, origin, }) } const direct = inventory["services"] if (Array.isArray(direct)) { direct.forEach((svc) => push(svc, "inventory.services")) } else if (direct) { push(direct, "inventory.services") } const extended = pickRecord(inventory, ["extended", "Extended"]) const windows = pickRecord(extended, ["windows", "Windows"]) if (windows) { const services = windows["services"] ?? windows["Services"] if (Array.isArray(services)) { services.forEach((svc) => push(svc, "extended.windows.services")) } else if (services) { push(services, "extended.windows.services") } } const linux = pickRecord(extended, ["linux", "Linux"]) if (linux) { const services = linux["services"] if (Array.isArray(services)) { services.forEach((svc) => push(svc, "extended.linux.services")) } else if (services) { push(services, "extended.linux.services") } } const dedup = new Map() entries.forEach((entry) => { const key = [ entry.hostname.toLowerCase(), (entry.name ?? "").toLowerCase(), (entry.displayName ?? "").toLowerCase(), ].join("|") if (!dedup.has(key)) dedup.set(key, entry) }) return Array.from(dedup.values()) } function deriveMachineMetrics( machine: MachineInventoryRecord, inventory: Record | null, ): WorksheetRow | null { const metricsRecord = toRecord(machine.metrics ?? null) if (!metricsRecord) return null const capturedAt = parseDateish(metricsRecord["capturedAt"]) ?? parseDateish(metricsRecord["collectedAt"]) ?? parseDateish(metricsRecord["timestamp"]) ?? null const cpuPercentRaw = Number( metricsRecord["cpuUsagePercent"] ?? metricsRecord["cpuUsage"] ?? metricsRecord["cpu_percent"] ?? metricsRecord["cpu"], ) const cpuPercent = Number.isFinite(cpuPercentRaw) ? clampPercent(cpuPercentRaw) : NaN const memoryTotalCandidates = [ metricsRecord["memoryTotalBytes"], metricsRecord["memory_total"], metricsRecord["memory"], metricsRecord["memoryTotal"], ] .map(parseBytesLike) .filter((value): value is number => value !== null) const hardware = extractHardware(inventory) if (hardware.memoryBytes && !memoryTotalCandidates.includes(hardware.memoryBytes)) { memoryTotalCandidates.push(hardware.memoryBytes) } const memoryTotalBytes = memoryTotalCandidates.find((value) => Number.isFinite(value)) ?? null const memoryUsedCandidates = [ metricsRecord["memoryUsedBytes"], metricsRecord["memory_used"], metricsRecord["memoryUsed"], metricsRecord["memoryBytesUsed"], ] .map(parseBytesLike) .filter((value): value is number => value !== null) const memoryUsedBytes = memoryUsedCandidates.find((value) => Number.isFinite(value)) ?? null const memoryPercentCandidates = [ metricsRecord["memoryUsedPercent"], metricsRecord["memory_percent"], metricsRecord["memoryPercent"], ] .map((value) => (typeof value === "number" ? clampPercent(value) : NaN)) .filter((value) => Number.isFinite(value)) let memoryPercent = memoryPercentCandidates[0] ?? NaN let resolvedMemoryUsed = memoryUsedBytes if ((resolvedMemoryUsed === null || Number.isNaN(resolvedMemoryUsed)) && memoryTotalBytes && Number.isFinite(memoryPercent)) { resolvedMemoryUsed = (memoryPercent / 100) * memoryTotalBytes } else if (resolvedMemoryUsed !== null && memoryTotalBytes) { memoryPercent = clampPercent((resolvedMemoryUsed / memoryTotalBytes) * 100) } const partitions = extractPartitionEntries(machine.hostname, inventory) let diskTotal = 0 let diskFree = 0 partitions.forEach((partition) => { if (partition.capacityBytes) { diskTotal += partition.capacityBytes } if (partition.freeBytes) { diskFree += partition.freeBytes } }) const diskUsed = diskTotal > 0 ? Math.max(0, diskTotal - diskFree) : null const diskPercentCandidates = [ diskTotal > 0 ? clampPercent((diskUsed ?? 0) / diskTotal * 100) : NaN, Number(metricsRecord["diskUsagePercent"]), Number(metricsRecord["diskUsedPercent"]), Number(metricsRecord["diskUsage"]), ].filter((value) => Number.isFinite(value)) const diskPercent = diskPercentCandidates[0] ?? NaN const gpuPercentCandidates = [ Number(metricsRecord["gpuUsagePercent"]), Number(metricsRecord["gpuUsage"]), Number(metricsRecord["gpu_percent"]), Number(metricsRecord["gpu"]), ].filter((value) => Number.isFinite(value)) const gpuPercent = gpuPercentCandidates[0] ?? NaN if ( Number.isNaN(cpuPercent) && resolvedMemoryUsed === null && memoryTotalBytes === null && Number.isNaN(memoryPercent) && diskUsed === null && diskTotal === 0 && Number.isNaN(gpuPercent) ) { return null } return [ machine.hostname, capturedAt ? formatDateTime(capturedAt) ?? "—" : "—", Number.isNaN(cpuPercent) ? "—" : `${cpuPercent.toFixed(0)}%`, formatBytesValue(resolvedMemoryUsed), formatBytesValue(memoryTotalBytes), Number.isNaN(memoryPercent) ? "—" : `${memoryPercent.toFixed(0)}%`, formatBytesValue(diskUsed), formatBytesValue(diskTotal || null), Number.isNaN(diskPercent) ? "—" : `${diskPercent.toFixed(0)}%`, Number.isNaN(gpuPercent) ? "—" : `${gpuPercent.toFixed(0)}%`, ] }