Adding a New Platform

Agents report their capabilities to the Hub. Steam is optional — implement only what your platform supports.

Agent Capabilities

The Hub dynamically shows/hides UI based on what the Agent reports:

File Capabilities (required)

file_upload— Receive game uploads
file_list— List installed games

Steam Capabilities (optional)

steam_shortcuts— Create/delete shortcuts
steam_artwork— Apply artwork
steam_users— List Steam users
steam_restart— Restart Steam

Monitoring Capabilities (optional)

telemetry— Hardware metrics streaming
console_log— Console log capture & streaming
game_log_wrapper— Game stdout/stderr capture (Linux)
PC + Steam

All capabilities

PC without Steam

file_upload, file_list

Android (future)

file_upload, file_list

Platform-Specific Files (if using Steam)

Rust cfg attributes separate platform code. Only needed if your platform supports Steam:

crates/steam/src/paths_*.rs

Steam directory detection

crates/steam/src/controller_*.rs

Start, stop, restart Steam

1 Steam Paths

// crates/steam/src/paths_darwin.rs

use std::path::PathBuf;
use crate::SteamError;

pub(crate) fn get_base_dir() -> Result<PathBuf, SteamError> {
    let home = std::env::var_os("HOME")
        .map(PathBuf::from)
        .ok_or(SteamError::NotFound)?;
    let steam_dir = home
        .join("Library")
        .join("Application Support")
        .join("Steam");
    Ok(steam_dir)
}

2 Steam Controller

// crates/steam/src/controller_darwin.rs

pub async fn is_running() -> bool {
    let output = tokio::process::Command::new("pgrep")
        .args(["-x", "steam"])
        .output().await;
    output.map(|o| o.status.success()).unwrap_or(false)
}

pub async fn start() -> Result<(), SteamError> {
    tokio::process::Command::new("open")
        .args(["-a", "Steam"])
        .spawn()
        .map_err(|e| SteamError::Controller(
            format!("failed to start Steam: {e}")))?;
    Ok(())
}

pub async fn shutdown_command() -> Result<(), SteamError> {
    tokio::process::Command::new("osascript")
        .args(["-e", "quit app \"Steam\""])
        .output().await
        .map_err(|e| SteamError::Controller(
            format!("failed to shutdown Steam: {e}")))?;
    Ok(())
}

Required Controller Methods

Start() Launch Steam
Shutdown() Graceful close
Restart() Full restart
IsRunning() Process check
IsGamingMode() Console mode
EnsureRunning() Start if needed
LaunchGame(appID) Launch via steam:// protocol
IsCEFAvailable() Check CEF debugger
WaitForCEF() Wait for CEF ready

Checklist (if adding Steam support)

paths_<platform>.rs - Steam directory
controller_<platform>.rs - Steam control
Test Steam detection works
Test shortcut creation works
Update README.md
Add to docs install section