Files
galaxy/app/src/settings/local_control.rs
T
rkw6086 dbfa8bcd48 Complete agent monitoring and Galaxy Control integration
- expose command-monitor conversations and preserve visible agent transcripts
- add bounded polling and a dedicated shell interrupt tool
- improve direct-provider images, skills, tool history, and usage handling
- package and brand Galaxy Control across releases, installers, persistence, and docs
2026-07-29 15:04:58 -05:00

218 lines
5.7 KiB
Rust

//! Secure local setting that gates local control.
//!
//! This setting is local-only, kept out of the user-visible settings file, and
//! persisted through Galaxy's secure storage provider. It is the authoritative
//! enablement bit for local control.
use anyhow::Result;
use galaxy_core::channel::{Channel, ChannelState};
use galaxyui_extras::secure_storage;
use serde::{Deserialize, Serialize};
use settings::macros::define_settings_group;
use settings::{SecureSetting, Setting, SupportedPlatforms, SyncToCloud};
use warpui::{AppContext, ModelContext};
const LOCAL_CONTROL_MODE_STORAGE_KEY: &str = "LocalControlMode";
/// User-selected local-control availability.
#[derive(
Clone,
Copy,
Debug,
Default,
Deserialize,
Eq,
PartialEq,
schemars::JsonSchema,
Serialize,
settings_value::SettingsValue,
)]
#[schemars(
description = "Whether Galaxy Control local automation access is enabled.",
rename_all = "snake_case"
)]
pub enum LocalControlMode {
#[default]
Disabled,
Enabled,
}
/// Channel-based default: local control is on for internal dogfood builds and
/// off for public channels, where users must opt in through Settings > Galaxy Control.
fn default_mode_for_channel(channel: Channel) -> LocalControlMode {
if channel.is_dogfood() {
LocalControlMode::Enabled
} else {
LocalControlMode::Disabled
}
}
impl LocalControlMode {
pub const ALL: [Self; 2] = [Self::Disabled, Self::Enabled];
pub fn is_enabled(self) -> bool {
matches!(self, Self::Enabled)
}
pub fn as_dropdown_label(self) -> &'static str {
match self {
Self::Disabled => "Disabled",
Self::Enabled => "Enabled",
}
}
}
define_settings_group!(LocalControlSettings, settings: [
local_control_mode: LocalControlModeSetting,
]);
/// Setting wrapper for the authoritative local-control mode.
pub struct LocalControlModeSetting {
inner: LocalControlMode,
is_explicitly_set: bool,
}
impl LocalControlModeSetting {
fn emit_changed(
ctx: &mut ModelContext<LocalControlSettings>,
change_event_reason: settings::ChangeEventReason,
) {
ctx.emit(LocalControlSettingsChangedEvent::LocalControlModeSetting {
change_event_reason,
});
}
}
impl SecureSetting for LocalControlModeSetting {
fn write_secure_storage_value(
storage: &dyn secure_storage::SecureStorage,
key: &str,
value: &str,
) -> Result<(), secure_storage::Error> {
storage.write_value_with_owner_only_fallback(key, value)
}
}
impl Setting for LocalControlModeSetting {
type Group = LocalControlSettings;
type Value = LocalControlMode;
fn new(value: Option<Self::Value>) -> Self {
match value {
Some(value) => Self {
inner: value,
is_explicitly_set: true,
},
None => Self {
inner: Self::default_value(),
is_explicitly_set: false,
},
}
}
fn setting_name() -> &'static str {
"LocalControlModeSetting"
}
fn storage_key() -> &'static str {
LOCAL_CONTROL_MODE_STORAGE_KEY
}
fn supported_platforms() -> SupportedPlatforms {
SupportedPlatforms::DESKTOP
}
fn sync_to_cloud() -> SyncToCloud {
SyncToCloud::Never
}
fn is_private() -> bool {
true
}
fn value(&self) -> &Self::Value {
&self.inner
}
fn clear_value(&mut self, ctx: &mut ModelContext<Self::Group>) -> Result<()> {
Self::clear_from_secure_storage(ctx)?;
self.inner = self.validate(Self::default_value());
self.is_explicitly_set = false;
Self::emit_changed(ctx, settings::ChangeEventReason::Clear);
Ok(())
}
fn load_value(
&mut self,
new_value: Self::Value,
explicitly_set: bool,
ctx: &mut ModelContext<Self::Group>,
) -> Result<()> {
let validated = self.validate(new_value);
if self.value() != &validated || self.is_explicitly_set != explicitly_set {
self.inner = validated;
self.is_explicitly_set = explicitly_set;
Self::emit_changed(ctx, settings::ChangeEventReason::LocalChange);
}
Ok(())
}
fn set_value_from_cloud_sync(
&mut self,
_: Self::Value,
_: &mut ModelContext<Self::Group>,
) -> Result<()> {
Ok(())
}
fn set_value(
&mut self,
new_value: Self::Value,
ctx: &mut ModelContext<Self::Group>,
) -> Result<()> {
let changed_in_storage = Self::write_to_secure_storage(&new_value, ctx)?;
if self.value() != &new_value || changed_in_storage {
self.inner = self.validate(new_value);
self.is_explicitly_set = true;
Self::emit_changed(ctx, settings::ChangeEventReason::LocalChange);
}
Ok(())
}
fn default_value() -> Self::Value {
default_mode_for_channel(ChannelState::channel())
}
fn new_from_storage(ctx: &mut AppContext) -> Self {
Self::new(Self::read_from_secure_storage(ctx))
}
fn is_supported_on_current_platform(&self) -> bool {
SupportedPlatforms::DESKTOP.matches_current_platform()
}
fn is_value_explicitly_set(&self) -> bool {
self.is_explicitly_set
}
}
impl std::ops::Deref for LocalControlModeSetting {
type Target = LocalControlMode;
fn deref(&self) -> &Self::Target {
self.value()
}
}
impl LocalControlSettings {
pub fn mode(&self) -> LocalControlMode {
*self.local_control_mode
}
pub fn is_enabled(&self) -> bool {
self.mode().is_enabled()
}
}
#[cfg(test)]
#[path = "local_control_tests.rs"]
mod tests;