Pool-Runtime (projects/todos/history) als Symlinks ins synced Repo, auch bei Pool-Anlage
This commit is contained in:
+71
-2
@@ -642,6 +642,44 @@ fn init_pool_config(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runtime, die pro Pool ins synced Repo gelinkt wird: Transkripte, Todos,
|
||||||
|
/// Prompt-Historie. (name, ist_ordner)
|
||||||
|
const SYNCED_RUNTIME: [(&str, bool); 3] =
|
||||||
|
[("projects", true), ("todos", true), ("history.jsonl", false)];
|
||||||
|
|
||||||
|
/// Zielort der synced Runtime-Daten eines Pools im claude-projects-Repo.
|
||||||
|
fn pool_data_dir(paths: &Paths, pool: &str) -> PathBuf {
|
||||||
|
paths.projects_dir().join("pool").join(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Ersetzt im Pool-Ordner projects/todos/history.jsonl durch Symlinks auf das
|
||||||
|
/// synced Repo. Die Symlinks sind maschinenlokal, die Daten reisen über git.
|
||||||
|
/// Vorhandene echte Inhalte werden verworfen (kein History-Erhalt — bewusst).
|
||||||
|
fn link_pool_runtime_in(paths: &Paths, pool: &str) -> Result<(), String> {
|
||||||
|
check_name(pool)?;
|
||||||
|
let src = paths.pool_dir(pool);
|
||||||
|
let data = pool_data_dir(paths, pool);
|
||||||
|
for (name, is_dir) in SYNCED_RUNTIME {
|
||||||
|
let target = data.join(name);
|
||||||
|
if is_dir {
|
||||||
|
fs::create_dir_all(&target).map_err(|e| e.to_string())?;
|
||||||
|
} else {
|
||||||
|
fs::create_dir_all(&data).map_err(|e| e.to_string())?;
|
||||||
|
if !target.exists() {
|
||||||
|
fs::write(&target, "").map_err(|e| e.to_string())?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let link = src.join(name);
|
||||||
|
if link.is_dir() && !link.is_symlink() {
|
||||||
|
fs::remove_dir_all(&link).map_err(|e| e.to_string())?;
|
||||||
|
} else if link.is_symlink() || link.exists() {
|
||||||
|
fs::remove_file(&link).map_err(|e| e.to_string())?;
|
||||||
|
}
|
||||||
|
std::os::unix::fs::symlink(&target, &link).map_err(|e| e.to_string())?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Legt einen apikey-Pool an: Key-Datei (0600), settings.json mit
|
/// Legt einen apikey-Pool an: Key-Datei (0600), settings.json mit
|
||||||
/// apiKeyHelper auf diese Datei, CLAUDE.md, pool.json.
|
/// apiKeyHelper auf diese Datei, CLAUDE.md, pool.json.
|
||||||
fn create_apikey_pool_in(paths: &Paths, name: &str, key: &str) -> Result<(), String> {
|
fn create_apikey_pool_in(paths: &Paths, name: &str, key: &str) -> Result<(), String> {
|
||||||
@@ -656,7 +694,8 @@ fn create_apikey_pool_in(paths: &Paths, name: &str, key: &str) -> Result<(), Str
|
|||||||
&dir,
|
&dir,
|
||||||
serde_json::json!({ "apiKeyHelper": format!("cat '{}'", key_path.display()) }),
|
serde_json::json!({ "apiKeyHelper": format!("cat '{}'", key_path.display()) }),
|
||||||
)?;
|
)?;
|
||||||
write_pool_json(&dir, name, "apikey")
|
write_pool_json(&dir, name, "apikey")?;
|
||||||
|
link_pool_runtime_in(paths, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Legt einen oauth-Pool an: Grundausstattung (leere settings.json + CLAUDE.md)
|
/// Legt einen oauth-Pool an: Grundausstattung (leere settings.json + CLAUDE.md)
|
||||||
@@ -665,7 +704,8 @@ fn create_apikey_pool_in(paths: &Paths, name: &str, key: &str) -> Result<(), Str
|
|||||||
fn create_oauth_pool_in(paths: &Paths, name: &str) -> Result<(), String> {
|
fn create_oauth_pool_in(paths: &Paths, name: &str) -> Result<(), String> {
|
||||||
let dir = check_new_pool(paths, name)?;
|
let dir = check_new_pool(paths, name)?;
|
||||||
init_pool_config(&dir, serde_json::json!({}))?;
|
init_pool_config(&dir, serde_json::json!({}))?;
|
||||||
write_pool_json(&dir, name, "oauth")
|
write_pool_json(&dir, name, "oauth")?;
|
||||||
|
link_pool_runtime_in(paths, name)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Löscht einen Pool samt Ordner (inkl. Credentials). Zugeordnete Projekte
|
/// Löscht einen Pool samt Ordner (inkl. Credentials). Zugeordnete Projekte
|
||||||
@@ -1096,6 +1136,17 @@ fn set_sync_setting(enabled: bool) -> Result<(), String> {
|
|||||||
set_sync_on_session_end_in(&Paths::real(), enabled)
|
set_sync_on_session_end_in(&Paths::real(), enabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Verlinkt die synced Runtime eines bestehenden Pools. Nur bei idlem Pool —
|
||||||
|
/// sonst würde das Transkript der laufenden Session ersetzt.
|
||||||
|
#[tauri::command]
|
||||||
|
fn link_pool_runtime(pool: String) -> Result<(), String> {
|
||||||
|
let paths = Paths::real();
|
||||||
|
if !running_projects_using_pool(&paths, &pool)?.is_empty() {
|
||||||
|
return Err(format!("{pool} wird benutzt — erst Session beenden"));
|
||||||
|
}
|
||||||
|
link_pool_runtime_in(&paths, &pool)
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
pub fn run() {
|
pub fn run() {
|
||||||
// generate_context! darf pro Crate nur einmal expandieren (_EMBED_INFO_PLIST).
|
// generate_context! darf pro Crate nur einmal expandieren (_EMBED_INFO_PLIST).
|
||||||
@@ -1213,6 +1264,7 @@ fn main_builder() -> tauri::Builder<tauri::Wry> {
|
|||||||
restart_project,
|
restart_project,
|
||||||
sync_setting,
|
sync_setting,
|
||||||
set_sync_setting,
|
set_sync_setting,
|
||||||
|
link_pool_runtime,
|
||||||
oauth_login,
|
oauth_login,
|
||||||
keychain_status,
|
keychain_status,
|
||||||
set_apikey,
|
set_apikey,
|
||||||
@@ -1647,6 +1699,23 @@ mod tests {
|
|||||||
assert!(!p.project_config("proj").exists());
|
assert!(!p.project_config("proj").exists());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn pool_anlegen_verlinkt_synced_runtime() {
|
||||||
|
let p = tmp_paths();
|
||||||
|
create_oauth_pool_in(&p, "privat").unwrap();
|
||||||
|
let pooldir = p.pool_dir("privat");
|
||||||
|
for (name, is_dir) in SYNCED_RUNTIME {
|
||||||
|
let link = pooldir.join(name);
|
||||||
|
assert!(link.is_symlink(), "{name} sollte Symlink sein");
|
||||||
|
assert_eq!(fs::read_link(&link).unwrap(), pool_data_dir(&p, "privat").join(name));
|
||||||
|
let target = pool_data_dir(&p, "privat").join(name);
|
||||||
|
assert_eq!(target.is_dir(), is_dir);
|
||||||
|
assert!(target.exists());
|
||||||
|
}
|
||||||
|
// Zieldaten liegen im claude-projects-Repo unter pool/<name>/
|
||||||
|
assert!(p.projects_dir().join("pool").join("privat").is_dir());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn sync_optin_default_aus_und_umschaltbar() {
|
fn sync_optin_default_aus_und_umschaltbar() {
|
||||||
let p = tmp_paths();
|
let p = tmp_paths();
|
||||||
|
|||||||
Reference in New Issue
Block a user