use rch_common::{
Classification, ClassificationTier, PlacementPlan, RequiredRuntime, SelectedWorker,
SelectionDiagnostics, SelectionReason, WorkerCapabilities, WorkerConfig,
};
use schemars::JsonSchema;
use serde::Serialize;
use crate::status_types::WorkerCapabilitiesFromApi;
#[derive(Debug, Clone, Default, Serialize, JsonSchema)]
pub struct WorkerDiskInfo {
pub disk_free_gb: Option<f64>,
pub disk_free_ratio: Option<f64>,
pub build_disk_free_gb: Option<f64>,
pub disk_measurement_source: Option<&'static str>,
pub disk_pressure_state: Option<String>,
pub disk_pressure_reason: Option<String>,
pub disk_pressure_source: Option<&'static str>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct WorkerInfo {
pub id: String,
pub host: String,
pub user: String,
pub total_slots: u32,
pub priority: u32,
pub tags: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub speedscore: Option<f64>,
#[serde(flatten)]
pub disk: WorkerDiskInfo,
}
impl From<&WorkerConfig> for WorkerInfo {
fn from(w: &WorkerConfig) -> Self {
Self {
id: w.id.as_str().to_string(),
host: w.host.clone(),
user: w.user.clone(),
total_slots: w.total_slots,
priority: w.priority,
tags: w.tags.clone(),
speedscore: None,
disk: WorkerDiskInfo::default(),
}
}
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct WorkersListResponse {
pub workers: Vec<WorkerInfo>,
pub count: usize,
}
#[derive(Debug, Clone, Serialize)]
pub struct WorkerProbeResult {
pub id: String,
pub host: String,
pub status: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub latency_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error_code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub capabilities: Option<WorkerCapabilities>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub missing_components: Vec<String>,
#[serde(flatten)]
pub disk: WorkerDiskInfo,
}
#[derive(Debug, Clone, Serialize, Default)]
pub struct WorkerProbeSummary {
pub total: usize,
pub healthy: usize,
pub unhealthy: usize,
pub capability_missing: usize,
pub failed: usize,
pub by_error_code: std::collections::BTreeMap<String, usize>,
}
#[derive(Debug, Clone, Serialize)]
pub struct WorkersProbeResponse {
pub results: Vec<WorkerProbeResult>,
pub summary: WorkerProbeSummary,
}
#[derive(Debug, Clone, Serialize)]
pub struct WorkersCapabilitiesReport {
pub workers: Vec<WorkerCapabilitiesFromApi>,
#[serde(skip_serializing_if = "Option::is_none")]
pub local: Option<WorkerCapabilities>,
#[serde(skip_serializing_if = "Option::is_none")]
pub required_runtime: Option<RequiredRuntime>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub warnings: Vec<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct WorkerBenchmarkResult {
pub id: String,
pub host: String,
pub status: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub duration_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct WorkerActionResponse {
pub worker_id: String,
pub action: String,
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DaemonStatusResponse {
pub running: bool,
pub socket_path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub uptime_seconds: Option<u64>,
}
#[derive(Debug, Clone, Serialize)]
#[allow(dead_code)]
pub struct SystemOverview {
pub daemon_running: bool,
pub hook_installed: bool,
pub workers_count: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub workers: Option<Vec<WorkerInfo>>,
}
#[derive(Debug, Clone, Serialize)]
pub struct DaemonActionResponse {
pub action: String,
pub success: bool,
pub socket_path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct DaemonReloadResponse {
pub success: bool,
pub added: usize,
pub updated: usize,
pub removed: usize,
#[serde(skip_serializing_if = "Vec::is_empty")]
pub warnings: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
pub struct DaemonLogsResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub log_file: Option<String>,
pub lines: Vec<String>,
pub found: bool,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigShowResponse {
pub general: ConfigGeneralSection,
pub compilation: ConfigCompilationSection,
pub transfer: ConfigTransferSection,
pub environment: ConfigEnvironmentSection,
pub circuit: ConfigCircuitSection,
pub output: ConfigOutputSection,
pub self_healing: ConfigSelfHealingSection,
pub sources: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value_sources: Option<Vec<ConfigValueSourceInfo>>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigValueSourceInfo {
pub key: String,
pub value: String,
pub source: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigGetResponse {
pub key: String,
pub value: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
#[allow(dead_code)]
pub struct ConfigExportResponse {
pub format: String,
pub content: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigGeneralSection {
pub enabled: bool,
pub force_local: bool,
pub force_remote: bool,
pub log_level: String,
pub socket_path: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigCompilationSection {
pub confidence_threshold: f64,
pub min_local_time_ms: u64,
pub remote_speedup_threshold: f64,
pub local_slots: u32,
pub build_slots: u32,
pub test_slots: u32,
pub check_slots: u32,
pub build_timeout_sec: u64,
pub test_timeout_sec: u64,
pub bun_timeout_sec: u64,
pub external_timeout_enabled: bool,
pub allow_local_fallback: bool,
pub remote_build_jobs: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigTransferSection {
pub compression_level: u32,
pub exclude_patterns: Vec<String>,
pub remote_base: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub sync_timeout_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_transfer_mb: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_transfer_time_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bwlimit_kbps: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub estimated_bandwidth_bps: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rsync_bin: Option<String>,
pub adaptive_compression: bool,
pub min_compression_level: u32,
pub max_compression_level: u32,
pub verify_artifacts: bool,
#[serde(skip_serializing_if = "is_default_verify_size")]
pub verify_max_size_bytes: u64,
}
pub fn is_default_verify_size(val: &u64) -> bool {
*val == 100 * 1024 * 1024 }
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigEnvironmentSection {
pub allowlist: Vec<String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigCircuitSection {
pub failure_threshold: u32,
pub success_threshold: u32,
pub error_rate_threshold: f64,
pub window_secs: u64,
pub open_cooldown_secs: u64,
pub half_open_max_probes: u32,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigOutputSection {
pub visibility: rch_common::OutputVisibility,
pub first_run_complete: bool,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigSelfHealingSection {
pub hook_starts_daemon: bool,
pub daemon_installs_hooks: bool,
pub auto_start_cooldown_secs: u64,
pub auto_start_timeout_secs: u64,
}
#[derive(Debug, Clone, Serialize)]
pub struct ConfigInitResponse {
pub created: Vec<String>,
pub already_existed: Vec<String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigValidationResponse {
pub errors: Vec<ConfigValidationIssue>,
pub warnings: Vec<ConfigValidationIssue>,
pub valid: bool,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigValidationIssue {
pub file: String,
pub message: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct ConfigSetResponse {
pub key: String,
pub value: String,
pub config_path: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigResetResponse {
pub key: String,
pub value: String,
pub config_path: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum LintSeverity {
Error,
Warning,
Info,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct LintIssue {
pub severity: LintSeverity,
pub code: String,
pub message: String,
pub remediation: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigLintResponse {
pub issues: Vec<LintIssue>,
pub error_count: usize,
pub warning_count: usize,
pub info_count: usize,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigDiffEntry {
pub key: String,
pub current: String,
pub default: String,
pub source: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct ConfigDiffResponse {
pub entries: Vec<ConfigDiffEntry>,
pub total_changes: usize,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DiagnoseResponse {
pub classification: Classification,
pub tiers: Vec<ClassificationTier>,
pub command: String,
pub normalized_command: String,
pub decision: DiagnoseDecision,
pub threshold: DiagnoseThreshold,
pub daemon: DiagnoseDaemonStatus,
pub required_runtime: RequiredRuntime,
#[serde(skip_serializing_if = "Option::is_none")]
pub local_capabilities: Option<WorkerCapabilities>,
#[serde(skip_serializing_if = "Vec::is_empty", default)]
pub capabilities_warnings: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub worker_selection: Option<DiagnoseWorkerSelection>,
pub placement: PlacementPlan,
#[serde(skip_serializing_if = "Option::is_none")]
pub speed_gate: Option<crate::hook::SpeedGateDiagnosis>,
#[serde(skip_serializing_if = "Option::is_none")]
pub dry_run: Option<DryRunSummary>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DiagnoseDecision {
pub would_intercept: bool,
pub reason: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DiagnoseThreshold {
pub value: f64,
pub source: String,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DiagnoseDaemonStatus {
pub socket_path: String,
pub socket_exists: bool,
pub reachable: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub uptime_seconds: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DiagnoseWorkerSelection {
pub estimated_cores: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub cargo_jobs: Option<u32>,
pub worker: Option<SelectedWorker>,
pub reason: SelectionReason,
#[serde(skip_serializing_if = "Option::is_none")]
pub diagnostics: Option<SelectionDiagnostics>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DryRunPipelineStep {
pub step: u32,
pub name: String,
pub description: String,
pub skipped: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub skip_reason: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub estimated_duration_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DryRunTransferEstimate {
pub bytes: u64,
pub human_size: String,
pub files: u32,
pub estimated_time_ms: u64,
pub would_skip: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub skip_reason: Option<String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DryRunSummary {
pub would_offload: bool,
pub reason: String,
pub pipeline_steps: Vec<DryRunPipelineStep>,
#[serde(skip_serializing_if = "Option::is_none")]
pub transfer_estimate: Option<DryRunTransferEstimate>,
#[serde(skip_serializing_if = "Option::is_none")]
pub total_estimated_ms: Option<u64>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct HookActionResponse {
pub action: String,
pub success: bool,
pub settings_path: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize)]
#[allow(dead_code)]
pub struct HookTestResponse {
pub classification_tests: Vec<ClassificationTestResult>,
pub daemon_connected: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub daemon_response: Option<String>,
pub workers_configured: usize,
pub workers: Vec<WorkerInfo>,
}
#[derive(Debug, Clone, Serialize)]
#[allow(dead_code)]
pub struct ClassificationTestResult {
pub command: String,
pub is_compilation: bool,
pub confidence: f64,
pub expected_intercept: bool,
pub passed: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum DoctorCheckStatus {
Pass,
Warning,
Fail,
Skipped,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DoctorCheck {
pub category: String,
pub name: String,
pub status: DoctorCheckStatus,
pub message: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub details: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub suggestion: Option<String>,
#[serde(default)]
pub fixable: bool,
#[serde(default)]
pub fix_applied: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub fix_message: Option<String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DoctorSummary {
pub total: usize,
pub passed: usize,
pub warnings: usize,
pub failed: usize,
pub fixed: usize,
pub would_fix: usize,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DoctorFixApplied {
pub check_name: String,
pub action: String,
pub success: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
}
#[derive(Debug, Clone, Serialize, JsonSchema)]
pub struct DoctorResponse {
pub schema_version: String,
pub checks: Vec<DoctorCheck>,
pub summary: DoctorSummary,
pub fixes_applied: Vec<DoctorFixApplied>,
}