mirror of
https://github.com/outbackdingo/optimclaw.git
synced 2026-08-25 14:53:34 +00:00
fix: persist onboard_completed to bootstrap .env so config survives restart (#241)
* fix: persist onboard_completed to bootstrap .env so config survives restart (#187) The wizard saved settings to the database but check_onboard_needed() read from the legacy settings.json on disk, causing re-onboarding on every run for non-NEAR AI users. Write ONBOARD_COMPLETED=true to ~/.ironclaw/.env and check that env var instead of the legacy file. Co-Authored-By: Claude Opus 4.6 <[email protected]> * Apply suggestion from @Copilot Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Claude Opus 4.6 <[email protected]> Co-authored-by: Copilot <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.6
Copilot
parent
097a26ace6
commit
3f58ed6232
@@ -492,4 +492,32 @@ INJECTED="pwned"#;
|
||||
assert_eq!(parsed.len(), 2);
|
||||
assert!(parsed.iter().all(|(k, _)| k != "DATABASE_URL"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_onboard_completed_round_trips_through_env() {
|
||||
let dir = tempdir().unwrap();
|
||||
let env_path = dir.path().join(".env");
|
||||
|
||||
// Simulate what the wizard writes: bootstrap vars + ONBOARD_COMPLETED
|
||||
let vars = [
|
||||
("DATABASE_BACKEND", "libsql"),
|
||||
("ONBOARD_COMPLETED", "true"),
|
||||
];
|
||||
let mut content = String::new();
|
||||
for (key, value) in &vars {
|
||||
let escaped = value.replace('\\', "\\\\").replace('"', "\\\"");
|
||||
content.push_str(&format!("{}=\"{}\"\n", key, escaped));
|
||||
}
|
||||
std::fs::write(&env_path, &content).unwrap();
|
||||
|
||||
// Verify dotenvy parses ONBOARD_COMPLETED correctly
|
||||
let parsed: Vec<(String, String)> = dotenvy::from_path_iter(&env_path)
|
||||
.unwrap()
|
||||
.filter_map(|r| r.ok())
|
||||
.collect();
|
||||
assert_eq!(parsed.len(), 2);
|
||||
let onboard = parsed.iter().find(|(k, _)| k == "ONBOARD_COMPLETED");
|
||||
assert!(onboard.is_some(), "ONBOARD_COMPLETED must be present");
|
||||
assert_eq!(onboard.unwrap().1, "true");
|
||||
}
|
||||
}
|
||||
|
||||
+12
-5
@@ -1575,14 +1575,21 @@ fn check_onboard_needed() -> Option<&'static str> {
|
||||
return Some("Database not configured");
|
||||
}
|
||||
|
||||
// The wizard writes ONBOARD_COMPLETED=true to ~/.ironclaw/.env,
|
||||
// which load_ironclaw_env() loads before this function runs.
|
||||
if std::env::var("ONBOARD_COMPLETED")
|
||||
.map(|v| v == "true")
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
// First run (onboarding never completed and no session).
|
||||
// Reads NEARAI_API_KEY env var directly because this function runs
|
||||
// before Config is loaded -- Config::from_env() may fail without a
|
||||
// database URL, which is what triggers onboarding in the first place.
|
||||
// Check for a NEAR AI API key or session file as a fallback
|
||||
// for users who configured credentials manually (no wizard).
|
||||
if std::env::var("NEARAI_API_KEY").is_err() {
|
||||
let settings = ironclaw::settings::Settings::load();
|
||||
let session_path = ironclaw::llm::session::default_session_path();
|
||||
if !settings.onboard_completed && !session_path.exists() {
|
||||
if !session_path.exists() {
|
||||
return Some("First run");
|
||||
}
|
||||
}
|
||||
|
||||
+11
-9
@@ -19,8 +19,9 @@ Explicit invocation. Loads `.env` files, runs the wizard, exits.
|
||||
ironclaw (first run, no database configured)
|
||||
```
|
||||
|
||||
Auto-detection via `check_onboard_needed()` in `main.rs`. Triggers when
|
||||
none of these are true:
|
||||
Auto-detection via `check_onboard_needed()` in `main.rs`. Skips onboarding
|
||||
when `ONBOARD_COMPLETED` env var is set (written to `~/.ironclaw/.env` by
|
||||
the wizard). Otherwise triggers when no database is configured:
|
||||
- `DATABASE_URL` env var is set
|
||||
- `LIBSQL_PATH` env var is set
|
||||
- `~/.ironclaw/ironclaw.db` exists on disk
|
||||
@@ -345,13 +346,14 @@ Final step of the wizard:
|
||||
1. Mark onboard_completed = true
|
||||
2. Write ALL settings to database (try postgres pool, then libSQL backend)
|
||||
3. Write bootstrap vars to ~/.ironclaw/.env:
|
||||
- DATABASE_BACKEND (always)
|
||||
- DATABASE_URL (if postgres)
|
||||
- LIBSQL_PATH (if libsql)
|
||||
- LIBSQL_URL (if turso sync)
|
||||
- LLM_BACKEND (always, when set)
|
||||
- LLM_BASE_URL (if openai_compatible)
|
||||
- OLLAMA_BASE_URL (if ollama)
|
||||
- DATABASE_BACKEND (always)
|
||||
- DATABASE_URL (if postgres)
|
||||
- LIBSQL_PATH (if libsql)
|
||||
- LIBSQL_URL (if turso sync)
|
||||
- LLM_BACKEND (always, when set)
|
||||
- LLM_BASE_URL (if openai_compatible)
|
||||
- OLLAMA_BASE_URL (if ollama)
|
||||
- ONBOARD_COMPLETED (always, "true")
|
||||
4. Print configuration summary
|
||||
```
|
||||
|
||||
|
||||
@@ -1542,6 +1542,10 @@ impl SetupWizard {
|
||||
env_vars.push(("OLLAMA_BASE_URL", url.clone()));
|
||||
}
|
||||
|
||||
// Always write ONBOARD_COMPLETED so that check_onboard_needed()
|
||||
// (which runs before the DB is connected) knows to skip re-onboarding.
|
||||
env_vars.push(("ONBOARD_COMPLETED", "true".to_string()));
|
||||
|
||||
if !env_vars.is_empty() {
|
||||
let pairs: Vec<(&str, &str)> =
|
||||
env_vars.iter().map(|(k, v)| (*k, v.as_str())).collect();
|
||||
|
||||
Reference in New Issue
Block a user