Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 2 additions & 3 deletions src/apps/cli/src/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1076,9 +1076,8 @@ async fn handle_relay_auth_error(
return;
}
let mut current_context = account_context.write().await;
if !current_context
.as_ref()
.is_some_and(|context| context.session.token == expected_token)
if current_context
.as_ref().is_none_or(|context| context.session.token != expected_token)
{
tracing::debug!("Ignoring auth error cleanup for a replaced account");
return;
Expand Down
9 changes: 3 additions & 6 deletions src/apps/cli/src/account_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ pub(crate) fn start_settings_sync_loop() {
on_settings_pushed: Some(Arc::new(|| {
crate::peer_host::notify_controllers_settings_changed();
})),
on_token_expired: Some(Arc::new(|| crate::account::mark_token_expired())),
on_token_expired: Some(Arc::new(crate::account::mark_token_expired)),
..Default::default()
};
settings_sync::start_settings_sync_engine(hooks);
Expand Down Expand Up @@ -96,18 +96,15 @@ pub(crate) async fn push_settings_after_local_change() {
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Default)]
pub(crate) enum SyncStatus {
#[default]
Idle,
Syncing,
Done,
Failed,
}

impl Default for SyncStatus {
fn default() -> Self {
Self::Idle
}
}

#[derive(Debug, Clone)]
pub(crate) struct SyncProgress {
Expand Down
3 changes: 1 addition & 2 deletions src/apps/cli/src/hook_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use bitfun_product_domains::external_sources::{
};
use clap::{Subcommand, ValueEnum};
use serde::Serialize;
use std::path::PathBuf;

#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum HookImportOutputFormat {
Expand Down Expand Up @@ -78,7 +77,7 @@ pub(crate) enum HookAction {
}

pub(crate) async fn run(action: Option<HookAction>) -> Result<()> {
let workspace = std::env::current_dir().ok().map(PathBuf::from);
let workspace = std::env::current_dir().ok();
match action.unwrap_or(HookAction::List {
refresh: false,
format: HookImportOutputFormat::Text,
Expand Down
3 changes: 1 addition & 2 deletions src/apps/cli/src/mcp_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use bitfun_product_domains::external_sources::{
EXTERNAL_MCP_IMPORT_SCHEMA_V1,
};
use clap::ValueEnum;
use std::path::PathBuf;

#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
pub(crate) enum McpImportOutputFormat {
Expand All @@ -21,7 +20,7 @@ pub(crate) struct McpImportCommand {
}

pub(crate) async fn execute(command: McpImportCommand) -> Result<()> {
let workspace = std::env::current_dir().ok().map(PathBuf::from);
let workspace = std::env::current_dir().ok();
let plan = bitfun_core::external_mcp_import::plan_external_mcp_import(workspace.clone())
.await
.map_err(operation_error)?;
Expand Down
2 changes: 1 addition & 1 deletion src/apps/cli/src/modes/chat/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,7 @@ impl ChatMode {
.and_then(|command| command.native_collision.as_ref())
.map(|collision| collision.conflict_key.as_str());
let expected_preference_revision = native_conflict_key
.and_then(|_| self.external_source_snapshot.as_ref())
.and(self.external_source_snapshot.as_ref())
.map(|snapshot| snapshot.preference_revision);
let expanded = tokio::task::block_in_place(|| {
rt_handle.block_on(expand_external_prompt_command(
Expand Down
11 changes: 3 additions & 8 deletions src/apps/cli/src/modes/chat/external_review.rs
Original file line number Diff line number Diff line change
Expand Up @@ -682,13 +682,10 @@ fn external_tool_pending_notice_key(snapshot: &ExternalSourceCatalogSnapshot) ->
.map(|conflict| format!("conflict:{}", conflict.conflict_key)),
)
.collect::<Vec<_>>();
decisions.extend(snapshot.diagnostics.iter().filter_map(|diagnostic| {
matches!(
decisions.extend(snapshot.diagnostics.iter().filter(|&diagnostic| matches!(
diagnostic.severity,
ExternalSourceDiagnosticSeverity::Warning | ExternalSourceDiagnosticSeverity::Error
)
.then(|| {
format!(
)).map(|diagnostic| format!(
"diagnostic:{:?}:{}:{}:{}",
diagnostic.severity,
diagnostic.code,
Expand All @@ -698,9 +695,7 @@ fn external_tool_pending_notice_key(snapshot: &ExternalSourceCatalogSnapshot) ->
.as_ref()
.map(|source| source.stable_key())
.unwrap_or_default()
)
})
}));
)));
if decisions.is_empty() {
return None;
}
Expand Down
2 changes: 1 addition & 1 deletion src/apps/cli/src/modes/exec/lifecycle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -996,7 +996,7 @@ impl ExecMode {
Err(error) => (Vec::new(), Err(error)),
};
for envelope in buffered_events {
let _ = self
self
.project_exec_nonterminal_event(
&envelope,
&session_id,
Expand Down
16 changes: 8 additions & 8 deletions src/apps/cli/src/peer_host/commands/external_sources.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,14 @@ async fn dispatch_inner(
public_snapshot(snapshot)
}

fn control_request(
request: &Value,
) -> ExternalSourceOperationResult<ExternalSourceControlRequestV1> {
serde_json::from_value(request.clone()).map_err(|_| {
ExternalSourceOperationError::invalid_request("Invalid external source control request")
})
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -339,11 +347,3 @@ mod tests {
));
}
}

fn control_request(
request: &Value,
) -> ExternalSourceOperationResult<ExternalSourceControlRequestV1> {
serde_json::from_value(request.clone()).map_err(|_| {
ExternalSourceOperationError::invalid_request("Invalid external source control request")
})
}
4 changes: 2 additions & 2 deletions src/apps/cli/src/peer_host/deny.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,11 @@ static CLI_UNSUPPORTED_EXACT: &[&str] = &[
];

pub(crate) fn is_local_only_command(command: &str) -> bool {
LOCAL_ONLY_COMMANDS.iter().any(|denied| *denied == command)
LOCAL_ONLY_COMMANDS.contains(&command)
}

pub(crate) fn is_cli_unsupported_command(command: &str) -> bool {
if CLI_UNSUPPORTED_EXACT.iter().any(|c| *c == command) {
if CLI_UNSUPPORTED_EXACT.contains(&command) {
return true;
}
let prefixes = [
Expand Down
2 changes: 1 addition & 1 deletion src/apps/cli/src/peer_host/fanout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,7 @@ async fn fanout_peer_device_event_once(queued: QueuedPeerDeviceEvent) {
};
let correlation_id = uuid::Uuid::new_v4().to_string();
if let Err(error) = relay_client
.send_device_message(&target, &correlation_id, &encrypted_data, &nonce)
.send_device_message(target, &correlation_id, &encrypted_data, &nonce)
.await
{
tracing::debug!("Peer event fanout to {target} failed: {error}");
Expand Down
28 changes: 14 additions & 14 deletions src/apps/cli/src/peer_host/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1069,6 +1069,20 @@ fn spawn_turn_cancellation(
});
}

static PEER_HOST_STATE: OnceLock<PeerHostState> = OnceLock::new();

pub(crate) fn set_peer_host_state(state: PeerHostState) -> Result<(), PeerHostState> {
PEER_HOST_STATE.set(state)
}

pub(crate) fn try_peer_host_state() -> Option<&'static PeerHostState> {
PEER_HOST_STATE.get()
}

pub(crate) fn peer_host_state() -> Result<&'static PeerHostState, String> {
try_peer_host_state().ok_or_else(|| "CLI peer host is not initialized".to_string())
}

#[cfg(test)]
mod tests {
use std::collections::HashSet;
Expand Down Expand Up @@ -1831,17 +1845,3 @@ mod tests {
assert!(tracker.register_root(root).is_err());
}
}

static PEER_HOST_STATE: OnceLock<PeerHostState> = OnceLock::new();

pub(crate) fn set_peer_host_state(state: PeerHostState) -> Result<(), PeerHostState> {
PEER_HOST_STATE.set(state)
}

pub(crate) fn try_peer_host_state() -> Option<&'static PeerHostState> {
PEER_HOST_STATE.get()
}

pub(crate) fn peer_host_state() -> Result<&'static PeerHostState, String> {
try_peer_host_state().ok_or_else(|| "CLI peer host is not initialized".to_string())
}
2 changes: 1 addition & 1 deletion src/apps/cli/tests/acp_stdio_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ async fn acp_stdio_preserves_mode_and_history_across_restart_then_closes_active_
.find(|response| response.get("error").is_some())
.expect("one concurrent session/load must be rejected");
assert!(loaded.get("error").is_none(), "{loaded}");
assert_eq!(current_config_value(&loaded, "mode"), Some(&json!("Plan")));
assert_eq!(current_config_value(loaded, "mode"), Some(&json!("Plan")));
assert!(
!replay_updates.is_empty(),
"session/load must replay persisted history before its success response"
Expand Down
2 changes: 1 addition & 1 deletion src/apps/cli/tests/terminal_process_contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ fn strict_stream_json_events(output: &str) -> Vec<serde_json::Value> {
if !is_protocol_candidate {
return None;
}
let value = serde_json::from_str::<serde_json::Value>(&line).unwrap_or_else(|error| {
let value = serde_json::from_str::<serde_json::Value>(line).unwrap_or_else(|error| {
panic!("invalid stream-json PTY line {line:?}: {error}\nfull output:\n{output}")
});
assert!(
Expand Down
2 changes: 1 addition & 1 deletion src/apps/desktop/src/api/clipboard_file_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ mod macos_clipboard {

pub(super) fn get_clipboard_files() -> Result<Vec<String>, String> {
let output = Command::new("osascript")
.args(&[
.args([
"-e",
r#"
set theFiles to {}
Expand Down
2 changes: 1 addition & 1 deletion src/apps/desktop/src/api/computer_use_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub async fn computer_use_open_system_settings(
.arg(url)
.status()
.map_err(|e| e.to_string())?;
return Ok(());
Ok(())
}
#[cfg(target_os = "windows")]
{
Expand Down
90 changes: 47 additions & 43 deletions src/apps/desktop/src/api/path_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,10 @@ pub fn stat_local_path_metadata(
)
})?;

// Not collapsible: on Windows a junction/reparse point is not reported as a
// symlink by `file_type()`, so the extra attribute check is load-bearing.
// Clippy only sees the `#[cfg(not(windows))] { false }` arm on other targets.
#[allow(clippy::needless_bool)]
let is_symlink = if link_metadata.file_type().is_symlink() {
true
} else {
Expand Down Expand Up @@ -299,49 +303,6 @@ fn encode_remote_file_bytes(bytes: Vec<u8>, encoding: Option<&str>) -> Result<St
String::from_utf8(bytes).map_err(|e| format!("File is not valid UTF-8: {}", e))
}

#[cfg(test)]
mod tests {
use super::{
encode_remote_file_bytes, get_path_manager_arc, should_force_local_assistant_path,
};

#[test]
fn remote_file_bytes_support_explicit_base64_encoding() {
let png_header = vec![0x89, b'P', b'N', b'G'];
assert_eq!(
encode_remote_file_bytes(png_header, Some("base64")).expect("base64 should encode"),
"iVBORw=="
);
}

#[test]
fn remote_file_bytes_preserve_text_default() {
assert_eq!(
encode_remote_file_bytes(b"hello".to_vec(), None).expect("text should decode"),
"hello"
);
assert!(encode_remote_file_bytes(vec![0xff], None).is_err());
}

#[test]
fn local_assistant_path_ignores_legacy_remote_fallback_without_explicit_hint() {
let assistant_path = get_path_manager_arc()
.assistant_workspace_dir("path-target-local-save", None)
.to_string_lossy()
.to_string();

assert!(should_force_local_assistant_path(&assistant_path, None));
assert!(!should_force_local_assistant_path(
&assistant_path,
Some("explicit-remote-connection")
));
assert!(!should_force_local_assistant_path(
"/tmp/regular-project",
None
));
}
}

pub async fn write_text_file(
app_state: &AppState,
raw_path: &str,
Expand Down Expand Up @@ -618,3 +579,46 @@ pub async fn create_directory(
}
}
}

#[cfg(test)]
mod tests {
use super::{
encode_remote_file_bytes, get_path_manager_arc, should_force_local_assistant_path,
};

#[test]
fn remote_file_bytes_support_explicit_base64_encoding() {
let png_header = vec![0x89, b'P', b'N', b'G'];
assert_eq!(
encode_remote_file_bytes(png_header, Some("base64")).expect("base64 should encode"),
"iVBORw=="
);
}

#[test]
fn remote_file_bytes_preserve_text_default() {
assert_eq!(
encode_remote_file_bytes(b"hello".to_vec(), None).expect("text should decode"),
"hello"
);
assert!(encode_remote_file_bytes(vec![0xff], None).is_err());
}

#[test]
fn local_assistant_path_ignores_legacy_remote_fallback_without_explicit_hint() {
let assistant_path = get_path_manager_arc()
.assistant_workspace_dir("path-target-local-save", None)
.to_string_lossy()
.to_string();

assert!(should_force_local_assistant_path(&assistant_path, None));
assert!(!should_force_local_assistant_path(
&assistant_path,
Some("explicit-remote-connection")
));
assert!(!should_force_local_assistant_path(
"/tmp/regular-project",
None
));
}
}
2 changes: 1 addition & 1 deletion src/apps/desktop/src/api/peer_host_invoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ struct HostInvokeBridgeRequest {
}

pub fn is_local_only_command(command: &str) -> bool {
LOCAL_ONLY_COMMANDS.iter().any(|denied| *denied == command)
LOCAL_ONLY_COMMANDS.contains(&command)
}

/// Register a controller device id to receive peer UI events.
Expand Down
Loading