use clap::{Parser, Subcommand};
use std::path::PathBuf;
pub mod agent_replay;
pub mod build_pipeline;
pub mod dev_runtime;
pub mod patch_engine;
pub mod runtime_connection;
pub mod scaffold;
pub mod webkit_server;
pub mod ws_server;
#[derive(Parser)]
#[command(name = "cvkg")]
#[command(about = "Cyber Viking GUI X CLI", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand)]
enum Commands {
New {
name: String,
#[arg(long)]
template: Option<String>,
#[arg(long)]
git: bool,
},
Dev {
#[arg(long)]
target: Option<String>,
#[arg(long, default_value_t = 3000)]
port: u16,
#[arg(long)]
inspector: bool,
},
Build {
#[arg(long)]
target: String,
#[arg(long)]
release: bool,
#[arg(long)]
features: Vec<String>,
},
Serve {
#[arg(long, default_value_t = 8080)]
port: u16,
#[arg(long)]
open: bool,
#[arg(long)]
inspector: bool,
},
Check {
#[arg(long)]
all: bool,
#[arg(long)]
target: Option<String>,
},
Test {
#[arg(long)]
ui: bool,
#[arg(long)]
target: Option<String>,
},
Inspect {
#[arg(long)]
url: String,
#[arg(long, default_value_t = 8081)]
ws_port: u16,
},
Export {
#[arg(long)]
base_path: String,
#[arg(long)]
optimize: bool,
},
Add {
name: String,
#[arg(long)]
features: Vec<String>,
},
Theme {
#[arg(long)]
input: PathBuf,
#[arg(long)]
output: PathBuf,
},
}
fn main() {
let cli = Cli::parse();
match cli.command {
Commands::New {
name,
template: _,
git: _,
} => {
let scaffolder = scaffold::Scaffolder::new(name);
if let Err(e) = scaffolder.run() {
eprintln!("Scaffolding failed: {}", e);
std::process::exit(1);
}
}
Commands::Dev {
target: _,
port,
inspector: _,
} => {
println!("Starting development server on port {}", port);
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], port));
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
if let Err(e) = ws_server::start_server(addr).await {
eprintln!("Failed to start dev server: {}", e);
}
});
}
Commands::Build {
target,
release,
features,
} => {
println!("Building for target: {}", target);
let artifact = build_pipeline::BuildPipeline::compile_project(
".",
Some(&target),
release,
&features,
);
println!("Build complete. Root ID: {}", artifact.root_id);
}
Commands::Serve {
port,
open: _,
inspector: _,
} => {
println!("Starting WebKit preview server on port {}", port);
let addr = std::net::SocketAddr::from(([0, 0, 0, 0], port));
tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap()
.block_on(async {
if let Err(e) = webkit_server::start_server(addr).await {
eprintln!("Failed to start preview server: {}", e);
}
});
}
Commands::Check { all: _, target: _ } => {
println!("Running CVKG type-check and layout audit...");
let status = std::process::Command::new("cargo")
.arg("check")
.status()
.expect("Failed to execute cargo check");
if status.success() {
println!("Check complete: All systems nominal.");
} else {
eprintln!("Check failed.");
std::process::exit(1);
}
}
Commands::Test { ui: _, target: _ } => {
println!("Running CVKG unit and snapshot tests...");
let status = std::process::Command::new("cargo")
.arg("test")
.status()
.expect("Failed to execute cargo test");
if status.success() {
println!("Tests complete: Berserker validated.");
} else {
eprintln!("Tests failed.");
std::process::exit(1);
}
}
Commands::Inspect { url, ws_port: _ } => {
println!("Launching CVKG Inspector for {}", url);
if let Err(e) = webbrowser::open(&url) {
eprintln!("Failed to launch browser: {}", e);
}
}
Commands::Export {
base_path,
optimize,
} => {
println!("Exporting CVKG WASM bundle to {}", base_path);
let mut cmd = std::process::Command::new("wasm-pack");
cmd.arg("build").arg("--target").arg("web");
if optimize {
cmd.arg("--release");
}
let status = cmd.status().expect("Failed to execute wasm-pack");
if status.success() {
println!("Export complete: Artifact ready for deployment.");
} else {
eprintln!("Export failed. Ensure wasm-pack is installed.");
}
}
Commands::Add { name, features } => {
println!("Adding CVKG component crate: {}", name);
let mut cmd = std::process::Command::new("cargo");
cmd.arg("add").arg(&name);
if !features.is_empty() {
cmd.arg("--features").arg(features.join(","));
}
let status = cmd.status().expect("Failed to execute cargo add");
if !status.success() {
eprintln!("Failed to add crate.");
}
}
Commands::Theme { input, output } => {
println!(
"Generating theme from {} to {}",
input.display(),
output.display()
);
let json_str = std::fs::read_to_string(&input).expect("Failed to read theme JSON");
let tokens: serde_json::Value =
serde_json::from_str(&json_str).expect("Invalid theme JSON");
let mut rs_content = String::from("/// Generated CVKG Theme\n");
rs_content.push_str("pub struct Theme {\n");
if let Some(obj) = tokens.as_object() {
for key in obj.keys() {
rs_content.push_str(&format!(" pub {}: [f32; 4],\n", key));
}
}
rs_content.push_str("}\n\n");
rs_content.push_str("pub const CUSTOM_THEME: Theme = Theme {\n");
if let Some(obj) = tokens.as_object() {
for (key, val) in obj {
if let Some(arr) = val.as_array() {
if arr.len() == 4 {
let r = arr[0].as_f64().unwrap_or(0.0);
let g = arr[1].as_f64().unwrap_or(0.0);
let b = arr[2].as_f64().unwrap_or(0.0);
let a = arr[3].as_f64().unwrap_or(1.0);
rs_content.push_str(&format!(
" {}: [{:.2}, {:.2}, {:.2}, {:.2}],\n",
key, r, g, b, a
));
}
}
}
}
rs_content.push_str("};\n");
std::fs::write(&output, rs_content).expect("Failed to write theme RS file");
println!("Theme generation successful.");
}
}
}