diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-06-10 22:26:10 +0200 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2026-06-10 22:26:10 +0200 |
| commit | 989f01ad230423c5a5105d6c9ff8580020e902ed (patch) | |
| tree | b750d06a9a39dcc76a1da7898b0748b0dad500cf | |
| parent | chore: Remove some unused rust code (diff) | |
| download | atuin-989f01ad230423c5a5105d6c9ff8580020e902ed.zip | |
chore: Remove more useless code
27 files changed, 59 insertions, 1382 deletions
diff --git a/crates/atuin-client/Cargo.toml b/crates/atuin-client/Cargo.toml index 1b007f2e..b5ac49a4 100644 --- a/crates/atuin-client/Cargo.toml +++ b/crates/atuin-client/Cargo.toml @@ -13,9 +13,8 @@ repository = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["sync", "hub", "daemon"] +default = ["sync", "daemon"] sync = ["urlencoding", "reqwest", "sha2", "hex"] -hub = ["reqwest"] daemon = [] check-update = [] diff --git a/crates/atuin-client/src/auth.rs b/crates/atuin-client/src/auth.rs index 8ea4b8ab..1031c11f 100644 --- a/crates/atuin-client/src/auth.rs +++ b/crates/atuin-client/src/auth.rs @@ -1,13 +1,9 @@ use async_trait::async_trait; use eyre::{Context, Result, bail}; -use reqwest::{StatusCode, Url, header::USER_AGENT}; -use serde::Deserialize; +use reqwest::{Url, header::USER_AGENT}; use atuin_common::{ - api::{ - ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ChangePasswordRequest, LoginRequest, - LoginResponse, RegisterResponse, - }, + api::{ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, ChangePasswordRequest, LoginRequest}, tls::ensure_crypto_provider, }; @@ -74,20 +70,12 @@ pub trait AuthClient: Send + Sync { /// Resolve the appropriate [`AuthClient`] for the current settings. pub async fn auth_client(settings: &Settings) -> Box<dyn AuthClient> { - if settings.is_hub_sync() { - let endpoint = settings.active_hub_endpoint().unwrap_or_default(); - Box::new(HubAuthClient::new( - endpoint.as_ref(), - settings.hub_session_token().await.ok(), - )) as Box<dyn AuthClient> - } else { - Box::new(LegacyAuthClient::new( - &settings.sync_address, - settings.session_token().await.ok(), - settings.network_connect_timeout, - settings.network_timeout, - )) as Box<dyn AuthClient> - } + Box::new(LegacyAuthClient::new( + &settings.sync_address, + settings.session_token().await.ok(), + settings.network_connect_timeout, + settings.network_timeout, + )) as Box<dyn AuthClient> } // --------------------------------------------------------------------------- @@ -221,247 +209,6 @@ impl AuthClient for LegacyAuthClient { } // --------------------------------------------------------------------------- -// Hub backend — talks to the Hub v0 API endpoints -// --------------------------------------------------------------------------- - -pub struct HubAuthClient { - address: String, - hub_token: Option<String>, -} - -impl HubAuthClient { - pub fn new(address: &str, hub_token: Option<String>) -> Self { - Self { - address: address.trim_end_matches('/').to_string(), - hub_token, - } - } -} - -/// Hub v0 error/status response — includes an optional `code` field for -/// machine-readable status like `"2fa_required"`. -#[derive(Debug, Deserialize)] -struct HubErrorResponse { - reason: String, - code: Option<String>, -} - -#[async_trait] -impl AuthClient for HubAuthClient { - async fn login( - &self, - username: &str, - password: &str, - totp_code: Option<&str>, - ) -> Result<AuthResponse> { - ensure_crypto_provider(); - let url = make_url(&self.address, "/api/v0/login")?; - let client = reqwest::Client::new(); - - let mut body = serde_json::json!({ - "username": username, - "password": password, - }); - if let Some(code) = totp_code { - body["totp_code"] = serde_json::Value::String(code.to_string()); - } - - let resp = client - .post(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .json(&body) - .send() - .await - .context("failed to connect to Atuin Hub")?; - - let status = resp.status(); - - if status.is_success() { - let login: LoginResponse = resp.json().await?; - return Ok(AuthResponse::Success { - session: login.session, - auth_type: login.auth, - }); - } - - if status == StatusCode::FORBIDDEN - && let Ok(err) = resp.json::<HubErrorResponse>().await - { - if err.code.as_deref() == Some("2fa_required") { - return Ok(AuthResponse::TwoFactorRequired); - } - bail!("{}", err.reason); - } - - if status == StatusCode::UNAUTHORIZED { - bail!("invalid credentials"); - } - - bail!("Hub login failed with status {status}"); - } - - async fn register(&self, username: &str, email: &str, password: &str) -> Result<AuthResponse> { - ensure_crypto_provider(); - let url = make_url(&self.address, "/api/v0/register")?; - let client = reqwest::Client::new(); - - let resp = client - .post(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .json(&serde_json::json!({ - "email": email, - "username": username, - "password": password, - })) - .send() - .await - .context("failed to connect to Atuin Hub")?; - - let status = resp.status(); - - if status.is_success() { - let reg: RegisterResponse = resp.json().await?; - return Ok(AuthResponse::Success { - session: reg.session, - auth_type: reg.auth, - }); - } - - if let Ok(err) = resp.json::<HubErrorResponse>().await { - bail!("{}", err.reason); - } - - bail!("Hub registration failed with status {status}"); - } - - async fn change_password( - &self, - current_password: &str, - new_password: &str, - totp_code: Option<&str>, - ) -> Result<MutateResponse> { - let hub_token = self.hub_token.as_deref().ok_or_else(|| { - eyre::eyre!( - "Not logged in to Atuin Hub. \ - Please run 'atuin login' to authenticate." - ) - })?; - - if !hub_token.starts_with("atapi_") { - bail!( - "Your Hub session token is invalid. \ - Please run 'atuin login' to re-authenticate with Atuin Hub." - ); - } - - ensure_crypto_provider(); - let url = make_url(&self.address, "/api/v0/account/password")?; - let client = reqwest::Client::new(); - - let mut body = serde_json::json!({ - "current_password": current_password, - "new_password": new_password, - }); - if let Some(code) = totp_code { - body["totp_code"] = serde_json::Value::String(code.to_string()); - } - - let resp = client - .patch(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .bearer_auth(hub_token) - .json(&body) - .send() - .await - .context("failed to connect to Atuin Hub")?; - - let status = resp.status(); - - if status.is_success() { - return Ok(MutateResponse::Success); - } - - if let Ok(err) = resp.json::<HubErrorResponse>().await { - match err.code.as_deref() { - Some("2fa_required") => return Ok(MutateResponse::TwoFactorRequired), - Some("invalid_2fa_code") => bail!("invalid two-factor code"), - _ => bail!("{}", err.reason), - } - } - - match status { - StatusCode::UNAUTHORIZED => bail!("current password is incorrect"), - StatusCode::FORBIDDEN => bail!("invalid login details"), - _ => bail!("Hub password change failed with status {status}"), - } - } - - async fn delete_account( - &self, - password: &str, - totp_code: Option<&str>, - ) -> Result<MutateResponse> { - let hub_token = self.hub_token.as_deref().ok_or_else(|| { - eyre::eyre!( - "Not logged in to Atuin Hub. \ - Please run 'atuin login' to authenticate." - ) - })?; - - if !hub_token.starts_with("atapi_") { - bail!( - "Your Hub session token is invalid. \ - Please run 'atuin login' to re-authenticate with Atuin Hub." - ); - } - - ensure_crypto_provider(); - let url = make_url(&self.address, "/api/v0/account")?; - let client = reqwest::Client::new(); - - let mut body = serde_json::json!({ - "password": password, - }); - if let Some(code) = totp_code { - body["totp_code"] = serde_json::Value::String(code.to_string()); - } - - let resp = client - .delete(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .bearer_auth(hub_token) - .json(&body) - .send() - .await - .context("failed to connect to Atuin Hub")?; - - let status = resp.status(); - - if status.is_success() { - return Ok(MutateResponse::Success); - } - - if let Ok(err) = resp.json::<HubErrorResponse>().await { - match err.code.as_deref() { - Some("2fa_required") => return Ok(MutateResponse::TwoFactorRequired), - Some("invalid_2fa_code") => bail!("invalid two-factor code"), - _ => bail!("{}", err.reason), - } - } - - match status { - StatusCode::UNAUTHORIZED => bail!("password is incorrect"), - StatusCode::FORBIDDEN => bail!("invalid login details"), - _ => bail!("Hub account deletion failed with status {status}"), - } - } -} - -// --------------------------------------------------------------------------- // Shared helpers // --------------------------------------------------------------------------- diff --git a/crates/atuin-client/src/hub.rs b/crates/atuin-client/src/hub.rs deleted file mode 100644 index 2e40aad4..00000000 --- a/crates/atuin-client/src/hub.rs +++ /dev/null @@ -1,304 +0,0 @@ -//! Hub authentication support for Atuin -//! -//! This module provides programmatic access to the Atuin Hub authentication flow. -//! It can be used by other crates (like atuin-ai) to authenticate with the Hub -//! and obtain session tokens. -//! -//! Hub authentication is separate from sync authentication - users can have both -//! a sync session (for history sync) and a hub session (for Hub-specific features -//! like AI). - -use std::time::Duration; - -use eyre::{Context, Result, bail}; -use reqwest::{StatusCode, Url, header::USER_AGENT}; - -use atuin_common::{ - api::{ - ATUIN_CARGO_VERSION, ATUIN_HEADER_VERSION, CliCodeResponse, CliVerifyResponse, - ErrorResponse, - }, - tls::ensure_crypto_provider, -}; - -use crate::settings::Settings; - -static APP_USER_AGENT: &str = concat!("atuin/", env!("CARGO_PKG_VERSION")); - -/// The result of starting a hub authentication flow -#[derive(Debug, Clone)] -pub struct HubAuthSession { - /// The code to be verified - pub code: String, - /// The URL the user should visit to authenticate - pub auth_url: String, - /// The hub address being used - pub hub_address: String, -} - -/// The result of polling for hub auth completion -#[derive(Debug, Clone)] -pub enum HubAuthStatus { - /// Still waiting for user authorization - Pending, - /// Authorization complete, contains the session token - Complete(String), - /// Authorization failed with an error - Failed(String), -} - -/// Default poll interval for checking auth status -pub const DEFAULT_POLL_INTERVAL: Duration = Duration::from_secs(2); - -/// Default timeout for the entire auth flow -pub const DEFAULT_AUTH_TIMEOUT: Duration = Duration::from_secs(600); - -impl HubAuthSession { - /// Start a new hub authentication session - /// - /// Returns a session containing the code and auth URL that the user should visit. - pub async fn start(hub_address: &str) -> Result<Self> { - debug!("Starting Hub authentication process..."); - - let hub_address = hub_address.trim_end_matches('/'); - let code_response = request_code(hub_address) - .await - .context("Failed to request authentication code from Hub")?; - - debug!("Received code from Hub"); - - let code = code_response.code; - let auth_url = format!("{}/auth/cli?code={}", hub_address, code); - - Ok(Self { - code, - auth_url, - hub_address: hub_address.to_string(), - }) - } - - /// Poll for the authentication status - /// - /// Returns the current status of the authentication flow. - pub async fn poll(&self) -> Result<HubAuthStatus> { - match verify_code(&self.hub_address, &self.code).await { - Ok(response) => { - if let Some(token) = response.token { - debug!("Authentication complete, received token"); - Ok(HubAuthStatus::Complete(token)) - } else if let Some(error) = response.error { - error!("Authentication failed: {}", error); - Ok(HubAuthStatus::Failed(error)) - } else { - Ok(HubAuthStatus::Pending) - } - } - Err(e) => { - // Transient errors shouldn't fail the whole flow - log::debug!("Verification poll failed: {}", e); - Ok(HubAuthStatus::Pending) - } - } - } - - /// Poll until completion or timeout - /// - /// This is a convenience method that polls repeatedly until the auth completes - /// or times out. - pub async fn wait_for_completion( - &self, - timeout: Duration, - poll_interval: Duration, - ) -> Result<String> { - let start = std::time::Instant::now(); - - debug!("Polling for Hub authentication completion..."); - - loop { - if start.elapsed() > timeout { - warn!("Authentication loop exited due to timeout"); - bail!("Authentication timed out. Please try again."); - } - - match self.poll().await? { - HubAuthStatus::Complete(token) => return Ok(token), - HubAuthStatus::Failed(error) => bail!("Authentication failed: {}", error), - HubAuthStatus::Pending => { - tokio::time::sleep(poll_interval).await; - } - } - } - } -} - -/// Save a hub session token -/// -/// This saves the token to the meta store so it can be used for subsequent Hub API calls. -/// Note: This is separate from the sync session token. -pub async fn save_session(token: &str) -> Result<()> { - Settings::meta_store() - .await? - .save_hub_session(token) - .await - .context("Failed to save hub session") -} - -/// Delete the hub session token (logout from Hub) -pub async fn delete_session() -> Result<()> { - Settings::meta_store() - .await? - .delete_hub_session() - .await - .context("Failed to delete hub session") -} - -/// Check if the user is logged in with Hub authentication -/// -/// Returns true if the user has a valid Hub session token. -/// This is independent of whether they have a sync session. -pub async fn is_logged_in() -> Result<bool> { - Settings::meta_store().await?.hub_logged_in().await -} - -/// Get the hub session token if available -/// -/// Returns the Hub session token if the user is logged in with Hub auth, -/// or None if not logged in. -pub async fn get_session_token() -> Result<Option<String>> { - Settings::meta_store().await?.hub_session_token().await -} - -/// Link an existing CLI sync account to the current Hub user. -/// -/// This associates the CLI's sync records with the Hub account, enabling -/// unified authentication. After linking: -/// - The Hub token can be used for sync operations -/// - Records are migrated to be accessible via Hub auth -/// -/// Requires: -/// - A valid Hub session (user must be logged in to Hub) -/// - A valid CLI session token to link -/// -/// Returns Ok(()) on success, or an error if: -/// - Not logged in to Hub -/// - CLI token is invalid -/// - CLI account is already linked to a different Hub account -pub async fn link_account(hub_address: &str, cli_token: &str) -> Result<()> { - let hub_token = get_session_token() - .await? - .ok_or_else(|| eyre::eyre!("Not logged in to Hub - cannot link account"))?; - - let url = make_url(hub_address, "/api/v0/account/link")?; - - debug!("Linking CLI account to Hub at {}", hub_address); - - ensure_crypto_provider(); - let client = reqwest::Client::new(); - - let resp = client - .post(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .bearer_auth(&hub_token) - .json(&serde_json::json!({ "token": cli_token })) - .send() - .await?; - - let status = resp.status(); - - if status == StatusCode::CONFLICT { - // 409 means CLI account is already linked to a (possibly different) Hub account - debug!("CLI account already linked to a Hub account"); - return Ok(()); - } - - handle_resp_error(resp).await?; - - info!("Successfully linked CLI account to Hub"); - Ok(()) -} - -// --- Internal HTTP functions --- - -fn make_url(address: &str, path: &str) -> Result<String> { - let address = if address.ends_with('/') { - address.to_string() - } else { - format!("{address}/") - }; - - let path = path.strip_prefix('/').unwrap_or(path); - - let url = Url::parse(&address) - .context("failed to parse hub address")? - .join(path) - .context("failed to join hub URL path")?; - - Ok(url.to_string()) -} - -async fn handle_resp_error(resp: reqwest::Response) -> Result<reqwest::Response> { - let status = resp.status(); - - if status == StatusCode::SERVICE_UNAVAILABLE { - error!("Service unavailable: check https://status.atuin.sh"); - bail!("Service unavailable: check https://status.atuin.sh"); - } - - if status == StatusCode::TOO_MANY_REQUESTS { - error!("Rate limited; please wait before trying again"); - bail!("Rate limited; please wait before trying again"); - } - - if !status.is_success() { - if let Ok(error) = resp.json::<ErrorResponse>().await { - error!("Hub error: {} - {}", status, error.reason); - bail!("Hub error: {} - {}", status, error.reason); - } - error!("Hub request failed with status: {}", status); - bail!("Hub request failed with status: {}", status); - } - - Ok(resp) -} - -/// Request a CLI auth code from the Atuin Hub -async fn request_code(address: &str) -> Result<CliCodeResponse> { - ensure_crypto_provider(); - let url = make_url(address, "/auth/cli/code")?; - let client = reqwest::Client::new(); - - debug!("Requesting code from Hub at {url}"); - - let resp = client - .post(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .send() - .await?; - let resp = handle_resp_error(resp).await?; - - let code_response = resp.json::<CliCodeResponse>().await?; - Ok(code_response) -} - -/// Poll to verify the CLI auth code and get the session token -async fn verify_code(address: &str, code: &str) -> Result<CliVerifyResponse> { - ensure_crypto_provider(); - let base = make_url(address, "/auth/cli/verify")?; - let url = format!("{base}?code={code}"); - let client = reqwest::Client::new(); - - debug!("Verifying code with Hub at {base}?code=******"); - - let resp = client - .post(&url) - .header(USER_AGENT, APP_USER_AGENT) - .header(ATUIN_HEADER_VERSION, ATUIN_CARGO_VERSION) - .send() - .await?; - let resp = handle_resp_error(resp).await?; - - let verify_response = resp.json::<CliVerifyResponse>().await?; - Ok(verify_response) -} diff --git a/crates/atuin-client/src/lib.rs b/crates/atuin-client/src/lib.rs index 7938176a..cd7785e1 100644 --- a/crates/atuin-client/src/lib.rs +++ b/crates/atuin-client/src/lib.rs @@ -7,8 +7,6 @@ extern crate log; pub mod api_client; #[cfg(feature = "sync")] pub mod auth; -#[cfg(feature = "hub")] -pub mod hub; #[cfg(feature = "sync")] pub mod login; #[cfg(feature = "sync")] diff --git a/crates/atuin-client/src/logout.rs b/crates/atuin-client/src/logout.rs index 80f0ad73..f720b302 100644 --- a/crates/atuin-client/src/logout.rs +++ b/crates/atuin-client/src/logout.rs @@ -7,7 +7,6 @@ pub async fn logout() -> Result<()> { if meta.logged_in().await? { meta.delete_session().await?; - meta.delete_hub_session().await?; println!("You have logged out!"); } else { println!("You are not logged in"); diff --git a/crates/atuin-client/src/meta.rs b/crates/atuin-client/src/meta.rs index eb6dd8cf..870f36d0 100644 --- a/crates/atuin-client/src/meta.rs +++ b/crates/atuin-client/src/meta.rs @@ -21,7 +21,6 @@ const KEY_LAST_SYNC: &str = "last_sync_time"; const KEY_LAST_VERSION_CHECK: &str = "last_version_check_time"; const KEY_LATEST_VERSION: &str = "latest_version"; const KEY_SESSION: &str = "session"; -const KEY_HUB_SESSION: &str = "hub_session"; const KEY_FILES_MIGRATED: &str = "files_migrated"; pub struct MetaStore { @@ -187,25 +186,7 @@ impl MetaStore { } pub async fn logged_in(&self) -> Result<bool> { - Ok(self.session_token().await?.is_some() || self.hub_session_token().await?.is_some()) - } - - // Hub session methods (separate from sync session, used for Hub-specific features like AI) - - pub async fn hub_session_token(&self) -> Result<Option<String>> { - self.get(KEY_HUB_SESSION).await - } - - pub async fn save_hub_session(&self, token: &str) -> Result<()> { - self.set(KEY_HUB_SESSION, token).await - } - - pub async fn delete_hub_session(&self) -> Result<()> { - self.delete(KEY_HUB_SESSION).await - } - - pub async fn hub_logged_in(&self) -> Result<bool> { - Ok(self.hub_session_token().await?.is_some()) + Ok(self.session_token().await?.is_some()) } // File migration: on first open, migrate old plain-text files into the database. diff --git a/crates/atuin-client/src/settings.rs b/crates/atuin-client/src/settings.rs index 1be6f363..ce84c053 100644 --- a/crates/atuin-client/src/settings.rs +++ b/crates/atuin-client/src/settings.rs @@ -11,7 +11,6 @@ use eyre::{Context, Error, Result, bail, eyre}; use fs_err::{File, create_dir_all}; use humantime::parse_duration; use regex::RegexSet; -use semver::Version; use serde::{Deserialize, Serialize}; use serde_with::DeserializeFromStr; use time::{OffsetDateTime, UtcOffset, format_description::FormatItem, macros::format_description}; @@ -23,32 +22,12 @@ static DATA_DIR: OnceLock<PathBuf> = OnceLock::new(); static META_CONFIG: OnceLock<(String, f64)> = OnceLock::new(); static META_STORE: OnceCell<crate::meta::MetaStore> = OnceCell::const_new(); -mod dotfiles; -mod kv; pub(crate) mod meta; -mod scripts; pub mod watcher; -pub struct HubEndpoint(String); - /// Default sync address for Atuin's hosted service pub const DEFAULT_SYNC_ADDRESS: &str = "https://api.atuin.sh"; -/// Default Hub web/API endpoint for Atuin's hosted service -pub const DEFAULT_HUB_ENDPOINT: &str = "https://hub.atuin.sh"; - -impl Default for HubEndpoint { - fn default() -> Self { - HubEndpoint(DEFAULT_HUB_ENDPOINT.to_string()) - } -} - -impl AsRef<str> for HubEndpoint { - fn as_ref(&self) -> &str { - &self.0 - } -} - #[derive(Clone, Debug, Deserialize, Copy, ValueEnum, PartialEq, Serialize)] pub enum SearchMode { #[serde(rename = "prefix")] @@ -374,13 +353,9 @@ pub struct Sync { #[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize, Default)] #[serde(rename_all = "lowercase")] pub enum SyncProtocol { - /// Use Hub authentication (Bearer token from Hub OAuth flow) - Hub, /// Use legacy CLI authentication (Token from CLI register/login) - Legacy, - /// Infer from sync_address (default behavior) #[default] - Auto, + Legacy, } /// Resolved authentication state for sync operations. @@ -394,14 +369,7 @@ pub enum SyncAuth { /// Self-hosted Rust server. Uses `Authorization: Token <session>` and /// legacy endpoints. Legacy { token: String }, - /// Hub with a valid Hub API token (`atapi_*`). Uses - /// `Authorization: Bearer <token>` and v0 endpoints. - Hub { token: String }, - /// Targeting Hub but only has a CLI session token. Uses - /// `Authorization: Token <session>` against compat/record endpoints. - /// Sync, password change, and account deletion still work, but the user - /// should be nudged to run `atuin login` for full Hub auth. - HubViaCli { token: String }, + /// Not authenticated at all. Contains an actionable user-facing message. NotLoggedIn { reason: String }, } @@ -415,8 +383,6 @@ impl SyncAuth { use crate::api_client::AuthToken; match self { SyncAuth::Legacy { token } => Ok(AuthToken::Token(token)), - SyncAuth::Hub { token } => Ok(AuthToken::Bearer(token)), - SyncAuth::HubViaCli { token } => Ok(AuthToken::Token(token)), SyncAuth::NotLoggedIn { reason } => Err(eyre!(reason)), } } @@ -1082,9 +1048,6 @@ pub struct Settings { /// The sync address for atuin. pub sync_address: String, - /// Sync protocol for authentication. When set to "auto" (default), the protocol - /// is inferred from sync_address. Set to "hub" to force Hub auth with a custom - /// sync_address (useful for local development). #[serde(default)] pub sync_protocol: SyncProtocol, @@ -1152,9 +1115,6 @@ pub struct Settings { pub preview: Preview, #[serde(default)] - pub dotfiles: dotfiles::Settings, - - #[serde(default)] pub daemon: Daemon, #[serde(default)] @@ -1167,12 +1127,6 @@ pub struct Settings { pub ui: Ui, #[serde(default)] - pub scripts: scripts::Settings, - - #[serde(default)] - pub kv: kv::Settings, - - #[serde(default)] pub tmux: Tmux, #[serde(default)] @@ -1180,9 +1134,6 @@ pub struct Settings { #[serde(default)] pub meta: meta::Settings, - - #[serde(default)] - pub ai: Ai, } impl Settings { @@ -1266,56 +1217,6 @@ impl Settings { } } - pub async fn hub_session_token(&self) -> Result<String> { - match Self::meta_store().await?.hub_session_token().await? { - Some(token) => Ok(token), - None => Err(eyre!("Tried to load hub session; not logged in")), - } - } - - /// Normalize a URL for comparison by trimming trailing slashes - fn normalize_url(url: &str) -> &str { - url.trim_end_matches('/') - } - - /// Check if a URL matches one of Atuin's official hosted addresses - fn is_official_address(url: &str) -> bool { - let normalized = Self::normalize_url(url); - normalized == Self::normalize_url(DEFAULT_SYNC_ADDRESS) - || normalized == Self::normalize_url(DEFAULT_HUB_ENDPOINT) - } - - /// Returns whether this configuration uses Hub-style sync. - /// - /// Hub sync uses Bearer token authentication and is the default for - /// Atuin's hosted service. This returns true when: - /// - `sync_protocol` is explicitly set to `Hub`, OR - /// - `sync_protocol` is `Auto` and `sync_address` is an official Atuin address - pub fn is_hub_sync(&self) -> bool { - match self.sync_protocol { - SyncProtocol::Hub => true, - SyncProtocol::Legacy => false, - SyncProtocol::Auto => Self::is_official_address(&self.sync_address), - } - } - - /// Returns the base URL for the Hub endpoint. - /// - /// For Atuin's official hosted service, this always returns `https://hub.atuin.sh` - /// regardless of whether `sync_address` is `api.atuin.sh` or `hub.atuin.sh`. - /// For self-hosted instances, returns the configured `sync_address`. - pub fn active_hub_endpoint(&self) -> Option<HubEndpoint> { - if self.is_hub_sync() { - if Self::is_official_address(&self.sync_address) { - Some(HubEndpoint::default()) - } else { - Some(HubEndpoint(self.sync_address.clone())) - } - } else { - None - } - } - /// Examines the configured sync target and available tokens to determine /// the correct auth strategy. Also performs cleanup of mis-stored tokens /// (e.g. a CLI token incorrectly saved in the Hub session slot). @@ -1330,45 +1231,12 @@ impl Settings { } }; - if !self.is_hub_sync() { - // Self-hosted / legacy server - return match meta.session_token().await { - Ok(Some(token)) => SyncAuth::Legacy { token }, - _ => SyncAuth::NotLoggedIn { - reason: "Not logged in. Run 'atuin login' to authenticate \ - with your sync server." - .into(), - }, - }; - } - - // Targeting Hub — check for a valid Hub API token first - if let Ok(Some(hub_token)) = meta.hub_session_token().await { - if hub_token.starts_with("atapi_") { - return SyncAuth::Hub { token: hub_token }; - } - - // A non-atapi_ token in the hub_session slot is a mis-stored CLI - // token (from the migration-fallback bug). Move it to the CLI - // session slot if that slot is empty, then clear hub_session - // only if the move succeeded. - if let Ok(None) = meta.session_token().await { - if meta.save_session(&hub_token).await.is_ok() { - let _ = meta.delete_hub_session().await; - } - } else { - // CLI slot already has a token; just clear the bad hub_session - let _ = meta.delete_hub_session().await; - } - // Fall through to check CLI token below - } - - // No valid Hub token — check for a CLI session token + // Self-hosted / legacy server match meta.session_token().await { - Ok(Some(token)) => SyncAuth::HubViaCli { token }, + Ok(Some(token)) => SyncAuth::Legacy { token }, _ => SyncAuth::NotLoggedIn { - reason: "Not logged in. Run 'atuin login' or 'atuin register' \ - to authenticate." + reason: "Not logged in. Run 'atuin login' to authenticate \ + with your sync server." .into(), }, } @@ -1384,70 +1252,6 @@ impl Settings { self.resolve_sync_auth().await.into_auth_token() } - #[cfg(feature = "check-update")] - async fn needs_update_check(&self) -> Result<bool> { - let last_check = Settings::last_version_check().await?; - let diff = OffsetDateTime::now_utc() - last_check; - - // Check a max of once per hour - Ok(diff.whole_hours() >= 1) - } - - #[cfg(feature = "check-update")] - async fn latest_version(&self) -> Result<Version> { - // Default to the current version, and if that doesn't parse, a version so high it's unlikely to ever - // suggest upgrading. - let current = - Version::parse(env!("CARGO_PKG_VERSION")).unwrap_or(Version::new(100000, 0, 0)); - - if !self.needs_update_check().await? { - let meta = Self::meta_store().await?; - let version = match meta.latest_version().await? { - Some(v) => Version::parse(&v).unwrap_or(current), - None => current, - }; - - return Ok(version); - } - - #[cfg(feature = "sync")] - let latest = crate::api_client::latest_version().await.unwrap_or(current); - - #[cfg(not(feature = "sync"))] - let latest = current; - - let meta = Self::meta_store().await?; - Settings::save_version_check_time().await?; - meta.save_latest_version(&latest.to_string()).await?; - - Ok(latest) - } - - // Return Some(latest version) if an update is needed. Otherwise, none. - #[cfg(feature = "check-update")] - pub async fn needs_update(&self) -> Option<Version> { - if !self.update_check { - return None; - } - - let current = - Version::parse(env!("CARGO_PKG_VERSION")).unwrap_or(Version::new(100000, 0, 0)); - - let latest = self.latest_version().await; - - if latest.is_err() { - return None; - } - - let latest = latest.unwrap(); - - if latest > current { - return Some(latest); - } - - None - } - pub fn default_filter_mode(&self, git_root: bool) -> FilterMode { self.filter_mode .filter(|x| self.search.filters.contains(x)) @@ -1465,11 +1269,6 @@ impl Settings { .unwrap_or(FilterMode::Global) } - #[cfg(not(feature = "check-update"))] - pub async fn needs_update(&self) -> Option<Version> { - None - } - pub fn builder() -> Result<ConfigBuilder<DefaultState>> { Self::builder_with_data_dir(&atuin_common::utils::data_dir()) } diff --git a/crates/atuin-client/src/settings/dotfiles.rs b/crates/atuin-client/src/settings/dotfiles.rs deleted file mode 100644 index bbaf914f..00000000 --- a/crates/atuin-client/src/settings/dotfiles.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone, Default)] -pub struct Settings { - #[serde(alias = "enable")] - pub enabled: bool, -} diff --git a/crates/atuin-client/src/settings/kv.rs b/crates/atuin-client/src/settings/kv.rs deleted file mode 100644 index afc24a35..00000000 --- a/crates/atuin-client/src/settings/kv.rs +++ /dev/null @@ -1,17 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Settings { - pub db_path: String, -} - -impl Default for Settings { - fn default() -> Self { - let dir = atuin_common::utils::data_dir(); - let path = dir.join("kv.db"); - - Self { - db_path: path.to_string_lossy().to_string(), - } - } -} diff --git a/crates/atuin-client/src/settings/scripts.rs b/crates/atuin-client/src/settings/scripts.rs deleted file mode 100644 index a1a5ed6a..00000000 --- a/crates/atuin-client/src/settings/scripts.rs +++ /dev/null @@ -1,17 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Settings { - pub db_path: String, -} - -impl Default for Settings { - fn default() -> Self { - let dir = atuin_common::utils::data_dir(); - let path = dir.join("scripts.db"); - - Self { - db_path: path.to_string_lossy().to_string(), - } - } -} diff --git a/crates/atuin/src/command/client.rs b/crates/atuin/src/command/client.rs index 3b0ef8a9..a1ebff29 100644 --- a/crates/atuin/src/command/client.rs +++ b/crates/atuin/src/command/client.rs @@ -56,7 +56,6 @@ mod history; mod import; mod info; mod init; -mod kv; mod search; mod setup; mod stats; @@ -92,10 +91,6 @@ pub enum Cmd { #[cfg(feature = "sync")] Account(account::Cmd), - /// Get or set small key-value pairs - #[command(subcommand)] - Kv(kv::Cmd), - /// Manage the atuin data store #[command(subcommand)] Store(store::Cmd), @@ -314,7 +309,10 @@ impl Cmd { // runs match self { Self::History(history) => return history.run(&settings).await, - Self::Init(init) => return init.run(&settings).await, + Self::Init(init) => { + init.run(&settings); + return Ok(()); + } Self::Doctor => return doctor::run(&settings).await, Self::Config(config) => return config.run(&settings).await, _ => {} @@ -341,8 +339,6 @@ impl Cmd { #[cfg(feature = "sync")] Self::Account(account) => account.run(settings, sqlite_store).await, - Self::Kv(kv) => kv.run(&settings, &sqlite_store).await, - Self::Store(store) => store.run(&settings, &db, sqlite_store).await, Self::Info => { diff --git a/crates/atuin/src/command/client/account.rs b/crates/atuin/src/command/client/account.rs index a1be65a5..fc1c9343 100644 --- a/crates/atuin/src/command/client/account.rs +++ b/crates/atuin/src/command/client/account.rs @@ -6,7 +6,6 @@ use atuin_client::settings::Settings; pub mod change_password; pub mod delete; -pub mod link; pub mod login; pub mod logout; pub mod register; @@ -33,20 +32,16 @@ pub enum Commands { /// Change your password ChangePassword(change_password::Cmd), - - /// Link your CLI sync account to your Hub account - Link, } impl Cmd { pub async fn run(self, settings: Settings, store: SqliteStore) -> Result<()> { match self.command { Commands::Login(l) => l.run(&settings, &store).await, - Commands::Register(r) => r.run(&settings, &store).await, + Commands::Register(r) => r.run(&settings).await, Commands::Logout => logout::run().await, Commands::Delete(d) => d.run(&settings).await, Commands::ChangePassword(c) => c.run(&settings).await, - Commands::Link => link::run(&settings).await, } } } diff --git a/crates/atuin/src/command/client/account/delete.rs b/crates/atuin/src/command/client/account/delete.rs index 7f8dc682..a5e7f0dd 100644 --- a/crates/atuin/src/command/client/account/delete.rs +++ b/crates/atuin/src/command/client/account/delete.rs @@ -49,7 +49,6 @@ impl Cmd { // Clean up sessions from meta store let meta = Settings::meta_store().await?; meta.delete_session().await?; - meta.delete_hub_session().await?; println!("Your account is deleted"); diff --git a/crates/atuin/src/command/client/account/link.rs b/crates/atuin/src/command/client/account/link.rs deleted file mode 100644 index 69c4eebe..00000000 --- a/crates/atuin/src/command/client/account/link.rs +++ /dev/null @@ -1,41 +0,0 @@ -use eyre::{Result, bail}; - -use atuin_client::settings::Settings; - -pub async fn run(settings: &Settings) -> Result<()> { - let meta = Settings::meta_store().await?; - - let cli_token = meta.session_token().await?; - let hub_token = meta.hub_session_token().await?; - - let Some(cli_token) = cli_token else { - bail!("No CLI session found. Please log in first with 'atuin login'."); - }; - - let hub_address = settings.active_hub_endpoint().unwrap_or_default(); - - if hub_token.is_some() { - println!("Found both Hub and CLI sessions. Linking accounts..."); - } else { - println!("Found CLI session but no Hub session. Logging in to Hub first..."); - - let session = atuin_client::hub::HubAuthSession::start(hub_address.as_ref()).await?; - println!("Open this URL to authenticate with Atuin Hub:"); - println!("{}", session.auth_url); - - let token = session - .wait_for_completion( - atuin_client::hub::DEFAULT_AUTH_TIMEOUT, - atuin_client::hub::DEFAULT_POLL_INTERVAL, - ) - .await?; - - atuin_client::hub::save_session(&token).await?; - println!("Hub authentication complete."); - } - - atuin_client::hub::link_account(hub_address.as_ref(), &cli_token).await?; - println!("Successfully linked CLI account to Hub."); - - Ok(()) -} diff --git a/crates/atuin/src/command/client/account/login.rs b/crates/atuin/src/command/client/account/login.rs index 072f0815..13918638 100644 --- a/crates/atuin/src/command/client/account/login.rs +++ b/crates/atuin/src/command/client/account/login.rs @@ -43,95 +43,19 @@ fn get_input() -> Result<String> { impl Cmd { pub async fn run(&self, settings: &Settings, store: &SqliteStore) -> Result<()> { match settings.resolve_sync_auth().await { - SyncAuth::Hub { .. } => { - println!("You are authenticated with Atuin Hub."); - println!("Run 'atuin logout' to log out."); - return Ok(()); - } SyncAuth::Legacy { .. } => { println!("You are logged in to your sync server."); println!("Run 'atuin logout' to log out."); return Ok(()); } - SyncAuth::HubViaCli { .. } => { - println!( - "You have a legacy sync session. \ - Continuing login to upgrade to full Hub authentication." - ); - } SyncAuth::NotLoggedIn { .. } => {} } - if settings.is_hub_sync() { - self.run_hub_login(settings, store).await?; - } else { - self.run_legacy_login(settings, store).await?; - } + self.run_legacy_login(settings, store).await?; verify_key_against_remote(settings).await } - /// Hub login: use the browser flow unless the username was provided for headless use. - async fn run_hub_login(&self, settings: &Settings, store: &SqliteStore) -> Result<()> { - let endpoint = settings.active_hub_endpoint().unwrap_or_default(); - - if let Some(username) = &self.username { - // Headless login via v0 API (for CI / scripting). - let client = auth::auth_client(settings).await; - - self.prompt_and_store_key(settings, store).await?; - - let password = self.password.clone().unwrap_or_else(read_user_password); - let mut totp_code = self.totp_code.clone(); - - let (session, auth_type) = loop { - let response = client - .login(username, &password, totp_code.as_deref()) - .await?; - - match response { - AuthResponse::Success { session, auth_type } => break (session, auth_type), - AuthResponse::TwoFactorRequired => { - totp_code = Some(or_user_input(None, "two-factor code")); - } - } - }; - - let meta = Settings::meta_store().await?; - let is_hub_token = auth_type.as_deref() == Some("hub") || session.starts_with("atapi_"); - - if is_hub_token { - meta.save_hub_session(&session).await?; - } else { - meta.save_session(&session).await?; - println!("\nNote: Your account has not been fully migrated to Atuin Hub."); - println!( - "Sync will continue to work, but you can visit hub.atuin.sh \ - to create an account and link it to your existing CLI account." - ); - } - } else { - // Interactive login via browser OAuth flow. - if self.from_registration { - load_key(settings)?; - } else { - self.prompt_and_store_key(settings, store).await?; - } - - self.ensure_hub_session(settings, endpoint.as_ref()).await?; - } - - // Silently attempt to link CLI account to Hub if one exists - if let Ok(cli_token) = settings.session_token().await - && let Err(e) = atuin_client::hub::link_account(endpoint.as_ref(), &cli_token).await - { - tracing::debug!("Could not link CLI account to Hub: {}", e); - } - - println!("Successfully authenticated."); - Ok(()) - } - /// Legacy login: always prompt for username/password interactively /// (or accept them via flags). async fn run_legacy_login(&self, settings: &Settings, store: &SqliteStore) -> Result<()> { @@ -157,27 +81,6 @@ impl Cmd { Ok(()) } - async fn ensure_hub_session(&self, _settings: &Settings, hub_address: &str) -> Result<()> { - tracing::info!("Authenticating with Atuin Hub..."); - - let session = atuin_client::hub::HubAuthSession::start(hub_address).await?; - println!("Open this URL to continue authenticating with Atuin Hub:"); - println!("{}", session.auth_url); - - let token = session - .wait_for_completion( - atuin_client::hub::DEFAULT_AUTH_TIMEOUT, - atuin_client::hub::DEFAULT_POLL_INTERVAL, - ) - .await?; - - tracing::info!("Authentication complete, saving session token"); - - atuin_client::hub::save_session(&token).await?; - - Ok(()) - } - async fn prompt_and_store_key(&self, settings: &Settings, store: &SqliteStore) -> Result<()> { let key_path = settings.key_path.as_str(); let key_path = PathBuf::from(key_path); @@ -293,7 +196,6 @@ async fn verify_key_against_remote(settings: &Settings) -> Result<()> { // half-authenticated state with a key that can't read the data. if let Ok(meta) = Settings::meta_store().await { let _ = meta.delete_session().await; - let _ = meta.delete_hub_session().await; } crate::print_error::print_error( "Wrong encryption key", diff --git a/crates/atuin/src/command/client/account/register.rs b/crates/atuin/src/command/client/account/register.rs index f01427c0..bd836e7b 100644 --- a/crates/atuin/src/command/client/account/register.rs +++ b/crates/atuin/src/command/client/account/register.rs @@ -2,11 +2,7 @@ use clap::Parser; use eyre::{Result, bail}; use super::login::or_user_input; -use atuin_client::{ - auth::{self, AuthResponse}, - record::sqlite_store::SqliteStore, - settings::{Settings, SyncAuth}, -}; +use atuin_client::settings::{Settings, SyncAuth}; #[derive(Parser, Debug)] pub struct Cmd { @@ -21,139 +17,50 @@ pub struct Cmd { } impl Cmd { - #[allow(clippy::too_many_lines)] - pub async fn run(&self, settings: &Settings, store: &SqliteStore) -> Result<()> { + pub async fn run(&self, settings: &Settings) -> Result<()> { match settings.resolve_sync_auth().await { - SyncAuth::Hub { .. } => { - println!("You are already authenticated with Atuin Hub."); - println!("Run 'atuin logout' to log out."); - return Ok(()); - } SyncAuth::Legacy { .. } => { println!("You are already logged in."); println!("Run 'atuin logout' to log out."); return Ok(()); } - SyncAuth::HubViaCli { .. } => { - println!( - "You already have a sync session. \ - Run 'atuin login' to upgrade to full Hub authentication." - ); - println!("Run 'atuin logout' first if you want to register a new account."); - return Ok(()); - } + SyncAuth::NotLoggedIn { .. } => {} } - if settings.is_hub_sync() { - let required_for_headless = 3; - let provided = [ - self.username.is_some(), - self.email.is_some(), - self.password.is_some(), - ] - .iter() - .filter(|&b| *b) - .count(); - if provided < required_for_headless { - println!( - "Username, password, and email are all required for headless registration. Continuing with interactive registration.\n" - ); - } - - if let (Some(username), Some(email), Some(password)) = - (&self.username, &self.email, &self.password) - { - // Headless registration via v0 API (for CI / scripting). - let client = auth::auth_client(settings).await; - - if password.is_empty() { - bail!("please provide a password"); - } + // Legacy registration flow + println!("Registering for an Atuin Sync account"); - let response = client.register(username, email, password).await?; + let username = or_user_input(self.username.clone(), "username"); + let email = or_user_input(self.email.clone(), "email"); + let password = self + .password + .clone() + .unwrap_or_else(super::login::read_user_password); - match response { - AuthResponse::Success { session, auth_type } => { - let meta = Settings::meta_store().await?; - let is_hub_token = - auth_type.as_deref() == Some("hub") || session.starts_with("atapi_"); - - if is_hub_token { - meta.save_hub_session(&session).await?; - } else { - meta.save_session(&session).await?; - println!( - "\nNote: Your account has not been fully migrated to Atuin Hub." - ); - println!( - "Sync will continue to work, but you can visit hub.atuin.sh \ - to create a new Hub account and link it to your existing CLI account." - ); - } - } - AuthResponse::TwoFactorRequired => { - bail!("unexpected two-factor requirement during registration"); - } - } - - let _key = atuin_client::encryption::load_key(settings)?; - - println!( - "Registration successful! Please make a note of your key (run 'atuin key') and keep it safe." - ); - println!( - "You will need it to log in on other devices, and we cannot help recover it if you lose it." - ); - } else { - // Interactive registration: delegate to the browser OAuth flow. - // Registration on Hub happens on the website; the CLI just needs - // to authenticate afterwards. - super::login::Cmd { - username: None, - password: None, - key: None, - totp_code: None, - from_registration: true, - } - .run(settings, store) - .await?; - } - } else { - // Legacy registration flow - println!("Registering for an Atuin Sync account"); - - let username = or_user_input(self.username.clone(), "username"); - let email = or_user_input(self.email.clone(), "email"); - let password = self - .password - .clone() - .unwrap_or_else(super::login::read_user_password); - - if password.is_empty() { - bail!("please provide a password"); - } + if password.is_empty() { + bail!("please provide a password"); + } - let session = atuin_client::api_client::register( - settings.sync_address.as_str(), - &username, - &email, - &password, - ) - .await?; + let session = atuin_client::api_client::register( + settings.sync_address.as_str(), + &username, + &email, + &password, + ) + .await?; - let meta = Settings::meta_store().await?; - meta.save_session(&session.session).await?; + let meta = Settings::meta_store().await?; + meta.save_session(&session.session).await?; - let _key = atuin_client::encryption::load_key(settings)?; + let _key = atuin_client::encryption::load_key(settings)?; - println!( - "Registration successful! Please make a note of your key (run 'atuin key') and keep it safe." - ); - println!( - "You will need it to log in on other devices, and we cannot help recover it if you lose it." - ); - } + println!( + "Registration successful! Please make a note of your key (run 'atuin key') and keep it safe." + ); + println!( + "You will need it to log in on other devices, and we cannot help recover it if you lose it." + ); Ok(()) } diff --git a/crates/atuin/src/command/client/doctor.rs b/crates/atuin/src/command/client/doctor.rs index ce65f66a..f6470226 100644 --- a/crates/atuin/src/command/client/doctor.rs +++ b/crates/atuin/src/command/client/doctor.rs @@ -249,25 +249,12 @@ impl SyncInfo { // resolve_sync_auth(), which has side effects (token migration cleanup) // that a diagnostic command should not trigger. let meta = Settings::meta_store().await.ok(); - let has_hub_token = match &meta { - Some(m) => m - .hub_session_token() - .await - .ok() - .flatten() - .is_some_and(|t| t.starts_with("atapi_")), - None => false, - }; let has_cli_token = match &meta { Some(m) => m.session_token().await.ok().flatten().is_some(), None => false, }; - let auth_state = if has_hub_token { - "Hub (authenticated)".into() - } else if settings.is_hub_sync() && has_cli_token { - "Hub (legacy token \u{2014} run 'atuin login' to upgrade)".into() - } else if !settings.is_hub_sync() && has_cli_token { + let auth_state = if has_cli_token { "Self-hosted (authenticated)".into() } else { "Not authenticated".into() diff --git a/crates/atuin/src/command/client/init.rs b/crates/atuin/src/command/client/init.rs index 98ef5c80..e0f284a7 100644 --- a/crates/atuin/src/command/client/init.rs +++ b/crates/atuin/src/command/client/init.rs @@ -1,6 +1,5 @@ use atuin_client::settings::{Settings, Tmux}; use clap::{Parser, ValueEnum}; -use eyre::Result; mod bash; mod fish; @@ -116,44 +115,13 @@ $env.config = ( } } - async fn dotfiles_init(&self, settings: &Settings) -> Result<()> { - match self.shell { - Shell::Zsh => { - zsh::init(self.disable_up_arrow, self.disable_ctrl_r, &settings.tmux).await?; - } - Shell::Bash => { - bash::init(self.disable_up_arrow, self.disable_ctrl_r, &settings.tmux).await?; - } - Shell::Fish => { - fish::init(self.disable_up_arrow, self.disable_ctrl_r, &settings.tmux).await?; - } - Shell::Nu => self.init_nu(&settings.tmux), - Shell::Xonsh => { - xonsh::init(self.disable_up_arrow, self.disable_ctrl_r, &settings.tmux).await?; - } - Shell::PowerShell => { - powershell::init(self.disable_up_arrow, self.disable_ctrl_r, &settings.tmux) - .await?; - } - } - - Ok(()) - } - - pub async fn run(self, settings: &Settings) -> Result<()> { + pub fn run(self, settings: &Settings) { if !settings.paths_ok() { eprintln!( "Atuin settings paths are broken. Disabling atuin shell hooks. Run `atuin doctor` to diagnose." ); - return Ok(()); - } - - if settings.dotfiles.enabled { - self.dotfiles_init(settings).await?; - } else { - self.static_init(settings); } - Ok(()) + self.static_init(settings); } } diff --git a/crates/atuin/src/command/client/init/bash.rs b/crates/atuin/src/command/client/init/bash.rs index 7fe57b33..2280dc3d 100644 --- a/crates/atuin/src/command/client/init/bash.rs +++ b/crates/atuin/src/command/client/init/bash.rs @@ -1,5 +1,4 @@ use atuin_client::settings::Tmux; -use eyre::Result; fn print_tmux_config(tmux: &Tmux) { if tmux.enabled { @@ -24,9 +23,3 @@ pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool, tmux: &Tmux) { println!("__atuin_bind_up_arrow={bind_up_arrow}"); println!("{base}"); } - -pub async fn init(disable_up_arrow: bool, disable_ctrl_r: bool, tmux: &Tmux) -> Result<()> { - init_static(disable_up_arrow, disable_ctrl_r, tmux); - - Ok(()) -} diff --git a/crates/atuin/src/command/client/init/fish.rs b/crates/atuin/src/command/client/init/fish.rs index e477faed..07c6a5ba 100644 --- a/crates/atuin/src/command/client/init/fish.rs +++ b/crates/atuin/src/command/client/init/fish.rs @@ -1,5 +1,4 @@ use atuin_client::settings::Tmux; -use eyre::Result; fn print_tmux_config(tmux: &Tmux) { if tmux.enabled { @@ -85,13 +84,3 @@ pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool, tmux: &Tmux) { println!("end"); } } - -pub async fn init( - disable_up_arrow: bool, - disable_ctrl_r: bool, - tmux: &Tmux, -) -> Result<()> { - init_static(disable_up_arrow, disable_ctrl_r, tmux); - - Ok(()) -} diff --git a/crates/atuin/src/command/client/init/powershell.rs b/crates/atuin/src/command/client/init/powershell.rs index a36b8e67..f92f1cbe 100644 --- a/crates/atuin/src/command/client/init/powershell.rs +++ b/crates/atuin/src/command/client/init/powershell.rs @@ -18,12 +18,6 @@ pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool, _tmux: &Tmux) { ); } -pub async fn init(disable_up_arrow: bool, disable_ctrl_r: bool, tmux: &Tmux) -> eyre::Result<()> { - init_static(disable_up_arrow, disable_ctrl_r, tmux); - - Ok(()) -} - fn ps_bool(value: bool) -> &'static str { if value { "$true" } else { "$false" } } diff --git a/crates/atuin/src/command/client/init/xonsh.rs b/crates/atuin/src/command/client/init/xonsh.rs index f14da3d8..9fb5730d 100644 --- a/crates/atuin/src/command/client/init/xonsh.rs +++ b/crates/atuin/src/command/client/init/xonsh.rs @@ -1,5 +1,4 @@ use atuin_client::settings::Tmux; -use eyre::Result; pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool, _tmux: &Tmux) { let base = include_str!("../../../shell/atuin.xsh"); @@ -21,9 +20,3 @@ pub fn init_static(disable_up_arrow: bool, disable_ctrl_r: bool, _tmux: &Tmux) { ); println!("{base}"); } - -pub async fn init(disable_up_arrow: bool, disable_ctrl_r: bool, tmux: &Tmux) -> Result<()> { - init_static(disable_up_arrow, disable_ctrl_r, tmux); - - Ok(()) -} diff --git a/crates/atuin/src/command/client/init/zsh.rs b/crates/atuin/src/command/client/init/zsh.rs index 392e987c..3f325167 100644 --- a/crates/atuin/src/command/client/init/zsh.rs +++ b/crates/atuin/src/command/client/init/zsh.rs @@ -1,5 +1,4 @@ use atuin_client::settings::Tmux; -use eyre::Result; fn print_tmux_config(tmux: &Tmux) { if tmux.enabled { @@ -37,13 +36,3 @@ bindkey -M vicmd 'k' atuin-up-search-vicmd"; } } } - -pub async fn init( - disable_up_arrow: bool, - disable_ctrl_r: bool, - tmux: &Tmux, -) -> Result<()> { - init_static(disable_up_arrow, disable_ctrl_r, tmux); - - Ok(()) -} diff --git a/crates/atuin/src/command/client/kv.rs b/crates/atuin/src/command/client/kv.rs deleted file mode 100644 index 88e3edeb..00000000 --- a/crates/atuin/src/command/client/kv.rs +++ /dev/null @@ -1,138 +0,0 @@ -use std::io::{self, IsTerminal, Read}; - -use clap::Subcommand; -use eyre::{Context, Result, eyre}; - -use atuin_client::{encryption, record::sqlite_store::SqliteStore, settings::Settings}; -use atuin_kv::store::KvStore; - -#[derive(Subcommand, Debug)] -#[command(infer_subcommands = true)] -pub enum Cmd { - /// Set a key-value pair - Set { - /// Key to set - #[arg(long, short)] - key: String, - - /// Value to store (reads from stdin if not provided) - value: Option<String>, - - /// Namespace for the key-value pair - #[arg(long, short, default_value = "default")] - namespace: String, - }, - - /// Delete one or more key-value pairs - #[command(alias = "rm")] - Delete { - /// Keys to delete - #[arg(required = true)] - keys: Vec<String>, - - /// Namespace for the key-value pair - #[arg(long, short, default_value = "default")] - namespace: String, - }, - - /// Retrieve a saved value - Get { - /// Key to retrieve - key: String, - - /// Namespace for the key-value pair - #[arg(long, short, default_value = "default")] - namespace: String, - }, - - /// List all keys in a namespace, or in all namespaces - #[command(alias = "ls")] - List { - /// Namespace to list keys from - #[arg(long, short, default_value = "default")] - namespace: String, - - /// List all keys in all namespaces - #[arg(long, short, alias = "all")] - all_namespaces: bool, - }, - - /// Rebuild the KV store - Rebuild, -} - -impl Cmd { - pub async fn run(&self, settings: &Settings, store: &SqliteStore) -> Result<()> { - let encryption_key: [u8; 32] = encryption::load_key(settings) - .context("could not load encryption key")? - .into(); - - let host_id = Settings::host_id().await?; - - let kv_db = atuin_kv::database::Database::new(settings.kv.db_path.clone(), 1.0).await?; - let kv_store = KvStore::new(store.clone(), kv_db, host_id, encryption_key); - - match self { - Self::Set { - key, - value, - namespace, - } => { - if namespace.is_empty() { - return Err(eyre!("namespace cannot be empty")); - } - - let value = if let Some(v) = value { - v.clone() - } else if !io::stdin().is_terminal() { - let mut buf = String::new(); - io::stdin() - .read_to_string(&mut buf) - .context("failed to read value from stdin")?; - buf - } else { - return Err(eyre!( - "no value provided. Pass as an argument or pipe via stdin" - )); - }; - - kv_store.set(namespace, key, &value).await - } - - Self::Delete { keys, namespace } => kv_store.delete(namespace, keys).await, - - Self::Get { key, namespace } => { - let kv = kv_store.get(namespace, key).await?; - - if let Some(val) = kv { - println!("{val}"); - } - - Ok(()) - } - - Self::List { - namespace, - all_namespaces, - } => { - let entries = if *all_namespaces { - kv_store.list(None).await? - } else { - kv_store.list(Some(namespace)).await? - }; - - for entry in entries { - if *all_namespaces { - println!("{}.{}", entry.namespace, entry.key); - } else { - println!("{}", entry.key); - } - } - - Ok(()) - } - - Self::Rebuild {} => kv_store.build().await, - } - } -} diff --git a/crates/atuin/src/command/client/search/interactive.rs b/crates/atuin/src/command/client/search/interactive.rs index 28b29824..8efe1f42 100644 --- a/crates/atuin/src/command/client/search/interactive.rs +++ b/crates/atuin/src/command/client/search/interactive.rs @@ -8,8 +8,6 @@ use std::io::Read as _; use atuin_common::{shell::Shell, utils::Escapable as _}; use eyre::Result; -use futures_util::FutureExt; -use semver::Version; use time::OffsetDateTime; use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; @@ -117,7 +115,6 @@ pub fn to_compactness(f: &Frame, settings: &Settings) -> Compactness { #[allow(clippy::struct_excessive_bools)] pub struct State { history_count: i64, - update_needed: Option<Version>, results_state: ListState, switched_search_mode: bool, search_mode: SearchMode, @@ -931,7 +928,7 @@ impl State { ) .split(header_chunk); - let title = self.build_title(theme); + let title = Self::build_title(theme); f.render_widget(title, header_chunks[0]); let help = self.build_help(settings, theme); @@ -1086,14 +1083,8 @@ impl State { )); } - fn build_title(&self, theme: &Theme) -> Paragraph<'_> { - let title = if self.update_needed.is_some() { - let error_style: Style = Style::from_crossterm(theme.get_error()); - Paragraph::new(Text::from(Span::styled( - format!("Atuin v{VERSION} - UPDATE"), - error_style.add_modifier(Modifier::BOLD), - ))) - } else { + fn build_title(theme: &Theme) -> Paragraph<'_> { + let title = { let style: Style = Style::from_crossterm(theme.as_style(Meaning::Base)); Paragraph::new(Text::from(Span::styled( format!("Atuin v{VERSION}"), @@ -1746,10 +1737,6 @@ pub async fn history( // Put the cursor at the end of the query by default input.end(); - let settings2 = settings.clone(); - let update_needed = tokio::spawn(async move { settings2.needs_update().await }).fuse(); - tokio::pin!(update_needed); - let initial_context = current_context().await?; let history_count = db.history_count(false).await?; @@ -1767,7 +1754,6 @@ pub async fn history( let mut app = State { history_count, results_state: ListState::default(), - update_needed: None, switched_search_mode: false, search_mode, tab_index: 0, @@ -1915,11 +1901,6 @@ pub async fn history( } } } - update_needed = &mut update_needed => { - // Don't fail interactive search if update check fails - // The update check is a nice-to-have feature, not critical - app.update_needed = update_needed.ok().flatten(); - } } if initial_input != app.search.input.as_str() @@ -2244,7 +2225,6 @@ mod tests { let settings = Settings::utc(); let mut state = State { history_count: 0, - update_needed: None, results_state: ListState::default(), switched_search_mode: false, search_mode: SearchMode::Fuzzy, @@ -2299,7 +2279,6 @@ mod tests { let mut state = State { history_count: 1, - update_needed: None, results_state: ListState::default(), switched_search_mode: false, search_mode: SearchMode::Fuzzy, @@ -2418,7 +2397,6 @@ mod tests { let mut state = State { history_count: 100, - update_needed: None, results_state: ListState::default(), switched_search_mode: false, search_mode: SearchMode::Fuzzy, @@ -2477,7 +2455,6 @@ mod tests { let mut state = State { history_count: 100, - update_needed: None, results_state: ListState::default(), switched_search_mode: false, search_mode: SearchMode::Fuzzy, @@ -2532,7 +2509,6 @@ mod tests { let mut state = State { history_count: 100, - update_needed: None, results_state: ListState::default(), switched_search_mode: false, search_mode: SearchMode::Fuzzy, @@ -2583,7 +2559,6 @@ mod tests { let mut state = State { history_count: 100, - update_needed: None, results_state: ListState::default(), switched_search_mode: false, search_mode: SearchMode::Fuzzy, @@ -2643,7 +2618,6 @@ mod tests { let mut state = State { history_count: 100, - update_needed: None, results_state: ListState::default(), switched_search_mode: false, search_mode: SearchMode::Fuzzy, @@ -2704,7 +2678,6 @@ mod tests { let settings = Settings::utc(); let mut state = State { history_count: results_len as i64, - update_needed: None, results_state: ListState::default(), switched_search_mode: false, search_mode: SearchMode::Fuzzy, @@ -3083,7 +3056,6 @@ mod tests { let mut state = State { history_count: 100, - update_needed: None, results_state: ListState::default(), switched_search_mode: false, search_mode: SearchMode::Fuzzy, diff --git a/crates/atuin/src/command/client/sync.rs b/crates/atuin/src/command/client/sync.rs index 15123a7f..12f0cacd 100644 --- a/crates/atuin/src/command/client/sync.rs +++ b/crates/atuin/src/command/client/sync.rs @@ -54,7 +54,7 @@ impl Cmd { Self::Sync { force } => run(&settings, force, db, store).await, Self::Login(l) => l.run(&settings, &store).await, Self::Logout => account::logout::run().await, - Self::Register(r) => r.run(&settings, &store).await, + Self::Register(r) => r.run(&settings).await, Self::Status => status::run(&settings, db).await, Self::Key { base64 } => { use atuin_client::encryption::{encode_key, load_key}; diff --git a/crates/atuin/src/sync.rs b/crates/atuin/src/sync.rs index 14982300..02e4db69 100644 --- a/crates/atuin/src/sync.rs +++ b/crates/atuin/src/sync.rs @@ -5,7 +5,6 @@ use atuin_client::{ settings::Settings, }; use atuin_common::record::RecordId; -use atuin_kv::store::KvStore; // This is the only crate that ties together all other crates. // Therefore, it's the only crate where functions tying together all stores can live @@ -27,14 +26,9 @@ pub async fn build( let downloaded = downloaded.unwrap_or(&[]); - let kv_db = atuin_kv::database::Database::new(settings.kv.db_path.clone(), 1.0).await?; - let history_store = HistoryStore::new(store.clone(), host_id, encryption_key); - let kv_store = KvStore::new(store.clone(), kv_db, host_id, encryption_key); history_store.incremental_build(db, downloaded).await?; - kv_store.build().await?; - Ok(()) } |
