mod app;
mod chat;
mod finetune;
mod gguf;
mod gresiq;
mod hf;
mod hf_clone;
mod hf_search;
mod hf_upload;
mod mcp;
mod merge;
mod project;
mod token;
mod ui;
use std::io::{IsTerminal, Write};
fn print_usage() {
println!(
"onde {}\n{}\n\n\
Usage:\n \
onde Launch the interactive terminal UI (requires a TTY)\n \
onde --mcp Run the MCP JSON-RPC server over stdio\n \
onde --version Print the version and exit\n \
onde --help Print this help and exit",
env!("CARGO_PKG_VERSION"),
env!("CARGO_PKG_DESCRIPTION"),
);
}
#[cfg(unix)]
fn redirect_output() -> Option<std::fs::File> {
use std::fs::{self, OpenOptions};
use std::os::fd::{AsRawFd, FromRawFd};
let log_dir = dirs::cache_dir()
.unwrap_or_else(|| std::path::PathBuf::from("/tmp"))
.join("onde");
if fs::create_dir_all(&log_dir).is_err() {
return None;
}
let log_file = match OpenOptions::new()
.create(true)
.append(true)
.open(log_dir.join("debug.log"))
{
Ok(f) => f,
Err(_) => return None,
};
unsafe {
let tty_fd = libc::dup(libc::STDOUT_FILENO);
if tty_fd < 0 {
return None;
}
let log_fd = log_file.as_raw_fd();
libc::dup2(log_fd, libc::STDOUT_FILENO);
libc::dup2(log_fd, libc::STDERR_FILENO);
Some(std::fs::File::from_raw_fd(tty_fd))
}
}
#[cfg(not(unix))]
fn redirect_output() -> Option<std::fs::File> {
None
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
if std::env::args().skip(1).any(|arg| arg == "--mcp") {
return mcp::serve().await;
}
let args: Vec<String> = std::env::args().skip(1).collect();
if args.iter().any(|arg| arg == "--version" || arg == "-V") {
println!("onde {}", env!("CARGO_PKG_VERSION"));
return Ok(());
}
if args.iter().any(|arg| arg == "--help" || arg == "-h") {
print_usage();
return Ok(());
}
if !std::io::stdout().is_terminal() {
print_usage();
return Ok(());
}
let tty = redirect_output();
let mut writer: Box<dyn Write> = match tty {
Some(f) => Box::new(std::io::BufWriter::new(f)),
None => Box::new(std::io::stdout()),
};
crossterm::terminal::enable_raw_mode()?;
crossterm::execute!(
writer,
crossterm::terminal::EnterAlternateScreen,
crossterm::event::EnableMouseCapture
)?;
let backend = ratatui::backend::CrosstermBackend::new(writer);
let mut terminal = ratatui::Terminal::new(backend)?;
let result = app::run(&mut terminal).await;
crossterm::execute!(
terminal.backend_mut(),
crossterm::terminal::LeaveAlternateScreen,
crossterm::event::DisableMouseCapture
)?;
crossterm::terminal::disable_raw_mode()?;
terminal.show_cursor()?;
result
}