fork: rename IronClaw → OptimClaw

Full rename of all identifiers, filenames, and references:
  ironclaw → optimclaw
  IronClaw → OptimClaw
  IRONCLAW → OPTIMCLAW
  ironclaw_common → optimclaw_common
  ironclaw_safety → optimclaw_safety

Upstream: nearai/ironclaw
This commit is contained in:
OutBack Dingo
2026-03-29 06:27:52 +07:00
parent 8a320ae9db
commit 6d9dbbb3b9
404 changed files with 2382 additions and 2382 deletions
+30 -30
View File
@@ -1,6 +1,6 @@
# Setup / Onboarding Specification
This document is the authoritative specification for IronClaw's onboarding
This document is the authoritative specification for OptimClaw's onboarding
wizard. Any code change to `src/setup/` **must** keep this document in sync.
If a future contributor or coding agent modifies setup behavior, update this
file first, then adjust the code to match.
@@ -10,21 +10,21 @@ file first, then adjust the code to match.
## Entry Points
```
ironclaw onboard [--skip-auth] [--channels-only] [--provider-only] [--quick]
optimclaw onboard [--skip-auth] [--channels-only] [--provider-only] [--quick]
```
Explicit invocation. Loads `.env` files, runs the wizard, exits.
```
ironclaw (first run, no database configured)
optimclaw (first run, no database configured)
```
Auto-detection via `check_onboard_needed()` in `main.rs`. Skips onboarding
when `ONBOARD_COMPLETED` env var is set (written to `~/.ironclaw/.env` by
when `ONBOARD_COMPLETED` env var is set (written to `~/.optimclaw/.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
- `~/.optimclaw/optimclaw.db` exists on disk
Auto-triggered onboarding uses **quick mode** by default.
@@ -38,7 +38,7 @@ The `--no-onboard` CLI flag suppresses auto-detection.
1. Parse CLI args
2. If Command::Onboard → load .env, run wizard, exit
3. If Command::Run or no command:
a. Load .env files (dotenvy::dotenv() then load_ironclaw_env())
a. Load .env files (dotenvy::dotenv() then load_optimclaw_env())
b. check_onboard_needed() → run wizard if needed
c. Config::from_env() → build config from env vars
d. Create SessionManager → load session token
@@ -48,7 +48,7 @@ The `--no-onboard` CLI flag suppresses auto-detection.
**Critical ordering:** `.env` files must be loaded (step 3a) before
`Config::from_env()` (step 3c) because bootstrap vars like
`DATABASE_BACKEND` live in `~/.ironclaw/.env`.
`DATABASE_BACKEND` live in `~/.optimclaw/.env`.
---
@@ -59,17 +59,17 @@ near-instant onboarding experience by auto-defaulting everything except
the LLM provider and model selection.
```
auto_setup_database() → libsql at ~/.ironclaw/ironclaw.db (zero prompts)
auto_setup_database() → libsql at ~/.optimclaw/optimclaw.db (zero prompts)
auto_setup_security() → keychain or env var (zero prompts)
Step 1/2: Inference Provider ← only interactive step
Step 2/2: Model Selection ← only interactive step
save_and_summarize() → includes tip to run `ironclaw onboard`
save_and_summarize() → includes tip to run `optimclaw onboard`
```
**`auto_setup_database()`:** Uses existing env vars if set (`DATABASE_URL`
for postgres, `LIBSQL_PATH` for libsql) without prompting. Otherwise
defaults to libsql at `~/.ironclaw/ironclaw.db`, creates the database,
defaults to libsql at `~/.optimclaw/optimclaw.db`, creates the database,
and runs migrations silently. Falls back to interactive mode only when
just the postgres feature is compiled and no `DATABASE_URL` is set.
@@ -82,7 +82,7 @@ except unavoidable macOS keychain dialogs.
`upsert_bootstrap_vars()` instead of `save_bootstrap_env()`, preserving
user-added variables like `HTTP_HOST` across re-onboarding.
The full 9-step wizard remains available via `ironclaw onboard`.
The full 9-step wizard remains available via `optimclaw onboard`.
---
@@ -144,7 +144,7 @@ Both features compiled?
3. Optionally run migrations
**libSQL path:**
1. Offer local path (default: `~/.ironclaw/ironclaw.db`)
1. Offer local path (default: `~/.optimclaw/optimclaw.db`)
2. Optional Turso cloud sync (URL + auth token)
3. Test connection via `connect_without_migrations()`
4. Always run migrations (idempotent CREATE IF NOT EXISTS)
@@ -180,7 +180,7 @@ SECRETS_MASTER_KEY env var set?
On macOS, `security_framework::get_generic_password()` can trigger TWO
system dialogs:
1. "Enter your password to unlock the keychain" (keychain locked)
2. "Allow ironclaw to access this keychain item" (per-app authorization)
2. "Allow optimclaw to access this keychain item" (per-app authorization)
This is OS-level behavior we cannot prevent. To minimize pain:
@@ -194,7 +194,7 @@ This is OS-level behavior we cannot prevent. To minimize pain:
Later calls to `init_secrets_context()` check this field first, avoiding
redundant keychain probes.
- **Never probe the keychain in read-only commands** (e.g., `ironclaw status`).
- **Never probe the keychain in read-only commands** (e.g., `optimclaw status`).
The status command reports "env not set (keychain may be configured)"
rather than triggering system dialogs.
@@ -253,10 +253,10 @@ with its own secret name and env var. It is **not** stored as `openai_compatible
(Responses API at `private.near.ai`, session token auth)
- Option 4: NEAR AI Cloud API key → **NEAR AI Cloud** mode
(Chat Completions API at `cloud-api.near.ai`, API key auth)
- **NEAR AI Chat** path: session token saved to `~/.ironclaw/session.json`.
- **NEAR AI Chat** path: session token saved to `~/.optimclaw/session.json`.
Hosting providers can set `NEARAI_SESSION_TOKEN` env var directly (takes
precedence over file-based tokens).
- **NEAR AI Cloud** path: `NEARAI_API_KEY` saved to `~/.ironclaw/.env`
- **NEAR AI Cloud** path: `NEARAI_API_KEY` saved to `~/.optimclaw/.env`
(bootstrap) and encrypted secrets store (`llm_nearai_api_key`).
`LlmConfig::resolve()` auto-selects `ChatCompletions` mode when the
API key is present.
@@ -321,7 +321,7 @@ key first, then falls back to the standard env var.
```
6a. Tunnel setup (if webhook channels needed)
6b. Discover WASM channels from ~/.ironclaw/channels/
6b. Discover WASM channels from ~/.optimclaw/channels/
6c. Build channel options: discovered + bundled + registry catalog
6d. Multi-select: CLI/TUI, HTTP, all available channels
6e. Install missing bundled channels (copy WASM binaries)
@@ -332,7 +332,7 @@ key first, then falls back to the standard env var.
```
**Channel sources** (priority order for installation):
1. Already installed in `~/.ironclaw/channels/`
1. Already installed in `~/.optimclaw/channels/`
2. Bundled channels (pre-compiled in `channels-src/`)
3. Registry channels (`registry/channels/*.json`, download-first with source fallback)
@@ -369,7 +369,7 @@ key first, then falls back to the standard env var.
1. Load `RegistryCatalog` from `registry/` directory
2. If registry not found, print info and skip
3. List all tool manifests from the catalog
4. Discover already-installed tools in `~/.ironclaw/tools/`
4. Discover already-installed tools in `~/.optimclaw/tools/`
5. Multi-select: show all registry tools with display name, auth method,
and description. Pre-check tools tagged `"default"` and already installed.
6. For each selected tool not yet installed, install via
@@ -405,14 +405,14 @@ Searches for `registry/` directory in order:
Settings are persisted in two places:
**Layer 1: `~/.ironclaw/.env`** (bootstrap vars)
**Layer 1: `~/.optimclaw/.env`** (bootstrap vars)
Contains only the settings needed BEFORE database connection. Written by
`save_bootstrap_env()` in `bootstrap.rs`.
```env
DATABASE_BACKEND="libsql"
LIBSQL_PATH="/Users/name/.ironclaw/ironclaw.db"
LIBSQL_PATH="/Users/name/.optimclaw/optimclaw.db"
SECRETS_MASTER_KEY="..." # only if env key source selected
ONBOARD_COMPLETED="true"
```
@@ -420,7 +420,7 @@ ONBOARD_COMPLETED="true"
Or for PostgreSQL:
```env
DATABASE_BACKEND="postgres"
DATABASE_URL="postgres://user:pass@localhost/ironclaw"
DATABASE_URL="postgres://user:pass@localhost/optimclaw"
SECRETS_MASTER_KEY="..."
ONBOARD_COMPLETED="true"
```
@@ -456,7 +456,7 @@ This prevents data loss if a later step fails (e.g., the user enters an
API key in step 3 but step 5 crashes — they won't need to re-enter it).
**`persist_after_step()`** is called after each step in `run()` and:
1. Writes bootstrap vars to `~/.ironclaw/.env` via `write_bootstrap_env()`
1. Writes bootstrap vars to `~/.optimclaw/.env` via `write_bootstrap_env()`
2. Writes all current settings to the database via `persist_settings()`
3. Silently ignores errors (e.g., if called before Step 1 establishes a DB)
@@ -492,7 +492,7 @@ Final step of the wizard:
4. Print configuration summary
```
Bootstrap vars written to `~/.ironclaw/.env` (only true chicken-and-egg vars
Bootstrap vars written to `~/.optimclaw/.env` (only true chicken-and-egg vars
that are needed before the DB is connected):
- `DATABASE_BACKEND` (always)
- `DATABASE_URL` (if postgres)
@@ -554,7 +554,7 @@ pub struct Settings {
// Step 7: Heartbeat
pub heartbeat: HeartbeatSettings, // enabled, interval, notify
// Advanced (not in wizard, set via `ironclaw config set`)
// Advanced (not in wizard, set via `optimclaw config set`)
pub agent: AgentSettings,
pub wasm: WasmSettings,
pub sandbox: SandboxSettings,
@@ -631,7 +631,7 @@ Must properly restore terminal state on all exit paths.
- Two dialogs per call is normal, not a bug
- Cache the result after first access to avoid repeat prompts
- Never probe keychain in read-only commands (`status`, `--help`)
- Service name: `"ironclaw"`, account: `"master_key"`
- Service name: `"optimclaw"`, account: `"master_key"`
### Linux Secret Service
@@ -649,15 +649,15 @@ local browser.
1. **NEAR AI Cloud API key (option 4 in auth menu):** Get an API key
from `https://cloud.near.ai` and paste it into the terminal. No
local listener is needed. The key is saved to `~/.ironclaw/.env`
local listener is needed. The key is saved to `~/.optimclaw/.env`
and the encrypted secrets store. Uses the OpenAI-compatible
ChatCompletions API mode.
2. **Custom callback URL:** Set `IRONCLAW_OAUTH_CALLBACK_URL` to a
2. **Custom callback URL:** Set `OPTIMCLAW_OAUTH_CALLBACK_URL` to a
publicly accessible URL (e.g., via SSH tunnel or reverse proxy) that
forwards to port 9876 on the server:
```bash
export IRONCLAW_OAUTH_CALLBACK_URL=https://myserver.example.com:9876
export OPTIMCLAW_OAUTH_CALLBACK_URL=https://myserver.example.com:9876
```
The `callback_url()` function in `oauth_defaults.rs` checks this env var
@@ -725,4 +725,4 @@ When changing the onboarding flow:
cargo clippy --all --benches --tests --examples --all-features -- -D warnings
cargo test --lib -- setup bootstrap
```
7. Test a fresh onboarding: `rm -rf ~/.ironclaw && cargo run`
7. Test a fresh onboarding: `rm -rf ~/.optimclaw && cargo run`
+2 -2
View File
@@ -307,7 +307,7 @@ async fn setup_tunnel_cloudflare() -> Result<TunnelSettings, ChannelSetupError>
}
/// Detect running cloudflared processes or managed services that could conflict
/// with IronClaw's tunnel management.
/// with OptimClaw's tunnel management.
fn detect_existing_cloudflared() -> Option<String> {
#[allow(unused_mut)]
let mut conflicts: Vec<String> = Vec::new();
@@ -1348,6 +1348,6 @@ mod tests {
let hint = http_webhook_secret_hint();
assert!(hint.contains("encrypted secrets database"));
assert!(hint.contains("loaded automatically on startup"));
assert!(!hint.contains("ironclaw secret get"));
assert!(!hint.contains("optimclaw secret get"));
}
}
+2 -2
View File
@@ -1,4 +1,4 @@
//! Interactive setup wizard for IronClaw.
//! Interactive setup wizard for OptimClaw.
//!
//! Provides a guided setup experience for:
//! 1. Database connection
@@ -16,7 +16,7 @@
//! # Example
//!
//! ```ignore
//! use ironclaw::setup::SetupWizard;
//! use optimclaw::setup::SetupWizard;
//!
//! let mut wizard = SetupWizard::new();
//! wizard.run().await?;
+2 -2
View File
@@ -305,7 +305,7 @@ pub fn confirm(prompt: &str, default: bool) -> io::Result<bool> {
pub fn print_banner() {
use crate::cli::fmt;
println!();
println!(" {}ironclaw{}", fmt::bold_accent(), fmt::reset());
println!(" {}optimclaw{}", fmt::bold_accent(), fmt::reset());
println!();
}
@@ -314,7 +314,7 @@ pub fn print_banner() {
/// # Example
///
/// ```ignore
/// print_header("IronClaw Setup Wizard");
/// print_header("OptimClaw Setup Wizard");
/// ```
pub fn print_header(text: &str) {
let width = text.len() + 4;
+34 -34
View File
@@ -18,7 +18,7 @@ use std::sync::Arc;
use deadpool_postgres::Config as PoolConfig;
use secrecy::{ExposeSecret, SecretString};
use crate::bootstrap::ironclaw_base_dir;
use crate::bootstrap::optimclaw_base_dir;
use crate::channels::wasm::{
ChannelCapabilitiesFile, available_channel_names, install_bundled_channel,
};
@@ -88,7 +88,7 @@ pub struct SetupConfig {
pub steps: Vec<String>,
}
/// Interactive setup wizard for IronClaw.
/// Interactive setup wizard for OptimClaw.
pub struct SetupWizard {
config: SetupConfig,
settings: Settings,
@@ -188,7 +188,7 @@ impl SetupWizard {
/// connection, so users don't have to re-enter everything.
pub async fn run(&mut self) -> Result<(), SetupError> {
print_banner();
print_header("IronClaw Setup Wizard");
print_header("OptimClaw Setup Wizard");
if !self.config.steps.is_empty() {
// Selective step mode: reconnect to existing DB and load settings,
@@ -485,7 +485,7 @@ impl SetupWizard {
#[allow(unreachable_code)]
Err(SetupError::Database(
"No database configured. Run full setup first (ironclaw onboard).".to_string(),
"No database configured. Run full setup first (optimclaw onboard).".to_string(),
))
}
@@ -494,7 +494,7 @@ impl SetupWizard {
async fn reconnect_postgres(&mut self) -> Result<(), SetupError> {
let url = std::env::var("DATABASE_URL").map_err(|_| {
SetupError::Database(
"DATABASE_URL not set. Run full setup first (ironclaw onboard).".to_string(),
"DATABASE_URL not set. Run full setup first (optimclaw onboard).".to_string(),
)
})?;
@@ -726,7 +726,7 @@ impl SetupWizard {
}
println!();
print_info("IronClaw uses an embedded SQLite database (libSQL).");
print_info("OptimClaw uses an embedded SQLite database (libSQL).");
print_info("No external database server required.");
println!();
@@ -825,7 +825,7 @@ impl SetupWizard {
if major_version < MIN_PG_MAJOR_VERSION {
return Err(SetupError::Database(format!(
"PostgreSQL {} detected. IronClaw requires PostgreSQL {} or later for pgvector support.\n\
"PostgreSQL {} detected. OptimClaw requires PostgreSQL {} or later for pgvector support.\n\
Upgrade: https://www.postgresql.org/download/",
version_str, MIN_PG_MAJOR_VERSION
)));
@@ -850,7 +850,7 @@ impl SetupWizard {
Ubuntu: apt install postgresql-{0}-pgvector\n \
Docker: use the pgvector/pgvector:pg{0} image\n \
Source: https://github.com/pgvector/pgvector#installation\n\n\
Then restart PostgreSQL and re-run: ironclaw onboard",
Then restart PostgreSQL and re-run: optimclaw onboard",
major_version
)));
}
@@ -1028,11 +1028,11 @@ impl SetupWizard {
// Make visible to optional_env() for any subsequent config resolution.
crate::config::inject_single_var("SECRETS_MASTER_KEY", &key_hex);
// Store hex for write_bootstrap_env to persist to ~/.ironclaw/.env.
// Store hex for write_bootstrap_env to persist to ~/.optimclaw/.env.
self.settings.secrets_master_key_hex = Some(key_hex.clone());
println!();
print_info("Master key generated and will be saved to ~/.ironclaw/.env");
print_info("Master key generated and will be saved to ~/.optimclaw/.env");
println!();
println!(" SECRETS_MASTER_KEY={}", key_hex);
println!();
@@ -1180,7 +1180,7 @@ impl SetupWizard {
crate::config::inject_single_var("SECRETS_MASTER_KEY", &key_hex);
self.settings.secrets_master_key_hex = Some(key_hex);
self.settings.secrets_master_key_source = KeySource::Env;
print_success("Master key stored in ~/.ironclaw/.env");
print_success("Master key stored in ~/.optimclaw/.env");
Ok(())
}
@@ -1630,7 +1630,7 @@ impl SetupWizard {
.await
.map_err(|e| SetupError::Auth(e.to_string()))?;
print_info("Authorize IronClaw with GitHub Copilot in your browser.");
print_info("Authorize OptimClaw with GitHub Copilot in your browser.");
print_info(&format!("Verification URL: {}", device.verification_uri));
print_info(&format!("One-time code: {}", device.user_code));
@@ -2516,7 +2516,7 @@ impl SetupWizard {
println!();
// Discover available WASM channels
let channels_dir = ironclaw_base_dir().join("channels");
let channels_dir = optimclaw_base_dir().join("channels");
let mut discovered_channels = discover_wasm_channels(&channels_dir).await;
let installed_names: HashSet<String> = discovered_channels
@@ -2715,7 +2715,7 @@ impl SetupWizard {
Some(c) => c,
None => {
print_info("Extension registry not found. Skipping tool installation.");
print_info("Install tools manually with: ironclaw tool install <path>");
print_info("Install tools manually with: optimclaw tool install <path>");
return Ok(());
}
};
@@ -2733,11 +2733,11 @@ impl SetupWizard {
print_info("Available tools from the extension registry:");
print_info("Select which tools to install. You can install more later with:");
print_info(" ironclaw registry install <name>");
print_info(" optimclaw registry install <name>");
println!();
// Check which tools are already installed
let tools_dir = ironclaw_base_dir().join("tools");
let tools_dir = optimclaw_base_dir().join("tools");
let installed_tools = discover_installed_tools(&tools_dir).await;
@@ -2777,7 +2777,7 @@ impl SetupWizard {
let installer = crate::registry::installer::RegistryInstaller::new(
repo_root.to_path_buf(),
tools_dir.clone(),
ironclaw_base_dir().join("channels"),
optimclaw_base_dir().join("channels"),
);
let mut installed_count = 0;
@@ -2804,7 +2804,7 @@ impl SetupWizard {
{
let provider = auth.provider.as_deref().unwrap_or(&tool.name);
// Only mention unique providers (Google tools share auth)
let hint = format!(" {} - ironclaw tool auth {}", provider, tool.name);
let hint = format!(" {} - optimclaw tool auth {}", provider, tool.name);
if !auth_needed
.iter()
.any(|h| h.starts_with(&format!(" {} -", provider)))
@@ -2837,7 +2837,7 @@ impl SetupWizard {
/// Step 8: Docker Sandbox -- check Docker installation and availability.
async fn step_docker_sandbox(&mut self) -> Result<(), SetupError> {
print_info("IronClaw can execute code, run builds, and use tools inside Docker");
print_info("OptimClaw can execute code, run builds, and use tools inside Docker");
print_info("containers. This keeps your system safe -- commands from the LLM run");
print_info("in an isolated sandbox with no access to your credentials, limited");
print_info("filesystem access, and network traffic restricted to an allowlist.");
@@ -3060,7 +3060,7 @@ impl SetupWizard {
Ok(saved)
}
/// Write bootstrap environment variables to `~/.ironclaw/.env`.
/// Write bootstrap environment variables to `~/.optimclaw/.env`.
///
/// Only true chicken-and-egg settings are written here — things needed
/// before the database is connected: `DATABASE_BACKEND`, `DATABASE_URL`,
@@ -3286,7 +3286,7 @@ impl SetupWizard {
let _ = loaded;
}
/// Save settings to the database and `~/.ironclaw/.env`, then print
/// Save settings to the database and `~/.optimclaw/.env`, then print
/// a warm completion card with the 3 key facts.
async fn save_and_summarize(&mut self) -> Result<(), SetupError> {
use crate::cli::fmt;
@@ -3313,9 +3313,9 @@ impl SetupWizard {
println!(" {}", sep);
println!();
// Title line: checkmark + "ironclaw is ready"
// Title line: checkmark + "optimclaw is ready"
println!(
" {}\u{2713}{} {}ironclaw is ready{}",
" {}\u{2713}{} {}optimclaw is ready{}",
fmt::success(),
fmt::reset(),
fmt::bold_accent(),
@@ -3395,14 +3395,14 @@ impl SetupWizard {
// Action hints
println!(
" {}Start chatting:{} {}ironclaw{}",
" {}Start chatting:{} {}optimclaw{}",
fmt::dim(),
fmt::reset(),
fmt::bold_accent(),
fmt::reset(),
);
println!(
" {}Full setup:{} {}ironclaw onboard{}",
" {}Full setup:{} {}optimclaw onboard{}",
fmt::dim(),
fmt::reset(),
fmt::bold_accent(),
@@ -3412,7 +3412,7 @@ impl SetupWizard {
if self.config.quick {
print_info(
"Tip: Run `ironclaw onboard` to configure channels, extensions, embeddings, and more.",
"Tip: Run `optimclaw onboard` to configure channels, extensions, embeddings, and more.",
);
println!();
}
@@ -3641,7 +3641,7 @@ async fn install_selected_registry_channels(
let installer = crate::registry::installer::RegistryInstaller::new(
repo_root.clone(),
ironclaw_base_dir().join("tools"),
optimclaw_base_dir().join("tools"),
channels_dir.to_path_buf(),
);
@@ -3761,7 +3761,7 @@ mod tests {
#[test]
fn test_wizard_owner_id_uses_resolved_env_scope() {
let _guard = lock_env();
let _owner = EnvGuard::set("IRONCLAW_OWNER_ID", " wizard-owner ");
let _owner = EnvGuard::set("OPTIMCLAW_OWNER_ID", " wizard-owner ");
let wizard = SetupWizard::new();
assert_eq!(wizard.owner_id(), "wizard-owner"); // safety: test-only assertion
@@ -3770,7 +3770,7 @@ mod tests {
#[test]
fn test_wizard_owner_id_uses_toml_scope() {
let _guard = lock_env();
let _owner = EnvGuard::clear("IRONCLAW_OWNER_ID");
let _owner = EnvGuard::clear("OPTIMCLAW_OWNER_ID");
let dir = tempdir().unwrap(); // safety: test-only tempdir setup
let path = dir.path().join("config.toml");
std::fs::write(&path, "owner_id = \"toml-owner\"\n").unwrap(); // safety: test-only fixture write
@@ -3786,18 +3786,18 @@ mod tests {
use std::os::unix::ffi::OsStringExt;
let _guard = lock_env();
let original = std::env::var_os("IRONCLAW_OWNER_ID");
let original = std::env::var_os("OPTIMCLAW_OWNER_ID");
unsafe {
std::env::set_var("IRONCLAW_OWNER_ID", OsString::from_vec(vec![0x66, 0x80]));
std::env::set_var("OPTIMCLAW_OWNER_ID", OsString::from_vec(vec![0x66, 0x80]));
}
let result = SetupWizard::try_with_config_and_toml(Default::default(), None);
unsafe {
if let Some(value) = original {
std::env::set_var("IRONCLAW_OWNER_ID", value);
std::env::set_var("OPTIMCLAW_OWNER_ID", value);
} else {
std::env::remove_var("IRONCLAW_OWNER_ID");
std::env::remove_var("OPTIMCLAW_OWNER_ID");
}
}
@@ -3994,7 +3994,7 @@ mod tests {
#[tokio::test]
async fn test_discover_wasm_channels_nonexistent_dir() {
let channels = discover_wasm_channels(
&std::env::temp_dir().join("ironclaw_nonexistent_dir_abcxyz123"),
&std::env::temp_dir().join("optimclaw_nonexistent_dir_abcxyz123"),
)
.await;
assert!(channels.is_empty());