mirror of
https://github.com/openai/codex.git
synced 2026-05-26 14:04:48 +00:00
## Why #22580 made app-server startup fail when the local SQLite state database cannot be initialized. Embedded/local TUI startup still continued on the permissive path, which left the CLI inconsistent and could hide a real startup problem behind unrelated UI. This brings local TUI startup onto the same fail-closed behavior while keeping recovery humane for the two failure modes we are seeing in practice: damaged database files and startup stalls caused by another process holding the database write lock. ## What changed - Embedded TUI startup now uses `state_db::try_init(...)` and returns a typed `LocalStateDbStartupError` that preserves the affected database path plus the underlying failure detail. - CLI startup handles that failure before entering the interactive TUI: - lock-contention failures tell users to quit other Codex processes and try again - failures consistent with a broken local database offer a safe repair that backs up Codex-owned SQLite files, rebuilds local database files, and retries startup once - declined or unsuccessful repairs print concise guidance plus technical details - Shared startup error plumbing lives in `tui/src/startup_error.rs`, while CLI recovery policy and focused recovery tests live in `cli/src/state_db_recovery.rs`. ## Verification - `cargo test -p codex-tui embedded_state_db_failure_is_typed_for_cli_recovery` - `cargo test -p codex-cli state_db_recovery` - Manually held an exclusive SQLite lock on `state_5.sqlite` and confirmed the CLI shows lock-specific guidance without offering repair. - Manually exercised the repair path with a deliberately invalid `sqlite_home` and confirmed it backs up the blocking path and resumes startup.
140 lines
4.1 KiB
Rust
140 lines
4.1 KiB
Rust
use clap::Args;
|
||
use clap::FromArgMatches;
|
||
use clap::Parser;
|
||
use codex_utils_cli::ApprovalModeCliArg;
|
||
use codex_utils_cli::CliConfigOverrides;
|
||
use codex_utils_cli::SharedCliOptions;
|
||
|
||
#[derive(Parser, Clone, Debug)]
|
||
#[command(version)]
|
||
pub struct Cli {
|
||
/// Optional user prompt to start the session.
|
||
#[arg(value_name = "PROMPT", value_hint = clap::ValueHint::Other)]
|
||
pub prompt: Option<String>,
|
||
|
||
/// Error out when config.toml contains fields that are not recognized by this version of Codex.
|
||
#[arg(long = "strict-config", default_value_t = false)]
|
||
pub strict_config: bool,
|
||
|
||
// Internal controls set by the top-level `codex resume` subcommand.
|
||
// These are not exposed as user flags on the base `codex` command.
|
||
#[clap(skip)]
|
||
pub resume_picker: bool,
|
||
|
||
#[clap(skip)]
|
||
pub resume_last: bool,
|
||
|
||
/// Internal: resume a specific recorded session by id (UUID). Set by the
|
||
/// top-level `codex resume <SESSION_ID>` wrapper; not exposed as a public flag.
|
||
#[clap(skip)]
|
||
pub resume_session_id: Option<String>,
|
||
|
||
/// Internal: show all sessions (disables cwd filtering and shows CWD column).
|
||
#[clap(skip)]
|
||
pub resume_show_all: bool,
|
||
|
||
/// Internal: include non-interactive sessions in resume listings.
|
||
#[clap(skip)]
|
||
pub resume_include_non_interactive: bool,
|
||
|
||
// Internal controls set by the top-level `codex fork` subcommand.
|
||
// These are not exposed as user flags on the base `codex` command.
|
||
#[clap(skip)]
|
||
pub fork_picker: bool,
|
||
|
||
#[clap(skip)]
|
||
pub fork_last: bool,
|
||
|
||
/// Internal: fork a specific recorded session by id (UUID). Set by the
|
||
/// top-level `codex fork <SESSION_ID>` wrapper; not exposed as a public flag.
|
||
#[clap(skip)]
|
||
pub fork_session_id: Option<String>,
|
||
|
||
/// Internal: show all sessions (disables cwd filtering and shows CWD column).
|
||
#[clap(skip)]
|
||
pub fork_show_all: bool,
|
||
|
||
#[clap(flatten)]
|
||
pub shared: TuiSharedCliOptions,
|
||
|
||
/// Configure when the model requires human approval before executing a command.
|
||
#[arg(long = "ask-for-approval", short = 'a')]
|
||
pub approval_policy: Option<ApprovalModeCliArg>,
|
||
|
||
/// Enable live web search. When enabled, the native Responses `web_search` tool is available to the model (no per‑call approval).
|
||
#[arg(long = "search", default_value_t = false)]
|
||
pub web_search: bool,
|
||
|
||
/// Disable alternate screen mode
|
||
///
|
||
/// Runs the TUI in inline mode, preserving terminal scrollback history.
|
||
#[arg(long = "no-alt-screen", default_value_t = false)]
|
||
pub no_alt_screen: bool,
|
||
|
||
#[clap(skip)]
|
||
pub config_overrides: CliConfigOverrides,
|
||
}
|
||
|
||
impl std::ops::Deref for Cli {
|
||
type Target = SharedCliOptions;
|
||
|
||
fn deref(&self) -> &Self::Target {
|
||
&self.shared.0
|
||
}
|
||
}
|
||
|
||
impl std::ops::DerefMut for Cli {
|
||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||
&mut self.shared.0
|
||
}
|
||
}
|
||
|
||
#[derive(Clone, Debug, Default)]
|
||
pub struct TuiSharedCliOptions(SharedCliOptions);
|
||
|
||
impl TuiSharedCliOptions {
|
||
pub fn into_inner(self) -> SharedCliOptions {
|
||
self.0
|
||
}
|
||
}
|
||
|
||
impl std::ops::Deref for TuiSharedCliOptions {
|
||
type Target = SharedCliOptions;
|
||
|
||
fn deref(&self) -> &Self::Target {
|
||
&self.0
|
||
}
|
||
}
|
||
|
||
impl std::ops::DerefMut for TuiSharedCliOptions {
|
||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||
&mut self.0
|
||
}
|
||
}
|
||
|
||
impl Args for TuiSharedCliOptions {
|
||
fn augment_args(cmd: clap::Command) -> clap::Command {
|
||
mark_tui_args(SharedCliOptions::augment_args(cmd))
|
||
}
|
||
|
||
fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
|
||
mark_tui_args(SharedCliOptions::augment_args_for_update(cmd))
|
||
}
|
||
}
|
||
|
||
impl FromArgMatches for TuiSharedCliOptions {
|
||
fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
|
||
SharedCliOptions::from_arg_matches(matches).map(Self)
|
||
}
|
||
|
||
fn update_from_arg_matches(&mut self, matches: &clap::ArgMatches) -> Result<(), clap::Error> {
|
||
self.0.update_from_arg_matches(matches)
|
||
}
|
||
}
|
||
|
||
fn mark_tui_args(cmd: clap::Command) -> clap::Command {
|
||
cmd.mut_arg("dangerously_bypass_approvals_and_sandbox", |arg| {
|
||
arg.conflicts_with("approval_policy")
|
||
})
|
||
}
|