//go:build windows package client import ( "encoding/json" "os" "os/user" "strings" ) const sysCheckWindowsScript = ` $ErrorActionPreference = 'SilentlyContinue' $o = [ordered]@{} # Identity try { $cs = Get-CimInstance Win32_ComputerSystem $o.manufacturer = $cs.Manufacturer $o.model = $cs.Model $o.domain = $cs.Domain $o.computer_name = $env:COMPUTERNAME $o.total_ram_gb = [math]::Round($cs.TotalPhysicalMemory / 1GB, 2) } catch {} try { $bios = Get-CimInstance Win32_BIOS $o.serial = $bios.SerialNumber $o.bios_version = $bios.SMBIOSBIOSVersion } catch {} try { $o.cpus = @(Get-CimInstance Win32_Processor | ForEach-Object { [ordered]@{ name = $_.Name cores = [int]$_.NumberOfCores logical = [int]$_.NumberOfLogicalProcessors max_mhz = [int]$_.MaxClockSpeed current_mhz = [int]$_.CurrentClockSpeed } }) } catch {} try { $o.gpus = @(Get-CimInstance Win32_VideoController | ForEach-Object { [ordered]@{ name = $_.Name driver = $_.DriverVersion vram_mb = if ($_.AdapterRAM -and $_.AdapterRAM -gt 0) { [int]($_.AdapterRAM / 1MB) } else { 0 } } }) } catch {} try { $o.disks = @(Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | ForEach-Object { $freePct = if ($_.Size -gt 0) { [int](($_.FreeSpace / $_.Size) * 100) } else { 0 } [ordered]@{ mount = $_.DeviceID label = $_.VolumeName fs_type = $_.FileSystem total_gb = [math]::Round($_.Size / 1GB, 2) free_gb = [math]::Round($_.FreeSpace / 1GB, 2) free_pct = $freePct } }) } catch {} try { $os = Get-CimInstance Win32_OperatingSystem $o.uptime_hours = [math]::Round(((Get-Date) - $os.LastBootUpTime).TotalHours, 1) $o.timezone = (Get-TimeZone).Id } catch {} try { $gw = Get-NetRoute -DestinationPrefix '0.0.0.0/0' -ErrorAction SilentlyContinue | Sort-Object RouteMetric | Select-Object -First 1 if ($gw) { $o.default_gateway = $gw.NextHop } } catch {} try { $routes = Get-NetRoute -AddressFamily IPv4 -ErrorAction SilentlyContinue | Select-Object -First 24 DestinationPrefix, NextHop, InterfaceAlias, RouteMetric | Format-Table -AutoSize | Out-String -Width 200 $o.routes_summary = $routes.Trim() } catch {} $o | ConvertTo-Json -Depth 5 -Compress ` func collectSysCheckPlatform(r *FullSysCheckReport) { out, err := silentCombinedOutput( "powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-WindowStyle", "Hidden", "-Command", sysCheckWindowsScript, ) if err != nil { r.ProbeErrors = append(r.ProbeErrors, "windows hardware probe: "+err.Error()) } else { parseWindowsSysCheckJSON(r, string(out)) } if r.Identity == nil { r.Identity = &SysCheckIdentity{} } if u, err := user.Current(); err == nil { r.Identity.Username = u.Username } r.Identity.MACAddress = primaryMACAddress() if r.Environment == nil { r.Environment = &SysCheckEnvironment{} } r.Environment.TempDir = os.Getenv("TEMP") r.Environment.HomeDir = os.Getenv("USERPROFILE") } func parseWindowsSysCheckJSON(r *FullSysCheckReport, raw string) { raw = strings.TrimSpace(raw) if idx := strings.LastIndex(raw, "{"); idx > 0 { raw = raw[idx:] } var m map[string]interface{} if json.Unmarshal([]byte(raw), &m) != nil { return } hw := &SysCheckHardware{} if v, ok := m["manufacturer"].(string); ok { hw.Manufacturer = v } if v, ok := m["model"].(string); ok { hw.Model = v } if v, ok := m["serial"].(string); ok { hw.Serial = v } if v, ok := m["bios_version"].(string); ok { hw.BIOSVersion = v } if v, ok := m["total_ram_gb"].(float64); ok { hw.MemoryGB = v } if v, ok := m["uptime_hours"].(float64); ok { hw.UptimeHours = v } hw.CPUs = parseCPUList(m["cpus"]) hw.GPUs = parseGPUList(m["gpus"]) hw.Disks = parseDiskList(m["disks"]) r.Hardware = hw if r.Identity == nil { r.Identity = &SysCheckIdentity{} } if v, ok := m["domain"].(string); ok { r.Identity.Domain = v } if v, ok := m["computer_name"].(string); ok { r.Identity.ComputerName = v } if r.Network == nil { r.Network = &SysCheckNetwork{} } if v, ok := m["default_gateway"].(string); ok { r.Network.DefaultGateway = v } if v, ok := m["routes_summary"].(string); ok { r.Network.RoutesSummary = v } if r.Environment == nil { r.Environment = &SysCheckEnvironment{} } if v, ok := m["timezone"].(string); ok { r.Environment.Timezone = v } } func parseCPUList(v interface{}) []SysCheckCPU { arr, ok := v.([]interface{}) if !ok { return nil } var out []SysCheckCPU for _, item := range arr { obj, ok := item.(map[string]interface{}) if !ok { continue } c := SysCheckCPU{Name: mapStr(obj, "name")} if n, ok := obj["cores"].(float64); ok { c.Cores = int(n) } if n, ok := obj["logical"].(float64); ok { c.Logical = int(n) } if n, ok := obj["max_mhz"].(float64); ok { c.MaxMHz = int(n) } if n, ok := obj["current_mhz"].(float64); ok { c.CurrentMHz = int(n) } out = append(out, c) } return out } func parseGPUList(v interface{}) []SysCheckGPU { arr, ok := v.([]interface{}) if !ok { return nil } var out []SysCheckGPU for _, item := range arr { obj, ok := item.(map[string]interface{}) if !ok { continue } g := SysCheckGPU{ Name: mapStr(obj, "name"), Driver: mapStr(obj, "driver"), } if n, ok := obj["vram_mb"].(float64); ok { g.VRAM_MB = int(n) } out = append(out, g) } return out } func parseDiskList(v interface{}) []SysCheckDisk { arr, ok := v.([]interface{}) if !ok { return nil } var out []SysCheckDisk for _, item := range arr { obj, ok := item.(map[string]interface{}) if !ok { continue } d := SysCheckDisk{ Mount: mapStr(obj, "mount"), Label: mapStr(obj, "label"), FSType: mapStr(obj, "fs_type"), } if n, ok := obj["total_gb"].(float64); ok { d.TotalGB = n } if n, ok := obj["free_gb"].(float64); ok { d.FreeGB = n } if n, ok := obj["free_pct"].(float64); ok { d.FreePct = int(n) } out = append(out, d) } return out } func mapStr(m map[string]interface{}, key string) string { if v, ok := m[key].(string); ok { return v } return "" }