Skip to content
Closed
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
2 changes: 1 addition & 1 deletion cmux-tui/bindings/cpp/.cmux-resource-api.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"catalog_sha256": "b7f15e13d00bdf49127444f640663bdb08c500077778bdd48e61d0dde5cd247e",
"catalog_sha256": "0551d5afb6a5628e592ccf0fce8a7218a55fd9d4e02342928d69bb9cdbf7daa3",
"operations": {
"agent.list": {
"class": "read"
Expand Down
2 changes: 1 addition & 1 deletion cmux-tui/bindings/go/.cmux-resource-api.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"catalog_sha256": "b7f15e13d00bdf49127444f640663bdb08c500077778bdd48e61d0dde5cd247e",
"catalog_sha256": "0551d5afb6a5628e592ccf0fce8a7218a55fd9d4e02342928d69bb9cdbf7daa3",
"operations": {
"agent.list": {
"class": "read"
Expand Down
2 changes: 1 addition & 1 deletion cmux-tui/bindings/java/.cmux-resource-api.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"catalog_sha256": "b7f15e13d00bdf49127444f640663bdb08c500077778bdd48e61d0dde5cd247e",
"catalog_sha256": "0551d5afb6a5628e592ccf0fce8a7218a55fd9d4e02342928d69bb9cdbf7daa3",
"operations": {
"agent.list": {
"class": "read"
Expand Down
2 changes: 1 addition & 1 deletion cmux-tui/bindings/python/.cmux-resource-api.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"catalog_sha256": "b7f15e13d00bdf49127444f640663bdb08c500077778bdd48e61d0dde5cd247e",
"catalog_sha256": "0551d5afb6a5628e592ccf0fce8a7218a55fd9d4e02342928d69bb9cdbf7daa3",
"operations": {
"agent.list": {
"class": "read"
Expand Down
2 changes: 1 addition & 1 deletion cmux-tui/bindings/rust/.cmux-resource-api.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"catalog_sha256": "b7f15e13d00bdf49127444f640663bdb08c500077778bdd48e61d0dde5cd247e",
"catalog_sha256": "0551d5afb6a5628e592ccf0fce8a7218a55fd9d4e02342928d69bb9cdbf7daa3",
"operations": {
"agent.list": {
"class": "read"
Expand Down
2 changes: 1 addition & 1 deletion cmux-tui/bindings/typescript/.cmux-resource-api.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"catalog_sha256": "b7f15e13d00bdf49127444f640663bdb08c500077778bdd48e61d0dde5cd247e",
"catalog_sha256": "0551d5afb6a5628e592ccf0fce8a7218a55fd9d4e02342928d69bb9cdbf7daa3",
"operations": {
"agent.list": {
"class": "read"
Expand Down
2 changes: 1 addition & 1 deletion cmux-tui/bindings/zig/.cmux-resource-api.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"catalog_sha256": "b7f15e13d00bdf49127444f640663bdb08c500077778bdd48e61d0dde5cd247e",
"catalog_sha256": "0551d5afb6a5628e592ccf0fce8a7218a55fd9d4e02342928d69bb9cdbf7daa3",
"operations": {
"agent.list": {
"class": "read"
Expand Down
1 change: 1 addition & 0 deletions cmux-tui/crates/cmux-tui-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ pub mod sizing_policy;
mod state;
mod stream_interrupt;
mod surface;
mod terminal_activity;
mod terminal_metadata;
mod workspace_registry;

Expand Down
15 changes: 13 additions & 2 deletions cmux-tui/crates/cmux-tui-core/src/resource_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,8 +448,19 @@ pub(crate) fn public_terminal_snapshot(
if let Some(cwd) = surface.and_then(crate::Surface::presented_directory) {
terminal["cwd"] = json!(cwd);
}
if let Some(progress) = surface.and_then(crate::Surface::terminal_progress) {
terminal["extra"] = json!({"progress": progress.to_json()});
// Activity facts (`terminal-activity-v1`) describe a live terminal; an
// exited one publishes neither.
if !matches!(durable.lifecycle, TerminalLifecycle::Exited | TerminalLifecycle::Tombstoned) {
let mut extra = Map::new();
if let Some(progress) = surface.and_then(crate::Surface::terminal_progress) {
extra.insert("progress".into(), progress.to_json());
}
if let Some(since) = surface.and_then(crate::Surface::terminal_busy_since_ms) {
extra.insert("busy".into(), crate::terminal_activity::busy_json(since));
}
if !extra.is_empty() {
terminal["extra"] = Value::Object(extra);
}
}
if durable.lifecycle == TerminalLifecycle::Exited {
terminal["exit"] =
Expand Down
80 changes: 8 additions & 72 deletions cmux-tui/crates/cmux-tui-core/src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ pub use loopback_forward::{
AuditReporter as LoopbackAuditReporter, LOOPBACK_FORWARD_CAPABILITY, LoopbackForwardPolicy,
};
mod bookmarks;
mod capabilities;
use capabilities::advertised_capabilities;
mod browser_profiles;
mod launch_snapshot;
mod personal;
Expand Down Expand Up @@ -363,78 +365,6 @@ fn machine_listening_tcp_json() -> anyhow::Result<Value> {
}
}

fn advertised_capabilities(bounded_clear_history_fallback_writes: bool) -> Vec<&'static str> {
let mut capabilities = vec![
ATTACH_INITIAL_SIZE_CAPABILITY,
"attach-identity-v1",
WORKSPACE_REGISTRY_CAPABILITY,
DAEMON_HANDOFF_FORCE_CAPABILITY,
GUARDED_BROWSER_POINTER_CAPABILITY,
VIEWPORT_SPLITS_CAPABILITY,
VIEWPORT_COLUMN_RESIZE_CAPABILITY,
STICKY_COLUMNS_CAPABILITY,
LAYOUT_UNDO_CAPABILITY,
TAB_WORKSPACE_MOVE_CAPABILITY,
CLEAR_HISTORY_CAPABILITY,
TERMINAL_COMMAND_JOURNAL_CAPABILITY,
SURFACE_SUBSCRIBE_FILTER_CAPABILITY,
SESSION_JOURNAL_CAPABILITY,
FRONTEND_JOURNAL_CAPABILITY,
VIEW_ATTACHMENT_LEASE_CAPABILITY,
VIEW_ATTACHMENT_DETACH_CAPABILITY,
SHARED_SIZING_CAPABILITY,
SIZING_VIEW_DETACH_CAPABILITY,
TERMINAL_COLOR_OVERRIDES_CAPABILITY,
TERMINAL_PENDING_SEQUENCE_CAPABILITY,
CREATION_RECEIPTS_CAPABILITY,
CREATION_ATTEMPT_KEYS_CAPABILITY,
CREATION_SELECTOR_FALLBACKS_CAPABILITY,
PROVIDER_MANAGED_WORKSPACE_GUARD_CAPABILITY,
BROWSER_PROVIDER_CAPABILITY,
CLIENT_FOCUS_CAPABILITY,
MACHINE_USAGE_CAPABILITY,
MACHINE_LISTENING_TCP_CAPABILITY,
SERVER_STATS_CAPABILITY,
TERMINAL_IDLE_CLOSE_CAPABILITY,
TERMINAL_REAP_CAPABILITY,
END_TERMINALS_KEEP_LAYOUT_CAPABILITY,
BATCH_CLOSE_CAPABILITY,
TERMINAL_RESOURCES_CAPABILITY,
TERMINAL_PLACEMENT_ENV_CAPABILITY,
WORKSPACE_GROUPS_CAPABILITY,
WORKSPACE_METADATA_CAPABILITY,
WORKSPACE_PIN_CAPABILITY,
NOTIFICATION_MARK_UNREAD_CAPABILITY,
TAB_METADATA_CAPABILITY,
FRONTEND_BROWSER_TABS_CAPABILITY,
TAB_DRAG_CAPABILITY,
NOTIFICATION_ACK_CAPABILITY,
TAB_GROUPS_CAPABILITY,
SAVED_TAB_GROUPS_CAPABILITY,
TERMINAL_ENV_CAPABILITY,
LOOPBACK_FORWARD_CAPABILITY,
SESSION_IDENTITY_CAPABILITY,
PROFILES_CAPABILITY,
PERSONAL_TERMINALS_CAPABILITY,
BROWSER_PROFILES_CAPABILITY,
BOOKMARKS_CAPABILITY,
SCREEN_METADATA_CAPABILITY,
SCREEN_GROUPS_CAPABILITY,
NOTIFICATION_SOURCE_CAPABILITY,
TERMINAL_SHELL_ARGS_CAPABILITY,
LAUNCH_SNAPSHOT_CAPABILITY,
STATE_RESOURCES_CAPABILITY,
WINDOW_RECORDS_CAPABILITY,
FRONTEND_BROWSER_OWNER_CAPABILITY,
];
if bounded_clear_history_fallback_writes {
capabilities.push(CLEAR_HISTORY_KEY_CAPABILITY);
}
#[cfg(any(target_os = "linux", target_os = "android", target_vendor = "apple"))]
capabilities.push(crate::image_paste::CAPABILITY);
capabilities
}

macro_rules! protocol_keys {
($($variant:ident => $constant:ident),+ $(,)?) => {
#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
Expand Down Expand Up @@ -7992,6 +7922,12 @@ fn handle_resource_connection_message(
!crate::resource_router::requires_connection_context(request.envelope.operation),
"connection-owned operation fell through to the transport-independent router"
);
// A process may own a status entry only over a trusted local connection.
if crate::state::status_owners::names_process_owner(&request)
&& let Err(error) = trusted_local_resource_client(mux, client, operation)
{
return send_resource_response(writer, id, operation, Err(error));
}
match crate::resource_router::handle_parsed_resource_request(mux, request) {
Ok(response) => writer.send_control(&response).is_ok(),
Err(error) => {
Expand Down
78 changes: 78 additions & 0 deletions cmux-tui/crates/cmux-tui-core/src/server/capabilities.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
//! The capabilities this daemon advertises in `identify`.

use super::*;

pub(super) fn advertised_capabilities(
bounded_clear_history_fallback_writes: bool,
) -> Vec<&'static str> {
let mut capabilities = vec![
ATTACH_INITIAL_SIZE_CAPABILITY,
"attach-identity-v1",
WORKSPACE_REGISTRY_CAPABILITY,
DAEMON_HANDOFF_FORCE_CAPABILITY,
GUARDED_BROWSER_POINTER_CAPABILITY,
VIEWPORT_SPLITS_CAPABILITY,
VIEWPORT_COLUMN_RESIZE_CAPABILITY,
STICKY_COLUMNS_CAPABILITY,
LAYOUT_UNDO_CAPABILITY,
TAB_WORKSPACE_MOVE_CAPABILITY,
CLEAR_HISTORY_CAPABILITY,
TERMINAL_COMMAND_JOURNAL_CAPABILITY,
SURFACE_SUBSCRIBE_FILTER_CAPABILITY,
SESSION_JOURNAL_CAPABILITY,
FRONTEND_JOURNAL_CAPABILITY,
VIEW_ATTACHMENT_LEASE_CAPABILITY,
VIEW_ATTACHMENT_DETACH_CAPABILITY,
SHARED_SIZING_CAPABILITY,
SIZING_VIEW_DETACH_CAPABILITY,
TERMINAL_COLOR_OVERRIDES_CAPABILITY,
TERMINAL_PENDING_SEQUENCE_CAPABILITY,
CREATION_RECEIPTS_CAPABILITY,
CREATION_ATTEMPT_KEYS_CAPABILITY,
CREATION_SELECTOR_FALLBACKS_CAPABILITY,
PROVIDER_MANAGED_WORKSPACE_GUARD_CAPABILITY,
BROWSER_PROVIDER_CAPABILITY,
CLIENT_FOCUS_CAPABILITY,
MACHINE_USAGE_CAPABILITY,
MACHINE_LISTENING_TCP_CAPABILITY,
SERVER_STATS_CAPABILITY,
TERMINAL_IDLE_CLOSE_CAPABILITY,
TERMINAL_REAP_CAPABILITY,
END_TERMINALS_KEEP_LAYOUT_CAPABILITY,
BATCH_CLOSE_CAPABILITY,
TERMINAL_RESOURCES_CAPABILITY,
TERMINAL_PLACEMENT_ENV_CAPABILITY,
WORKSPACE_GROUPS_CAPABILITY,
WORKSPACE_METADATA_CAPABILITY,
WORKSPACE_PIN_CAPABILITY,
NOTIFICATION_MARK_UNREAD_CAPABILITY,
TAB_METADATA_CAPABILITY,
FRONTEND_BROWSER_TABS_CAPABILITY,
TAB_DRAG_CAPABILITY,
NOTIFICATION_ACK_CAPABILITY,
TAB_GROUPS_CAPABILITY,
SAVED_TAB_GROUPS_CAPABILITY,
TERMINAL_ENV_CAPABILITY,
LOOPBACK_FORWARD_CAPABILITY,
SESSION_IDENTITY_CAPABILITY,
PROFILES_CAPABILITY,
PERSONAL_TERMINALS_CAPABILITY,
BROWSER_PROFILES_CAPABILITY,
BOOKMARKS_CAPABILITY,
SCREEN_METADATA_CAPABILITY,
SCREEN_GROUPS_CAPABILITY,
NOTIFICATION_SOURCE_CAPABILITY,
TERMINAL_SHELL_ARGS_CAPABILITY,
LAUNCH_SNAPSHOT_CAPABILITY,
STATE_RESOURCES_CAPABILITY,
WINDOW_RECORDS_CAPABILITY,
FRONTEND_BROWSER_OWNER_CAPABILITY,
crate::terminal_activity::CAPABILITY,
];
if bounded_clear_history_fallback_writes {
capabilities.push(CLEAR_HISTORY_KEY_CAPABILITY);
}
#[cfg(any(target_os = "linux", target_os = "android", target_vendor = "apple"))]
capabilities.push(crate::image_paste::CAPABILITY);
capabilities
}
2 changes: 2 additions & 0 deletions cmux-tui/crates/cmux-tui-core/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ mod prelude;
pub(crate) mod router;
pub(crate) mod screen_state_store;
pub(crate) mod screens;
pub(crate) mod status_meta;
pub(crate) mod status_owners;
pub(crate) mod store;
pub(crate) mod tab_state_store;
pub(crate) mod tabs;
Expand Down
22 changes: 22 additions & 0 deletions cmux-tui/crates/cmux-tui-core/src/state/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ pub(crate) fn handles(operation: ResourceOperation) -> bool {
)
}

/// The loading-indicator fields of `workspace_status.set`. Local trust for
/// a process owner is checked by the transport, which alone knows the
/// connection; whether it runs, inside the commit (after the replay check).
fn status_set_meta(
fields: &Map<String, Value>,
) -> Result<crate::state::status_meta::StatusMeta, ResourceError> {
crate::state::status_meta::StatusMeta::from_fields(fields).map_err(state_error)
}

/// Map a state failure to its typed protocol error. Registry validation
/// failures are `bad request: ...`.
fn state_error(error: anyhow::Error) -> ResourceError {
Expand Down Expand Up @@ -500,12 +509,17 @@ pub(crate) fn dispatch(
| Op::WorkspaceProgressClear
| Op::WorkspaceLogAppend
| Op::WorkspaceLogClear => {
let meta = match operation {
Op::WorkspaceStatusSet => Some(status_set_meta(fields)?),
_ => None,
};
let change = match operation {
Op::WorkspaceStatusSet => WorkspaceStatusChange::Set {
key: string(fields, "key").unwrap_or_default(),
text: string(fields, "text").unwrap_or_default(),
icon: string(fields, "icon"),
color: string(fields, "color"),
meta: Box::new(meta.clone().unwrap_or_default()),
},
Op::WorkspaceStatusClear => {
WorkspaceStatusChange::Clear { key: string(fields, "key") }
Expand All @@ -531,6 +545,14 @@ pub(crate) fn dispatch(
change,
)
.map_err(state_error)?;
if let Some(meta) = meta {
crate::state::status_owners::watch_owners(
mux,
meta.owner_terminal.as_deref(),
meta.owner_pid,
meta.ttl_ms.is_some(),
);
}
state_result(mux, commit)
}
operation => Err(ResourceError::operation_failed(
Expand Down
Loading
Loading