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
28 changes: 28 additions & 0 deletions src/apps/desktop/src/api/dto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ pub struct WorkspaceIdentityDto {
pub emoji: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceWorktreeInfoDto {
pub path: String,
pub branch: Option<String>,
pub main_repo_path: String,
pub is_main: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct WorkspaceInfoDto {
Expand All @@ -57,6 +66,8 @@ pub struct WorkspaceInfoDto {
pub statistics: Option<ProjectStatisticsDto>,
pub identity: Option<WorkspaceIdentityDto>,
#[serde(skip_serializing_if = "Option::is_none")]
pub worktree: Option<WorkspaceWorktreeInfoDto>,
#[serde(skip_serializing_if = "Option::is_none")]
pub connection_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub connection_name: Option<String>,
Expand Down Expand Up @@ -97,6 +108,10 @@ impl WorkspaceInfoDto {
.identity
.as_ref()
.map(WorkspaceIdentityDto::from_workspace_identity),
worktree: info
.worktree
.as_ref()
.map(WorkspaceWorktreeInfoDto::from_workspace_worktree_info),
connection_id,
connection_name,
}
Expand All @@ -116,6 +131,19 @@ impl WorkspaceIdentityDto {
}
}

impl WorkspaceWorktreeInfoDto {
pub fn from_workspace_worktree_info(
info: &bitfun_core::service::workspace::manager::WorkspaceWorktreeInfo,
) -> Self {
Self {
path: info.path.clone(),
branch: info.branch.clone(),
main_repo_path: info.main_repo_path.clone(),
is_main: info.is_main,
}
}
}

impl WorkspaceTypeDto {
pub fn from_workspace_type(
workspace_type: &bitfun_core::service::workspace::manager::WorkspaceType,
Expand Down
26 changes: 25 additions & 1 deletion src/apps/desktop/src/api/terminal_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ use bitfun_core::service::terminal::{
ExecuteCommandResponse as CoreExecuteCommandResponse,
GetHistoryRequest as CoreGetHistoryRequest, GetHistoryResponse as CoreGetHistoryResponse,
ResizeRequest as CoreResizeRequest, SendCommandRequest as CoreSendCommandRequest,
SessionResponse as CoreSessionResponse, ShellInfo as CoreShellInfo, ShellType,
SessionResponse as CoreSessionResponse, SessionSource as CoreSessionSource,
ShellInfo as CoreShellInfo, ShellType,
SignalRequest as CoreSignalRequest, TerminalApi, TerminalConfig,
WriteRequest as CoreWriteRequest,
};
Expand Down Expand Up @@ -97,6 +98,7 @@ pub struct CreateSessionRequest {
pub env: Option<std::collections::HashMap<String, String>>,
pub cols: Option<u16>,
pub rows: Option<u16>,
pub source: Option<String>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand All @@ -114,6 +116,7 @@ pub struct SessionResponse {
/// None/null for local terminals.
#[serde(skip_serializing_if = "Option::is_none")]
pub connection_id: Option<String>,
pub source: String,
}

impl From<CoreSessionResponse> for SessionResponse {
Expand All @@ -128,6 +131,7 @@ impl From<CoreSessionResponse> for SessionResponse {
cols: resp.cols,
rows: resp.rows,
connection_id: None,
source: format_session_source(&resp.source),
}
}
}
Expand Down Expand Up @@ -276,6 +280,21 @@ fn parse_shell_type(s: &str) -> Option<ShellType> {
}
}

fn parse_session_source(source: &str) -> Option<CoreSessionSource> {
match source.to_lowercase().as_str() {
"manual" => Some(CoreSessionSource::Manual),
"agent" => Some(CoreSessionSource::Agent),
_ => None,
}
}

fn format_session_source(source: &CoreSessionSource) -> String {
match source {
CoreSessionSource::Manual => "manual".to_string(),
CoreSessionSource::Agent => "agent".to_string(),
}
}

#[tauri::command]
pub async fn terminal_get_shells(
state: State<'_, TerminalState>,
Expand Down Expand Up @@ -326,6 +345,7 @@ pub async fn terminal_create(
request.cols.unwrap_or(80),
request.rows.unwrap_or(24),
Some(remote_cwd.as_str()),
request.source.as_deref().and_then(parse_session_source),
)
.await
.map_err(|e| format!("Failed to create remote session: {}", e))?;
Expand All @@ -344,6 +364,7 @@ pub async fn terminal_create(
cols: session.cols,
rows: session.rows,
connection_id: Some(connection_id.clone()),
source: format_session_source(&session.source),
};

let app_handle = _app.clone();
Expand Down Expand Up @@ -408,6 +429,7 @@ pub async fn terminal_create(
cols: request.cols,
rows: request.rows,
remote_connection_id: None,
source: request.source.as_deref().and_then(parse_session_source),
};

let session = api
Expand Down Expand Up @@ -437,6 +459,7 @@ pub async fn terminal_get(
cols: session.cols,
rows: session.rows,
connection_id: Some(session.connection_id),
source: format_session_source(&session.source),
});
}
}
Expand Down Expand Up @@ -472,6 +495,7 @@ pub async fn terminal_list(
cols: s.cols,
rows: s.rows,
connection_id: Some(s.connection_id),
source: format_session_source(&s.source),
}));
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use futures::StreamExt;
use log::{debug, error, info};
use serde_json::{json, Value};
use std::time::{Duration, Instant};
use terminal_core::session::SessionSource;
use terminal_core::shell::{ShellDetector, ShellType};
use terminal_core::{
CommandCompletionReason, CommandStreamEvent, ExecuteCommandRequest, SendCommandRequest,
Expand Down Expand Up @@ -509,6 +510,7 @@ Usage notes:
)),
shell_type: shell_type.clone(),
env: Some(Self::noninteractive_env()),
source: Some(SessionSource::Agent),
..Default::default()
},
)
Expand Down Expand Up @@ -723,6 +725,7 @@ impl BashTool {
session_name: None,
shell_type,
env: Some(Self::noninteractive_env()),
source: Some(SessionSource::Agent),
..Default::default()
},
)
Expand Down
2 changes: 2 additions & 0 deletions src/crates/core/src/service/remote_connect/remote_server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1680,6 +1680,7 @@ impl RemoteExecutionDispatcher {
// exists and the 30-second readiness wait is skipped entirely.
{
use terminal_core::{TerminalApi, TerminalBindingOptions};
use terminal_core::session::SessionSource;
let sid = session_id.to_string();
let binding_workspace_for_terminal = binding_workspace.clone();
tokio::spawn(async move {
Expand All @@ -1702,6 +1703,7 @@ impl RemoteExecutionDispatcher {
env: Some(
crate::agentic::tools::implementations::bash_tool::BashTool::noninteractive_env(),
),
source: Some(SessionSource::Agent),
..Default::default()
},
)
Expand Down
4 changes: 4 additions & 0 deletions src/crates/core/src/service/remote_ssh/remote_terminal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
//! - This eliminates Mutex deadlock between read and write operations

use crate::service::remote_ssh::manager::SSHConnectionManager;
use crate::service::terminal::session::SessionSource;
use anyhow::Context;
use std::collections::HashMap;
use std::sync::Arc;
Expand All @@ -31,6 +32,7 @@ pub struct RemoteTerminalSession {
pub status: SessionStatus,
pub cols: u16,
pub rows: u16,
pub source: SessionSource,
}

#[derive(Debug, Clone, PartialEq)]
Expand Down Expand Up @@ -87,6 +89,7 @@ impl RemoteTerminalManager {
cols: u16,
rows: u16,
initial_cwd: Option<&str>,
source: Option<SessionSource>,
) -> anyhow::Result<CreateSessionResult> {
let ssh_guard = self.ssh_manager.read().await;
let manager = ssh_guard.as_ref().context("SSH manager not initialized")?;
Expand Down Expand Up @@ -123,6 +126,7 @@ impl RemoteTerminalManager {
status: SessionStatus::Active,
cols,
rows,
source: source.unwrap_or_default(),
};

{
Expand Down
10 changes: 9 additions & 1 deletion src/crates/core/src/service/terminal/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ use crate::config::TerminalConfig;
use crate::events::TerminalEvent;
use crate::session::{
get_session_manager, init_session_manager, is_session_manager_initialized,
CommandCompletionReason, CommandExecuteResult, ExecuteOptions, SessionManager, TerminalSession,
CommandCompletionReason, CommandExecuteResult, ExecuteOptions, SessionManager, SessionSource,
TerminalSession,
};
use crate::shell::{ShellDetector, ShellType};
use crate::{TerminalError, TerminalResult};
Expand Down Expand Up @@ -48,6 +49,9 @@ pub struct CreateSessionRequest {
/// Optional remote connection ID (for remote workspace sessions)
#[serde(rename = "remoteConnectionId", skip_serializing_if = "Option::is_none")]
pub remote_connection_id: Option<String>,
/// Optional session creation source
#[serde(skip_serializing_if = "Option::is_none")]
pub source: Option<SessionSource>,
}

/// Response for session creation
Expand All @@ -69,6 +73,8 @@ pub struct SessionResponse {
/// Terminal dimensions
pub cols: u16,
pub rows: u16,
/// Session creation source
pub source: SessionSource,
}

impl From<TerminalSession> for SessionResponse {
Expand All @@ -82,6 +88,7 @@ impl From<TerminalSession> for SessionResponse {
status: format!("{:?}", session.status),
cols: session.cols,
rows: session.rows,
source: session.source,
}
}
}
Expand Down Expand Up @@ -314,6 +321,7 @@ impl TerminalApi {
request.env,
request.cols,
request.rows,
request.source,
)
.await?;

Expand Down
3 changes: 2 additions & 1 deletion src/crates/core/src/service/terminal/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ pub use pty::{
};
pub use session::{
CommandCompletionReason, CommandExecuteResult, CommandStream, CommandStreamEvent,
ExecuteOptions, SessionManager, SessionStatus, TerminalBindingOptions, TerminalSession,
ExecuteOptions, SessionManager, SessionSource, SessionStatus, TerminalBindingOptions,
TerminalSession,
TerminalSessionBinding,
};
pub use shell::{
Expand Down
5 changes: 5 additions & 0 deletions src/crates/core/src/service/terminal/src/session/binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use dashmap::DashMap;
use log::warn;

use crate::session::get_session_manager;
use crate::session::SessionSource;
use crate::shell::ShellType;
use crate::{TerminalError, TerminalResult};

Expand All @@ -37,6 +38,8 @@ pub struct TerminalBindingOptions {
pub cols: Option<u16>,
/// Terminal rows (default: 30)
pub rows: Option<u16>,
/// Source of the terminal session
pub source: Option<SessionSource>,
}

/// Terminal Session Binding Manager
Expand Down Expand Up @@ -114,6 +117,7 @@ impl TerminalSessionBinding {
options.env,
options.cols,
options.rows,
options.source,
)
.await?;

Expand Down Expand Up @@ -191,6 +195,7 @@ impl TerminalSessionBinding {
options.env,
options.cols,
options.rows,
options.source,
)
.await?;

Expand Down
19 changes: 16 additions & 3 deletions src/crates/core/src/service/terminal/src/session/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::shell::{
};
use crate::{TerminalError, TerminalResult};

use super::{SessionStatus, TerminalSession};
use super::{SessionSource, SessionStatus, TerminalSession};

const COMMAND_TIMEOUT_INTERRUPT_GRACE_MS: Duration = Duration::from_millis(500);

Expand Down Expand Up @@ -387,9 +387,20 @@ impl SessionManager {
env: Option<HashMap<String, String>>,
cols: Option<u16>,
rows: Option<u16>,
source: Option<SessionSource>,
) -> TerminalResult<TerminalSession> {
self.create_session_with_options(session_id, name, shell_type, cwd, env, cols, rows, true)
.await
self.create_session_with_options(
session_id,
name,
shell_type,
cwd,
env,
cols,
rows,
true,
source,
)
.await
}

/// Create a new terminal session with optional shell integration
Expand All @@ -403,6 +414,7 @@ impl SessionManager {
cols: Option<u16>,
rows: Option<u16>,
enable_integration: bool,
source: Option<SessionSource>,
) -> TerminalResult<TerminalSession> {
// Use provided session ID or generate a new one
let session_id = session_id.unwrap_or_else(|| uuid::Uuid::new_v4().to_string());
Expand Down Expand Up @@ -506,6 +518,7 @@ impl SessionManager {
cwd,
cols,
rows,
source.unwrap_or_default(),
);

// Store the session
Expand Down
Loading
Loading