Pools können auf ein vorhandenes Config-Verzeichnis verweisen

Wer claude schon benutzt, hat einen fertigen Login in ~/.claude. Bisher
begann ai-control trotzdem bei null: jeder Pool war ein neu angelegtes
Verzeichnis, der erste Sessionstart verlangte /login. Ein Pool kann jetzt
stattdessen auf ein bestehendes Verzeichnis zeigen.

pool.json trägt dafür optional `dir` (home-kontrahiert). Der Pool-Ordner
ist dann nur die Hülle für diese Datei; pool_config_dir() löst auf und
ist die einzige Stelle, über die Sessionstart, Verbrauchsstatistik,
Panel-Provisionierung und Keychain-Name gehen.

Zeigt ein Pool auf claudes Default-Verzeichnis, bleibt CLAUDE_CONFIG_DIR
ungesetzt: claude nimmt dieses Verzeichnis von sich aus und damit den
unsuffixierten Keychain-Eintrag, an dem der bestehende Login hängt (am
Keychain gegengeprüft — "Claude Code-credentials" ohne Suffix neben den
suffixierten Pool-Einträgen). Ein Verweis auf ein anderes Verzeichnis
setzt die Variable wie gewohnt.

Das verwiesene Verzeichnis gehört der App nicht: dort entsteht nur die
Panel-Ausstattung (Skill, MCP-Eintrag, Freigabe-Ergänzung, alles
additiv), keine settings.json-Grundausstattung. Löschen trifft nur die
Hülle; Runtime-Symlinks und das Zurücksetzen des Keychain-Eintrags sind
gesperrt. Ein Verzeichnis kann nur ein Pool belegen, Ziele innerhalb von
pools/ sind ausgeschlossen.

In der UI erscheint der Knopf nur, wenn ~/.claude existiert und noch
kein Pool darauf zeigt; die Liste zeigt den Pfad unter dem Namen und
statt "Zurücksetzen" den Hinweis, dass die Anmeldung claude gehört.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
marcusH
2026-07-22 09:43:00 +02:00
parent 6b4afd2bc5
commit df828daea9
16 changed files with 383 additions and 25 deletions
+26 -4
View File
@@ -9,8 +9,9 @@ use crate::domain::archive::{
use crate::domain::credentials::keychain_service;
use crate::domain::paths::Paths;
use crate::domain::pool::{
create_apikey_pool_in, create_oauth_pool_in, delete_pool_in, ensure_oauth_pool,
link_pool_runtime_in, list_pools_in, read_pool, rename_pool_in, set_apikey_in, PoolInfo,
create_apikey_pool_in, create_oauth_pool_in, create_reference_pool_in, delete_pool_in,
ensure_oauth_pool, link_pool_runtime_in, list_pools_in, offered_default_dir, read_pool,
rename_pool_in, set_apikey_in, PoolInfo,
};
use crate::domain::project::{
add_project_in, add_work_dir_in, assign_pool_in, create_project_full_in, delete_preview_in,
@@ -215,7 +216,21 @@ pub(crate) fn set_apikey(pool: String, key: String, allow_file: bool) -> Result<
#[tauri::command]
pub(crate) fn keychain_status(pool: String) -> Result<bool, String> {
crate::platform::oauth_keychain_exists(&keychain_service(&Paths::real(), &pool))
crate::platform::oauth_keychain_exists(&keychain_service(&Paths::real(), &pool)?)
}
/// Claudes Default-Verzeichnis, sofern vorhanden und noch nicht als Pool
/// eingebunden — die Pool-Anlage bietet es dann als fertigen Pool an.
#[tauri::command]
pub(crate) fn default_config_dir() -> Option<String> {
offered_default_dir(&Paths::real())
}
/// Pool, der auf ein bestehendes Config-Verzeichnis verweist (statt eines neu
/// angelegten). Der vorhandene Login dort bleibt gültig.
#[tauri::command]
pub(crate) fn create_reference_pool(name: String, dir: String) -> Result<String, String> {
create_reference_pool_in(&Paths::real(), &name, &dir)
}
/// Setzt einen oauth-Pool zurück: löscht den suffixierten Keychain-Eintrag,
@@ -225,6 +240,13 @@ pub(crate) fn keychain_status(pool: String) -> Result<bool, String> {
pub(crate) fn oauth_login(pool: String) -> Result<(), String> {
let paths = Paths::real();
ensure_oauth_pool(&paths, &pool)?;
// Bei einem verwiesenen Pool hinge daran der Login des fremden
// Verzeichnisses — den löscht die App nicht.
if let Some(dir) = read_pool(&paths, &pool)?.dir {
return Err(format!(
"Neuanmeldung geht bei einem verwiesenen Pool nicht ({dir}) — dort meldet claude selbst an"
));
}
let running = running_projects_using_pool(&paths, &pool)?;
if !running.is_empty() {
@@ -234,7 +256,7 @@ pub(crate) fn oauth_login(pool: String) -> Result<(), String> {
));
}
let service = keychain_service(&paths, &pool);
let service = keychain_service(&paths, &pool)?;
if crate::platform::oauth_keychain_exists(&service)? {
crate::platform::oauth_keychain_delete(&service)?;
}