Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,5 @@ book/
.env*

run-build.sh
# Amazon Q CLI files
.amazonq/
9 changes: 6 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/alacritty_terminal/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ workspace = true
[dependencies]
bitflags.workspace = true
camino.workspace = true
fig_os_shim.workspace = true
serde.workspace = true
serde_yaml = "0.9"
shell-color.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/alacritty_terminal/src/term/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -833,7 +833,7 @@ impl<T> Term<T> {
trace!("New command cursor: {:?}", self.shell_state.cmd_cursor);

// Add work around for emojis
if let Ok(cursor_offset) = std::env::var("Q_PROMPT_OFFSET_WORKAROUND") {
if let Ok(cursor_offset) = fig_os_shim::Env::new().q_prompt_offset_workaround() {
if let Ok(offset) = cursor_offset.parse::<i32>() {
self.shell_state.cmd_cursor = self.shell_state.cmd_cursor.map(|cursor| Point {
column: Column((cursor.column.0 as i32 - offset).max(0) as usize),
Expand Down
1 change: 1 addition & 0 deletions crates/fig_api_client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ aws-types.workspace = true
bytes.workspace = true
fig_auth.workspace = true
fig_aws_common.workspace = true
fig_os_shim.workspace = true
fig_request.workspace = true
fig_settings.workspace = true
fig_util.workspace = true
Expand Down
4 changes: 1 addition & 3 deletions crates/fig_api_client/src/clients/streaming_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,7 @@ pub struct StreamingClient {

impl StreamingClient {
pub async fn new() -> Result<Self, Error> {
let client = if fig_util::system_info::in_cloudshell()
|| std::env::var("Q_USE_SENDMESSAGE").is_ok_and(|v| !v.is_empty())
{
let client = if fig_util::system_info::in_cloudshell() || fig_os_shim::Env::new().q_use_sendmessage() {
Self::new_qdeveloper_client(&Endpoint::load_q()).await?
} else {
Self::new_codewhisperer_client(&Endpoint::load_codewhisperer()).await
Expand Down
2 changes: 1 addition & 1 deletion crates/fig_desktop/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ async fn main() -> ExitCode {

#[cfg(target_os = "linux")]
{
match std::env::var("Q_BACKEND").ok().as_deref() {
match fig_os_shim::Env::new().q_backend().ok().as_deref() {
Some("default") => {},
// SAFETY: we are calling set_var in a single-threaded context.
Some(backend) => unsafe { std::env::set_var("GDK_BACKEND", backend) },
Expand Down
2 changes: 1 addition & 1 deletion crates/fig_install/src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ const DEFAULT_RELEASE_URL: &str = "https://desktop-release.q.us-east-1.amazonaws
/// - The setting `install.releaseUrl`
/// - Falls back to the default or the build time env var `Q_BUILD_DESKTOP_RELEASE_URL`
static RELEASE_URL: LazyLock<Url> = LazyLock::new(|| {
match std::env::var("Q_DESKTOP_RELEASE_URL") {
match fig_os_shim::Env::new().q_desktop_release_url() {
Ok(s) => Url::parse(&s),
Err(_) => match fig_settings::settings::get_string("install.releaseUrl") {
Ok(Some(s)) => Url::parse(&s),
Expand Down
1 change: 1 addition & 0 deletions crates/fig_log/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ default = []
# sentry = ["dep:sentry-tracing"]

[dependencies]
fig_os_shim.workspace = true
fig_util.workspace = true
parking_lot.workspace = true
thiserror.workspace = true
Expand Down
13 changes: 6 additions & 7 deletions crates/fig_log/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ use std::path::Path;
use std::sync::Mutex;

use fig_util::CHAT_BINARY_NAME;
use fig_util::env_var::Q_LOG_LEVEL;
use thiserror::Error;
use tracing::info;
use tracing::level_filters::LevelFilter;
Expand Down Expand Up @@ -192,11 +191,11 @@ pub fn initialize_logging<T: AsRef<Path>>(args: LogArgs<T>) -> Result<LogGuard,
///
/// Returns a string identifying the current log level.
pub fn get_log_level() -> String {
Q_LOG_LEVEL_GLOBAL
.lock()
.unwrap()
.clone()
.unwrap_or_else(|| std::env::var(Q_LOG_LEVEL).unwrap_or_else(|_| DEFAULT_FILTER.to_string()))
Q_LOG_LEVEL_GLOBAL.lock().unwrap().clone().unwrap_or_else(|| {
fig_os_shim::Env::new()
.q_log_level()
.unwrap_or_else(|_| DEFAULT_FILTER.to_string())
})
}

/// Set the log level to the given level.
Expand Down Expand Up @@ -247,7 +246,7 @@ fn create_filter_layer() -> EnvFilter {
.lock()
.unwrap()
.clone()
.or_else(|| std::env::var(Q_LOG_LEVEL).ok());
.or_else(|| fig_os_shim::Env::new().q_log_level().ok());

match log_level {
Some(level) => EnvFilter::builder()
Expand Down
77 changes: 77 additions & 0 deletions crates/fig_os_shim/src/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,83 @@ impl Env {
pub fn in_appimage(&self) -> bool {
self.get_os("APPIMAGE").is_some()
}

// Q-specific environment variable methods
pub fn q_fake_is_remote(&self) -> bool {
self.get_os("Q_FAKE_IS_REMOTE").is_some()
Copy link
Contributor

@kkashilk kkashilk Oct 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: rename get_os to get_os_env_var

}

pub fn q_log_level(&self) -> Result<String, VarError> {
self.get("Q_LOG_LEVEL")
}

pub fn q_log_stdout(&self) -> bool {
self.get_os("Q_LOG_STDOUT").is_some()
}

pub fn amazon_q_sigv4(&self) -> bool {
self.get("AMAZON_Q_SIGV4").is_ok_and(|v| !v.is_empty())
}

pub fn amazon_q_chat_shell(&self) -> String {
self.get("AMAZON_Q_CHAT_SHELL").unwrap_or_else(|_| "bash".to_string())
}

pub fn q_cli_client_application(&self) -> Result<String, VarError> {
self.get("Q_CLI_CLIENT_APPLICATION")
}

pub fn q_parent(&self) -> Result<String, VarError> {
self.get("Q_PARENT")
}

pub fn q_term(&self) -> Result<String, VarError> {
self.get("Q_TERM")
}

pub fn q_using_zsh_autosuggestions(&self) -> bool {
self.get_os("Q_USING_ZSH_AUTOSUGGESTIONS").is_some()
}

pub fn q_init_snapshot_test(&self) -> bool {
self.get_os("Q_INIT_SNAPSHOT_TEST").is_some()
}

pub fn q_desktop_release_url(&self) -> Result<String, VarError> {
self.get("Q_DESKTOP_RELEASE_URL")
}

pub fn q_inline_shell_completion_cache_enabled(&self) -> bool {
self.get_os("Q_INLINE_SHELL_COMPLETION_CACHE_DISABLE").is_none()
}

pub fn q_inline_shell_completion_history_count(&self) -> Result<String, VarError> {
self.get("Q_INLINE_SHELL_COMPLETION_HISTORY_COUNT")
}

pub fn q_inline_shell_completion_debounce_ms(&self) -> Result<String, VarError> {
self.get("Q_INLINE_SHELL_COMPLETION_DEBOUNCE_MS")
}

pub fn q_backend(&self) -> Result<String, VarError> {
self.get("Q_BACKEND")
}

pub fn q_prompt_offset_workaround(&self) -> Result<String, VarError> {
self.get("Q_PROMPT_OFFSET_WORKAROUND")
}

pub fn q_use_sendmessage(&self) -> bool {
self.get("Q_USE_SENDMESSAGE").is_ok_and(|v| !v.is_empty())
}

pub fn q_custom_cert(&self) -> Result<String, VarError> {
self.get("Q_CUSTOM_CERT")
}

pub fn has_q_parent(&self) -> bool {
self.q_parent().is_ok()
}
}

impl Shim for Env {
Expand Down
1 change: 1 addition & 0 deletions crates/fig_request/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ workspace = true
bytes.workspace = true
cfg-if.workspace = true
cookie = "0.18.0"
fig_os_shim.workspace = true
fig_settings.workspace = true
fig_util.workspace = true
reqwest_cookie_store = "0.8.0"
Expand Down
3 changes: 2 additions & 1 deletion crates/fig_request/src/reqwest_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ pub fn create_default_root_cert_store() -> RootCertStore {
let _ = root_cert_store.add(cert);
}

let custom_cert = std::env::var("Q_CUSTOM_CERT")
let custom_cert = fig_os_shim::Env::new()
.q_custom_cert()
.ok()
.or_else(|| fig_settings::state::get_string("Q_CUSTOM_CERT").ok().flatten());

Expand Down
2 changes: 1 addition & 1 deletion crates/fig_util/src/directories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ pub fn remote_socket_path() -> Result<PathBuf> {
// Normal implementation for non-test code
// TODO(grant): This is only enabled on Linux for now to prevent public dist
if is_remote() && !in_cloudshell() && cfg!(target_os = "linux") {
if let Some(parent_socket) = std::env::var_os(Q_PARENT) {
if let Some(parent_socket) = fig_os_shim::Env::new().get_os(Q_PARENT) {
Ok(PathBuf::from(parent_socket))
} else {
Err(DirectoryError::QParentNotSet)
Expand Down
10 changes: 4 additions & 6 deletions crates/fig_util/src/system_info/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ use sha2::{
};

use crate::Error;
use crate::env_var::Q_PARENT;
use crate::manifest::is_minimal;

/// The support level for different platforms
Expand Down Expand Up @@ -266,13 +265,13 @@ pub fn in_wsl() -> bool {
/// Is the calling binary running on a remote instance
pub fn is_remote() -> bool {
// TODO(chay): Add detection for inside docker container
in_ssh() || in_cloudshell() || in_wsl() || std::env::var_os("Q_FAKE_IS_REMOTE").is_some()
in_ssh() || in_cloudshell() || in_wsl() || fig_os_shim::Env::new().q_fake_is_remote()
}

/// Determines if we have an IPC path to a Desktop app from a remote environment
pub fn has_parent() -> bool {
static HAS_PARENT: OnceLock<bool> = OnceLock::new();
*HAS_PARENT.get_or_init(|| std::env::var_os(Q_PARENT).is_some())
*HAS_PARENT.get_or_init(|| fig_os_shim::Env::new().has_q_parent())
}

/// This true if the env var `AWS_EXECUTION_ENV=CloudShell`
Expand All @@ -283,13 +282,12 @@ pub fn in_cloudshell() -> bool {

pub fn in_codespaces() -> bool {
static IN_CODESPACES: OnceLock<bool> = OnceLock::new();
*IN_CODESPACES
.get_or_init(|| std::env::var_os("CODESPACES").is_some() || std::env::var_os("Q_CODESPACES").is_some())
*IN_CODESPACES.get_or_init(|| fig_os_shim::Env::new().in_codespaces())
}

pub fn in_ci() -> bool {
static IN_CI: OnceLock<bool> = OnceLock::new();
*IN_CI.get_or_init(|| std::env::var_os("CI").is_some() || std::env::var_os("Q_CI").is_some())
*IN_CI.get_or_init(|| fig_os_shim::Env::new().in_ci())
}

#[cfg(target_os = "macos")]
Expand Down
8 changes: 5 additions & 3 deletions crates/figterm/src/inline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,21 @@ static INLINE_ENABLED: Mutex<bool> = Mutex::const_new(true);
static LAST_RECEIVED: Mutex<Option<SystemTime>> = Mutex::const_new(None);

static CACHE_ENABLED: LazyLock<bool> =
LazyLock::new(|| std::env::var_os("Q_INLINE_SHELL_COMPLETION_CACHE_DISABLE").is_none());
LazyLock::new(|| fig_os_shim::Env::new().q_inline_shell_completion_cache_enabled());
static COMPLETION_CACHE: LazyLock<Mutex<CompletionCache>> = LazyLock::new(|| Mutex::new(CompletionCache::new()));

static TELEMETRY_QUEUE: Mutex<TelemetryQueue> = Mutex::const_new(TelemetryQueue::new());

static HISTORY_COUNT: LazyLock<usize> = LazyLock::new(|| {
std::env::var("Q_INLINE_SHELL_COMPLETION_HISTORY_COUNT")
fig_os_shim::Env::new()
.q_inline_shell_completion_history_count()
.ok()
.and_then(|s| s.parse().ok())
.unwrap_or(HISTORY_COUNT_DEFAULT)
});
static DEBOUNCE_DURATION: LazyLock<Duration> = LazyLock::new(|| {
std::env::var("Q_INLINE_SHELL_COMPLETION_DEBOUNCE_MS")
fig_os_shim::Env::new()
.q_inline_shell_completion_debounce_ms()
.ok()
.and_then(|s| s.parse().ok())
.map_or(DEBOUNCE_DURATION_DEFAULT, Duration::from_millis)
Expand Down
3 changes: 1 addition & 2 deletions crates/figterm/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ use fig_settings::state;
use fig_util::consts::CLI_BINARY_NAME;
use fig_util::env_var::{
Q_LOG_LEVEL,
Q_PARENT,
Q_SHELL,
Q_TERM,
QTERM_SESSION_ID,
Expand Down Expand Up @@ -491,7 +490,7 @@ fn figterm_main(command: Option<&[String]>) -> Result<()> {
std::env::set_var(QTERM_SESSION_ID, &session_id);
}

let parent_id = std::env::var(Q_PARENT).ok();
let parent_id = fig_os_shim::Env::new().q_parent().ok();

let mut terminal = SystemTerminal::new_from_stdio()?;
let screen_size = terminal.get_screen_size()?;
Expand Down
3 changes: 1 addition & 2 deletions crates/q_cli/src/cli/doctor/checks/sshd_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use async_trait::async_trait;
use eyre::Context;
use fig_auth::is_amzn_user;
use fig_util::CLI_BINARY_NAME;
use fig_util::env_var::Q_PARENT;
use fig_util::url::AUTOCOMPLETE_SSH_WIKI;
use owo_colors::OwoColorize;
use regex::Regex;
Expand Down Expand Up @@ -55,7 +54,7 @@ impl DoctorCheck<()> for SshdConfigCheck {

let sshd_config = match std::fs::read_to_string(sshd_config_path).context("Could not read sshd_config") {
Ok(config) => config,
Err(_err) if std::env::var_os(Q_PARENT).is_some() => {
Err(_err) if fig_os_shim::Env::new().q_parent().is_ok() => {
// We will assume amzn users have this configured correctly and warn other users.
if is_amzn_user().await.unwrap_or_default() {
return Ok(());
Expand Down
Loading
Loading