aboutsummaryrefslogtreecommitdiff
path: root/src/api
diff options
context:
space:
mode:
Diffstat (limited to 'src/api')
-rw-r--r--src/api/mod.rs7
-rw-r--r--src/api/schema.rs22
-rw-r--r--src/api/schema/commands.rs16
-rw-r--r--src/api/schema/common.rs19
-rw-r--r--src/api/schema/integrations.rs17
-rw-r--r--src/api/schema/panes.rs80
-rw-r--r--src/api/schema/response.rs29
-rw-r--r--src/api/schema/tests.rs75
-rw-r--r--src/api/schema/workspaces.rs3
-rw-r--r--src/api/server.rs18
10 files changed, 268 insertions, 18 deletions
diff --git a/src/api/mod.rs b/src/api/mod.rs
index b4a5d262..363d7702 100644
--- a/src/api/mod.rs
+++ b/src/api/mod.rs
@@ -8,7 +8,7 @@ mod wait;
pub use event_hub::EventHub;
pub(crate) use server::start_server_with_stop_control;
-pub use server::{start_server_with_capabilities, ServerHandle};
+pub use server::ServerHandle;
pub use status::{read_runtime_status_at, RuntimeStatus};
use std::path::PathBuf;
@@ -25,6 +25,9 @@ pub(crate) fn request_changes_ui(request: &Request) -> bool {
Method::ServerReloadConfig(_)
| Method::ServerReloadAgentManifests(_)
| Method::NotificationShow(_)
+ | Method::ProductAnnouncementDismiss(_)
+ | Method::ReleaseNotesDismiss(_)
+ | Method::CommandInvoke(_)
| Method::WorkspaceCreate(_)
| Method::WorkspaceFocus(_)
| Method::WorkspaceRename(_)
@@ -55,6 +58,8 @@ pub(crate) fn request_changes_ui(request: &Request) -> bool {
| Method::PaneZoom(_)
| Method::PaneFocusDirection(_)
| Method::PaneResize(_)
+ | Method::PaneScroll(_)
+ | Method::PaneEditScrollback(_)
| Method::PaneFocus(_)
| Method::PaneInputSet(_)
| Method::PaneRename(_)
diff --git a/src/api/schema.rs b/src/api/schema.rs
index e06b701c..074cd4ed 100644
--- a/src/api/schema.rs
+++ b/src/api/schema.rs
@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
pub mod agents;
+pub mod commands;
pub mod common;
pub mod events;
pub mod integrations;
@@ -14,6 +15,7 @@ pub mod workspaces;
pub mod worktrees;
pub use agents::*;
+pub use commands::*;
pub use common::*;
pub use events::*;
pub use integrations::*;
@@ -57,6 +59,12 @@ pub enum Method {
ServerReloadAgentManifests(EmptyParams),
#[serde(rename = "notification.show")]
NotificationShow(NotificationShowParams),
+ #[serde(rename = "product_announcement.dismiss")]
+ ProductAnnouncementDismiss(ProductAnnouncementDismissParams),
+ #[serde(rename = "release_notes.dismiss")]
+ ReleaseNotesDismiss(ReleaseNotesDismissParams),
+ #[serde(rename = "command.invoke")]
+ CommandInvoke(CommandInvokeParams),
#[serde(rename = "client.window_title.set")]
ClientWindowTitleSet(ClientWindowTitleSetParams),
#[serde(rename = "client.window_title.clear")]
@@ -153,6 +161,16 @@ pub enum Method {
PaneFocusDirection(PaneFocusDirectionParams),
#[serde(rename = "pane.resize")]
PaneResize(PaneResizeParams),
+ #[serde(rename = "pane.scroll")]
+ PaneScroll(PaneScrollParams),
+ #[serde(rename = "pane.edit_scrollback")]
+ PaneEditScrollback(PaneTarget),
+ #[serde(rename = "pane.selection.read")]
+ PaneSelectionRead(PaneSelectionReadParams),
+ #[serde(rename = "pane.copy_motion")]
+ PaneCopyMotion(PaneCopyMotionParams),
+ #[serde(rename = "pane.copy_search")]
+ PaneCopySearch(PaneCopySearchParams),
#[serde(rename = "pane.list")]
PaneList(PaneListParams),
#[serde(rename = "pane.current")]
@@ -163,6 +181,8 @@ pub enum Method {
PaneFocus(PaneTarget),
#[serde(rename = "pane.input.set")]
PaneInputSet(PaneInputSetParams),
+ #[serde(rename = "pane.link.activate")]
+ PaneLinkActivate(PaneLinkActivateParams),
#[serde(rename = "pane.rename")]
PaneRename(PaneRenameParams),
#[serde(rename = "pane.send_text")]
@@ -214,6 +234,8 @@ pub enum Method {
EventsWait(EventsWaitParams),
#[serde(rename = "pane.wait_for_output")]
PaneWaitForOutput(PaneWaitForOutputParams),
+ #[serde(rename = "integration.list")]
+ IntegrationList(EmptyParams),
#[serde(rename = "integration.install")]
IntegrationInstall(IntegrationInstallParams),
#[serde(rename = "integration.uninstall")]
diff --git a/src/api/schema/commands.rs b/src/api/schema/commands.rs
new file mode 100644
index 00000000..c9ccd4bc
--- /dev/null
+++ b/src/api/schema/commands.rs
@@ -0,0 +1,16 @@
+use serde::{Deserialize, Serialize};
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct CommandInvokeParams {
+ /// Opaque endpoint-issued command identifier from the client-shell projection.
+ pub command_id: String,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub workspace_id: Option<String>,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub tab_id: Option<String>,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub pane_id: Option<String>,
+ /// Client-owned selection coordinates, validated against the pane's content revision.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub selection: Option<super::PaneSelectionReadParams>,
+}
diff --git a/src/api/schema/common.rs b/src/api/schema/common.rs
index 7313002c..15f56e6c 100644
--- a/src/api/schema/common.rs
+++ b/src/api/schema/common.rs
@@ -36,6 +36,17 @@ pub struct PaneTarget {
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct ProductAnnouncementDismissParams {
+ pub version: String,
+ pub id: String,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct ReleaseNotesDismissParams {
+ pub version: String,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct TabTarget {
pub tab_id: String,
}
@@ -109,14 +120,6 @@ impl NotificationShowSound {
pub fn is_none(&self) -> bool {
matches!(self, Self::None)
}
-
- pub fn to_sound(self) -> Option<crate::sound::Sound> {
- match self {
- Self::None => None,
- Self::Done => Some(crate::sound::Sound::Done),
- Self::Request => Some(crate::sound::Sound::Request),
- }
- }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
diff --git a/src/api/schema/integrations.rs b/src/api/schema/integrations.rs
index 6151dedb..0fce2913 100644
--- a/src/api/schema/integrations.rs
+++ b/src/api/schema/integrations.rs
@@ -1,5 +1,22 @@
use serde::{Deserialize, Serialize};
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum IntegrationState {
+ NotInstalled,
+ Current,
+ Outdated,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct IntegrationInfo {
+ pub target: IntegrationTarget,
+ pub label: String,
+ pub command: String,
+ pub available: bool,
+ pub state: IntegrationState,
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct IntegrationInstallParams {
pub target: IntegrationTarget,
diff --git a/src/api/schema/panes.rs b/src/api/schema/panes.rs
index 6435a422..517870f7 100644
--- a/src/api/schema/panes.rs
+++ b/src/api/schema/panes.rs
@@ -48,6 +48,17 @@ pub struct PaneInputSetParams {
pub right_click: PaneRightClickTarget,
}
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct PaneLinkActivateParams {
+ pub pane_id: String,
+ pub viewport_row: u16,
+ pub col: u16,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub content_revision: Option<u64>,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub offset_from_bottom: Option<u64>,
+}
+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum PaneDirection {
@@ -231,6 +242,75 @@ pub struct PaneResizeParams {
pub amount: Option<f32>,
}
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct PaneScrollParams {
+ pub pane_id: String,
+ pub offset_from_bottom: u64,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct PaneTextPoint {
+ pub row: u32,
+ pub col: u16,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct PaneSelectionReadParams {
+ pub pane_id: String,
+ pub anchor: PaneTextPoint,
+ pub cursor: PaneTextPoint,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub content_revision: Option<u64>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum PaneCopyMotion {
+ LineEnd,
+ FirstNonBlank,
+ NextWordStart,
+ PreviousWordStart,
+ NextWordEnd,
+ NextBigWordStart,
+ PreviousBigWordStart,
+ NextBigWordEnd,
+ PreviousParagraph,
+ NextParagraph,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct PaneCopyMotionParams {
+ pub pane_id: String,
+ pub cursor: PaneTextPoint,
+ pub motion: PaneCopyMotion,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub content_revision: Option<u64>,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+#[serde(rename_all = "snake_case")]
+pub enum PaneCopySearchDirection {
+ Forward,
+ Backward,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct PaneTextRange {
+ pub start: PaneTextPoint,
+ pub end: PaneTextPoint,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
+pub struct PaneCopySearchParams {
+ pub pane_id: String,
+ pub query: String,
+ pub direction: PaneCopySearchDirection,
+ pub cursor: PaneTextPoint,
+ pub content_revision: u64,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub previous: Option<PaneTextRange>,
+}
+
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema, Default)]
pub struct PaneListParams {
#[serde(default, skip_serializing_if = "Option::is_none")]
diff --git a/src/api/schema/response.rs b/src/api/schema/response.rs
index da2813a6..180d6645 100644
--- a/src/api/schema/response.rs
+++ b/src/api/schema/response.rs
@@ -9,7 +9,7 @@ use super::integrations::{
use super::panes::{
LayoutDescription, PaneEdgesResult, PaneFocusDirectionResult, PaneInfo, PaneLayoutSnapshot,
PaneMoveResult, PaneNeighborResult, PaneProcessInfo, PaneReadResult, PaneResizeResult,
- PaneSwapResult, PaneZoomResult,
+ PaneSwapResult, PaneTextPoint, PaneTextRange, PaneZoomResult,
};
use super::plugins::{
InstalledPluginInfo, PluginActionInfo, PluginCommandLogInfo, PluginInvocationContext,
@@ -162,6 +162,25 @@ pub enum ResponseResult {
PaneRead {
read: PaneReadResult,
},
+ PaneSelection {
+ pane_id: String,
+ text: String,
+ },
+ PaneCopyMotion {
+ pane_id: String,
+ cursor: PaneTextPoint,
+ content_revision: u64,
+ },
+ PaneCopySearch {
+ pane_id: String,
+ content_revision: u64,
+ matches: Vec<PaneTextRange>,
+ total: u64,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ current: Option<u32>,
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ current_global: Option<u64>,
+ },
PaneGraphicsFrameAck {
sequence: u64,
revision: u64,
@@ -210,6 +229,9 @@ pub enum ResponseResult {
changed: bool,
reason: ClientWindowTitleReason,
},
+ IntegrationList {
+ integrations: Vec<super::integrations::IntegrationInfo>,
+ },
IntegrationInstall {
target: IntegrationTarget,
details: IntegrationInstallResult,
@@ -252,6 +274,11 @@ pub enum ResponseResult {
context: PluginInvocationContext,
log: PluginCommandLogInfo,
},
+ PaneLinkActivated {
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ url: Option<String>,
+ handled: bool,
+ },
PluginLogList {
logs: Vec<PluginCommandLogInfo>,
},
diff --git a/src/api/schema/tests.rs b/src/api/schema/tests.rs
index 663f3fc4..e4a7f2a3 100644
--- a/src/api/schema/tests.rs
+++ b/src/api/schema/tests.rs
@@ -50,6 +50,7 @@ fn request_uses_dot_method_names() {
let request = Request {
id: "req_1".into(),
method: Method::WorkspaceCreate(WorkspaceCreateParams {
+ source_workspace_id: None,
cwd: Some("/tmp".into()),
focus: true,
label: Some("api".into()),
@@ -273,6 +274,54 @@ fn request_round_trips_for_agent_explain() {
}
#[test]
+fn integration_list_request_and_response_round_trip() {
+ let request = Request {
+ id: "req_integrations".into(),
+ method: Method::IntegrationList(EmptyParams::default()),
+ };
+ let json = serde_json::to_value(&request).unwrap();
+ assert_eq!(json["method"], "integration.list");
+ assert_eq!(serde_json::from_value::<Request>(json).unwrap(), request);
+
+ let response = SuccessResponse {
+ id: "req_integrations".into(),
+ result: ResponseResult::IntegrationList {
+ integrations: vec![IntegrationInfo {
+ target: IntegrationTarget::Codex,
+ label: "codex".into(),
+ command: "codex".into(),
+ available: true,
+ state: IntegrationState::Outdated,
+ }],
+ },
+ };
+ let json = serde_json::to_value(&response).unwrap();
+ assert_eq!(json["result"]["type"], "integration_list");
+ assert_eq!(json["result"]["integrations"][0]["state"], "outdated");
+ assert_eq!(
+ serde_json::from_value::<SuccessResponse>(json).unwrap(),
+ response
+ );
+}
+
+#[test]
+fn command_invoke_request_round_trips_without_command_text() {
+ let request = Request {
+ id: "req_command".into(),
+ method: Method::CommandInvoke(CommandInvokeParams {
+ command_id: "cmd_0123456789abcdef0123456789abcdef".into(),
+ workspace_id: Some("w1".into()),
+ tab_id: Some("w1:t1".into()),
+ pane_id: Some("w1:p1".into()),
+ selection: None,
+ }),
+ };
+ let json = serde_json::to_value(&request).unwrap();
+ assert_eq!(json["method"], "command.invoke");
+ assert_eq!(serde_json::from_value::<Request>(json).unwrap(), request);
+}
+
+#[test]
fn notification_show_request_parses() {
let json = r#"{"id":"req_1","method":"notification.show","params":{"title":"build failed","body":"api workspace","position":"top-left","sound":"request"}}"#;
let request: Request = serde_json::from_str(json).unwrap();
@@ -1264,6 +1313,32 @@ fn event_wait_parses_typed_match() {
}
#[test]
+fn pane_link_activate_round_trips() {
+ let request = Request {
+ id: "req_pane_link".into(),
+ method: Method::PaneLinkActivate(PaneLinkActivateParams {
+ pane_id: "w1:p1".into(),
+ viewport_row: 3,
+ col: 7,
+ content_revision: Some(42),
+ offset_from_bottom: Some(5),
+ }),
+ };
+ let json = serde_json::to_value(&request).unwrap();
+ assert_eq!(json["method"], "pane.link.activate");
+ let restored: Request = serde_json::from_value(json).unwrap();
+ assert_eq!(restored, request);
+
+ let response = ResponseResult::PaneLinkActivated {
+ url: Some("https://example.test".into()),
+ handled: false,
+ };
+ let json = serde_json::to_string(&response).unwrap();
+ let restored: ResponseResult = serde_json::from_str(&json).unwrap();
+ assert_eq!(restored, response);
+}
+
+#[test]
fn plugin_action_list_and_invoke_round_trips() {
let list = Request {
id: "req_plugin_action_list".into(),
diff --git a/src/api/schema/workspaces.rs b/src/api/schema/workspaces.rs
index bfc63252..1b7132c4 100644
--- a/src/api/schema/workspaces.rs
+++ b/src/api/schema/workspaces.rs
@@ -6,6 +6,9 @@ use super::common::AgentStatus;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct WorkspaceCreateParams {
+ /// Workspace whose focused pane supplies the `follow` cwd policy.
+ #[serde(default, skip_serializing_if = "Option::is_none")]
+ pub source_workspace_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub cwd: Option<String>,
#[serde(default)]
diff --git a/src/api/server.rs b/src/api/server.rs
index 3c42d699..ff0b6fdf 100644
--- a/src/api/server.rs
+++ b/src/api/server.rs
@@ -64,14 +64,6 @@ pub(crate) fn start_server_with_stop_control(
start_server_inner(api_tx, event_hub, default_capabilities(), Some(server_stop))
}
-pub fn start_server_with_capabilities(
- api_tx: ApiRequestSender,
- event_hub: EventHub,
- capabilities: Option<ServerCapabilities>,
-) -> std::io::Result<ServerHandle> {
- start_server_inner(api_tx, event_hub, capabilities, None)
-}
-
fn default_capabilities() -> Option<ServerCapabilities> {
Some(ServerCapabilities {
live_handoff: crate::platform::capabilities().live_handoff,
@@ -388,6 +380,9 @@ fn api_method_name(method: &Method) -> &'static str {
Method::ServerAgentManifests(_) => "server.agent_manifests",
Method::ServerReloadAgentManifests(_) => "server.reload_agent_manifests",
Method::NotificationShow(_) => "notification.show",
+ Method::ProductAnnouncementDismiss(_) => "product_announcement.dismiss",
+ Method::ReleaseNotesDismiss(_) => "release_notes.dismiss",
+ Method::CommandInvoke(_) => "command.invoke",
Method::ClientWindowTitleSet(_) => "client.window_title.set",
Method::ClientWindowTitleClear(_) => "client.window_title.clear",
Method::SessionSnapshot(_) => "session.snapshot",
@@ -436,11 +431,17 @@ fn api_method_name(method: &Method) -> &'static str {
Method::PaneEdges(_) => "pane.edges",
Method::PaneFocusDirection(_) => "pane.focus_direction",
Method::PaneResize(_) => "pane.resize",
+ Method::PaneScroll(_) => "pane.scroll",
+ Method::PaneEditScrollback(_) => "pane.edit_scrollback",
+ Method::PaneSelectionRead(_) => "pane.selection.read",
+ Method::PaneCopyMotion(_) => "pane.copy_motion",
+ Method::PaneCopySearch(_) => "pane.copy_search",
Method::PaneList(_) => "pane.list",
Method::PaneCurrent(_) => "pane.current",
Method::PaneGet(_) => "pane.get",
Method::PaneFocus(_) => "pane.focus",
Method::PaneInputSet(_) => "pane.input.set",
+ Method::PaneLinkActivate(_) => "pane.link.activate",
Method::PaneRename(_) => "pane.rename",
Method::PaneSendText(_) => "pane.send_text",
Method::PaneSendKeys(_) => "pane.send_keys",
@@ -464,6 +465,7 @@ fn api_method_name(method: &Method) -> &'static str {
Method::EventsSubscribe(_) => "events.subscribe",
Method::EventsWait(_) => "events.wait",
Method::PaneWaitForOutput(_) => "pane.wait_for_output",
+ Method::IntegrationList(_) => "integration.list",
Method::IntegrationInstall(_) => "integration.install",
Method::IntegrationUninstall(_) => "integration.uninstall",
Method::PluginLink(_) => "plugin.link",