- codecraft: full-screen terminal client (ratatui) – local tools and files, models via the CodeCraft server gateway or own providers (standalone with a guided setup), slash commands, @files, permission dialogs, resume, update - Static Linux CLI builds (x86_64/aarch64, cargo-zigbuild) served by the server: /cli/install.sh and /cli/download/<target> - Project memory (remember tool, .codecraft/memory.md) and personal memory - Model fallback when a host is unreachable or a budget is used up - Version/commit in UI and health, daily release check, "What's new" dialog - PWA: install button, maskable icon, manifest id and screenshots - Case-insensitive detection of connection errors for retries Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
103 lines
3.6 KiB
Rust
103 lines
3.6 KiB
Rust
//! Local configuration (~/.config/codecraft/config.toml, readable only by the user).
|
||
|
||
use anyhow::{Context, Result};
|
||
use codecraft_core::config::ProviderConfig;
|
||
use serde::{Deserialize, Serialize};
|
||
use std::collections::BTreeMap;
|
||
use std::path::PathBuf;
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||
pub struct ServerLogin {
|
||
pub url: String,
|
||
pub token: String,
|
||
#[serde(default)]
|
||
pub username: String,
|
||
}
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||
pub struct Config {
|
||
/// Signed-in CodeCraft server (models, modes and rules come from there).
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub server: Option<ServerLogin>,
|
||
/// Model used for new sessions ("provider/model", server models as "server:provider/model").
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub default_model: Option<String>,
|
||
/// Permission mode for new sessions (default: auto).
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub permission_mode: Option<String>,
|
||
/// UI language (en, de); default from the server or $LANG.
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub lang: Option<String>,
|
||
/// Theme override, e.g. "nord" and "dark" (default: your server theme).
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub theme: Option<String>,
|
||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||
pub theme_mode: Option<String>,
|
||
/// Own model providers (standalone mode), same format as the server config.
|
||
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
|
||
pub providers: BTreeMap<String, ProviderConfig>,
|
||
}
|
||
|
||
pub fn config_dir() -> PathBuf {
|
||
if let Some(x) = std::env::var_os("CODECRAFT_CONFIG_DIR") {
|
||
return PathBuf::from(x);
|
||
}
|
||
#[cfg(windows)]
|
||
if let Some(x) = std::env::var_os("APPDATA") {
|
||
return PathBuf::from(x).join("codecraft");
|
||
}
|
||
let base = std::env::var_os("XDG_CONFIG_HOME").map(PathBuf::from).unwrap_or_else(|| home().join(".config"));
|
||
base.join("codecraft")
|
||
}
|
||
|
||
pub fn data_dir() -> PathBuf {
|
||
if let Some(x) = std::env::var_os("CODECRAFT_DATA_DIR") {
|
||
return PathBuf::from(x);
|
||
}
|
||
#[cfg(windows)]
|
||
if let Some(x) = std::env::var_os("LOCALAPPDATA") {
|
||
return PathBuf::from(x).join("codecraft");
|
||
}
|
||
let base = std::env::var_os("XDG_DATA_HOME").map(PathBuf::from).unwrap_or_else(|| home().join(".local").join("share"));
|
||
base.join("codecraft")
|
||
}
|
||
|
||
pub fn home() -> PathBuf {
|
||
std::env::var_os("HOME").or_else(|| std::env::var_os("USERPROFILE")).map(PathBuf::from).unwrap_or_else(|| PathBuf::from("."))
|
||
}
|
||
|
||
fn path() -> PathBuf {
|
||
config_dir().join("config.toml")
|
||
}
|
||
|
||
impl Config {
|
||
pub fn load() -> Result<Self> {
|
||
let p = path();
|
||
match std::fs::read_to_string(&p) {
|
||
Ok(text) => toml::from_str(&text).with_context(|| format!("invalid {}", p.display())),
|
||
Err(_) => Ok(Self::default()),
|
||
}
|
||
}
|
||
|
||
pub fn save(&self) -> Result<()> {
|
||
let p = path();
|
||
std::fs::create_dir_all(p.parent().unwrap())?;
|
||
let text = format!(
|
||
"# CodeCraft terminal client – edit with `codecraft setup` or by hand.\n# API keys may be written as \"${{ENV_VAR}}\".\n\n{}",
|
||
toml::to_string_pretty(self)?
|
||
);
|
||
std::fs::write(&p, text)?;
|
||
#[cfg(unix)]
|
||
{
|
||
use std::os::unix::fs::PermissionsExt;
|
||
std::fs::set_permissions(&p, std::fs::Permissions::from_mode(0o600))?;
|
||
}
|
||
Ok(())
|
||
}
|
||
|
||
/// Nothing set up yet: the setup wizard runs first.
|
||
pub fn is_empty(&self) -> bool {
|
||
self.server.is_none() && self.providers.is_empty()
|
||
}
|
||
}
|