apps/server/src/environment/ServerEnvironmentMachine.ts

import type { EnvironmentMachineKind } from "@t3tools/contracts";
import { HostProcessPlatform } from "@t3tools/shared/hostProcess";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";

import * as ProcessRunner from "../processRunner.ts";

/**
 * Best-effort hardware detection for the environment icon. Every probe is
 * allowed to fail: a null result means "no signal", and the client draws a
 * generic server until the user picks something in Settings → Connections.
 */

const DMI_ROOT = "/sys/class/dmi/id";

// SMBIOS 3.x System Enclosure types (table 17). Codes that describe a shape
// rather than a machine (docking stations, blades enclosures, IoT gateways)
// fall through to null on purpose.
const DMI_CHASSIS_KINDS: Readonly<Record<string, EnvironmentMachineKind>> = {
  "3": "desktop", // Desktop
  "4": "desktop", // Low Profile Desktop
  "5": "desktop", // Pizza Box
  "6": "desktop", // Mini Tower
  "7": "desktop", // Tower
  "8": "laptop", // Portable
  "9": "laptop", // Laptop
  "10": "laptop", // Notebook
  "13": "desktop", // All in One
  "14": "laptop", // Sub Notebook
  "15": "desktop", // Space-saving
  "16": "desktop", // Lunch Box
  "17": "server", // Main Server Chassis
  "18": "server", // Expansion Chassis
  "19": "server", // SubChassis
  "20": "server", // Bus Expansion Chassis
  "21": "server", // Peripheral Chassis
  "22": "server", // RAID Chassis
  "23": "server", // Rack Mount Chassis
  "24": "server", // Sealed-case PC
  "28": "server", // Blade
  "31": "laptop", // Convertible
  "32": "laptop", // Detachable
  "35": "desktop", // Mini PC
};

// Hypervisors and cloud providers write themselves into the DMI vendor or
// product strings; any hit means the box is a VM, and a VM reads as "cloud"
// regardless of the chassis type the hypervisor fakes. Hyper-V is matched on
// its "Virtual Machine" product, not the "Microsoft Corporation" vendor that
// physical Surface devices share.
const VIRTUALIZATION_MARKERS = [
  "qemu",
  "kvm",
  "bochs",
  "vmware",
  "virtualbox",
  "innotek",
  "xen",
  "parallels",
  "amazon ec2",
  "google compute engine",
  "digitalocean",
  "hetzner",
  "linode",
  "vultr",
  "scaleway",
  "openstack",
  "cloud",
  "virtual machine",
];

function normalize(value: string | null | undefined): string | null {
  const trimmed = value?.trim();
  return trimmed && trimmed.length > 0 ? trimmed : null;
}

/** Marketing names and Intel-era model identifiers share these prefixes. */
export function machineKindFromAppleProductName(name: string): EnvironmentMachineKind | null {
  const normalized = name.trim().toLowerCase().replaceAll(/\s+/g, "");
  if (normalized.startsWith("macmini")) return "mac-mini";
  if (normalized.startsWith("macstudio")) return "mac-studio";
  if (normalized.startsWith("macbook")) return "laptop";
  if (normalized.startsWith("imac") || normalized.startsWith("macpro")) return "desktop";
  return null;
}

export function machineKindFromDmi(input: {
  readonly chassisType: string | null;
  readonly sysVendor: string | null;
  readonly productName: string | null;
}): EnvironmentMachineKind | null {
  const productName = input.productName ?? "";
  const vendorAndProduct = `${input.sysVendor ?? ""} ${productName}`.toLowerCase();
  if (VIRTUALIZATION_MARKERS.some((marker) => vendorAndProduct.includes(marker))) {
    return "cloud";
  }
  // Apple hardware booting Linux (Asahi) still reports the Apple product name.
  const appleKind = machineKindFromAppleProductName(productName);
  if (appleKind !== null) {
    return appleKind;
  }
  return input.chassisType === null ? null : (DMI_CHASSIS_KINDS[input.chassisType] ?? null);
}

const readOptionalFile = Effect.fn("readOptionalFile")(function* (path: string) {
  const fileSystem = yield* FileSystem.FileSystem;
  return yield* fileSystem.readFileString(path).pipe(
    Effect.map(normalize),
    Effect.catch(() => Effect.succeed(null)),
  );
});

const runProbe = Effect.fn("runMachineProbe")(function* (input: {
  readonly command: string;
  readonly args: ReadonlyArray<string>;
}) {
  const processRunner = yield* ProcessRunner.ProcessRunner;
  return yield* processRunner
    .run({
      command: input.command,
      args: input.args,
      timeout: "5 seconds",
      timeoutBehavior: "timedOutResult",
    })
    .pipe(
      Effect.map((result) => (result.code === 0 ? normalize(result.stdout) : null)),
      Effect.catch(() => Effect.succeed(null)),
    );
});

// IOKit's `product` node carries the marketing name ("Mac mini (2024)") on
// Apple silicon; Intel Macs lack it, so `hw.model` ("Macmini8,1") is the
// fallback. Both are single-digit-millisecond calls.
const detectDarwinMachineKind = Effect.fn("detectDarwinMachineKind")(function* () {
  const ioreg = yield* runProbe({ command: "ioreg", args: ["-rd1", "-n", "product"] });
  const productName = ioreg?.match(/"product-name"\s*=\s*<"([^"]+)">/)?.[1] ?? null;
  const fromProductName =
    productName === null ? null : machineKindFromAppleProductName(productName);
  if (fromProductName !== null) {
    return fromProductName;
  }
  const model = yield* runProbe({ command: "sysctl", args: ["-n", "hw.model"] });
  return model === null ? null : machineKindFromAppleProductName(model);
});

const detectLinuxMachineKind = Effect.fn("detectLinuxMachineKind")(function* () {
  const [chassisType, sysVendor, productName] = yield* Effect.all([
    readOptionalFile(`${DMI_ROOT}/chassis_type`),
    readOptionalFile(`${DMI_ROOT}/sys_vendor`),
    readOptionalFile(`${DMI_ROOT}/product_name`),
  ]);
  return machineKindFromDmi({ chassisType, sysVendor, productName });
});

export const detectServerEnvironmentMachineKind = Effect.fn("detectServerEnvironmentMachineKind")(
  function* () {
    const platform = yield* HostProcessPlatform;
    switch (platform) {
      case "darwin":
        return yield* detectDarwinMachineKind();
      case "linux":
        return yield* detectLinuxMachineKind();
      default:
        return null;
    }
  },
);