Add terminal client, memory, model fallback, version and install improvements
- 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>
This commit is contained in:
1 parent
aa1f8cf016
commit
6dffcb501a
52 files changed
+6477
-765
No files matched your search
@@ -0,0 +1,57 @@
|
||||
//! Version information and a daily check for new releases.
|
||||
|
||||
use crate::app::App;
|
||||
use serde_json::{Value, json};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
/// Git commit the image was built from (Docker build arg GIT_COMMIT).
|
||||
pub fn commit() -> &'static str {
|
||||
option_env!("CODECRAFT_COMMIT").unwrap_or("")
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct UpdateState {
|
||||
pub latest: Mutex<Option<Value>>,
|
||||
}
|
||||
|
||||
fn newer(a: &str, b: &str) -> bool {
|
||||
let p = |v: &str| v.trim_start_matches('v').split(['.', '-']).take(3).map(|x| x.parse::<u64>().unwrap_or(0)).collect::<Vec<_>>();
|
||||
p(a) > p(b)
|
||||
}
|
||||
|
||||
async fn check(app: &App) -> Option<Value> {
|
||||
let url = app.config().server.update_url.clone();
|
||||
if url.is_empty() {
|
||||
return None;
|
||||
}
|
||||
let v: Value = crate::provider::http_client().get(&url).timeout(Duration::from_secs(20)).send().await.ok()?.json().await.ok()?;
|
||||
let tag = v["tag_name"].as_str()?.to_string();
|
||||
if !newer(&tag, VERSION) {
|
||||
return None;
|
||||
}
|
||||
Some(json!({"version": tag.trim_start_matches('v'), "url": v["html_url"], "notes": v["body"].as_str().unwrap_or("").chars().take(4000).collect::<String>(), "published_at": v["published_at"]}))
|
||||
}
|
||||
|
||||
pub fn spawn_checker(app: Arc<App>) {
|
||||
tokio::spawn(async move {
|
||||
tokio::time::sleep(Duration::from_secs(60)).await;
|
||||
loop {
|
||||
let found = check(&app).await;
|
||||
*app.update.latest.lock().unwrap() = found;
|
||||
tokio::time::sleep(Duration::from_secs(24 * 3600)).await;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn version_compare() {
|
||||
assert!(super::newer("v0.2.0", "0.1.9"));
|
||||
assert!(super::newer("1.0.0", "0.9.9"));
|
||||
assert!(!super::newer("v0.1.0", "0.1.0"));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user