Skip to main content

rmux_proto/
request.rs

1//! Detached request contracts.
2
3use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6use crate::{
7    ControlModeRequest, HandshakeRequest, PaneTarget, PaneTargetRef, SdkWaitId, SdkWaitOwnerId,
8    SessionName, Target, WindowTarget,
9};
10
11#[path = "request/compat.rs"]
12mod compat;
13
14#[path = "request/display.rs"]
15mod display;
16pub use display::{DisplayMessageExtRequest, DisplayMessageRequest};
17
18#[path = "request/show.rs"]
19mod show;
20pub use show::ShowHooksRequest;
21pub use show::{ShowEnvironmentRequest, ShowOptionsRequest};
22
23#[path = "request/layout.rs"]
24mod layout;
25pub use layout::{
26    NextLayoutRequest, PreviousLayoutRequest, SelectCustomLayoutRequest, SelectLayoutRequest,
27    SelectLayoutTarget, SelectOldLayoutRequest, SpreadLayoutRequest,
28};
29
30#[path = "request/pane.rs"]
31mod pane;
32pub use pane::{
33    BreakPaneRequest, DisplayPanesRequest, JoinPaneRequest, KillPaneRequest, LastPaneRequest,
34    MovePaneRequest, PaneBroadcastInputRequest, PaneInputRequest, PaneKillRequest,
35    PaneOutputCursorRequest, PaneOutputSubscriptionStart, PaneResizeRequest, PaneRespawnRequest,
36    PaneSelectRequest, PaneSnapshotRefRequest, PaneSnapshotRequest, PaneSplitSize, PipePaneRequest,
37    ResizePaneRequest, RespawnPaneRequest, SelectPaneAdjacentRequest, SelectPaneDirection,
38    SelectPaneMarkRequest, SelectPaneRequest, SendKeysExt2Request, SendKeysExtRequest,
39    SendKeysRequest, SplitWindowExtRequest, SplitWindowRequest, SplitWindowTarget,
40    SubscribePaneOutputRefRequest, SubscribePaneOutputRequest, SwapPaneDirection, SwapPaneRequest,
41    UnsubscribePaneOutputRequest,
42};
43
44#[path = "request/window.rs"]
45mod window;
46pub use window::{
47    KillWindowRequest, LastWindowRequest, LinkWindowRequest, ListWindowsRequest, MoveWindowRequest,
48    MoveWindowTarget, NewWindowRequest, NextWindowRequest, PreviousWindowRequest,
49    RenameWindowRequest, ResizeWindowAdjustment, ResizeWindowRequest, RespawnWindowRequest,
50    RotateWindowDirection, RotateWindowRequest, SelectWindowRequest, SwapWindowRequest,
51};
52
53#[path = "request/target.rs"]
54mod target;
55pub use target::{ResolveTargetRequest, ResolveTargetType};
56
57#[path = "request/session.rs"]
58mod session;
59pub use session::{
60    CreateSessionLeaseRequest, HasSessionRequest, KillSessionRequest, ListSessionsRequest,
61    NewSessionExtRequest, NewSessionRequest, ReleaseSessionLeaseRequest, RenameSessionRequest,
62    RenewSessionLeaseRequest,
63};
64
65#[path = "request/server.rs"]
66mod server;
67pub use server::{
68    DaemonStatusRequest, KillServerRequest, LockClientRequest, LockServerRequest,
69    LockSessionRequest, ServerAccessRequest, ShutdownIfIdleRequest,
70};
71
72#[path = "request/client.rs"]
73mod client;
74pub use client::{
75    AttachSessionExt2Request, AttachSessionExt3Request, AttachSessionExtRequest,
76    AttachSessionRequest, DetachClientExtRequest, DetachClientRequest, ListClientsRequest,
77    RefreshClientRequest, SuspendClientRequest, SwitchClientExt2Request, SwitchClientExt3Request,
78    SwitchClientExtRequest, SwitchClientRequest,
79};
80
81#[path = "request/keys.rs"]
82mod keys;
83pub use keys::{
84    BindKeyRequest, ClockModeRequest, CopyModeRequest, ListKeysRequest, SendPrefixRequest,
85    UnbindKeyRequest,
86};
87
88#[path = "request/options.rs"]
89mod options;
90pub use options::{
91    SetEnvironmentMode, SetEnvironmentRequest, SetHookMutationRequest, SetHookRequest,
92    SetOptionByNameRequest, SetOptionRequest,
93};
94
95#[path = "request/buffer.rs"]
96mod buffer;
97pub use buffer::{
98    CapturePaneRequest, ClearHistoryRequest, DeleteBufferRequest, ListBuffersRequest,
99    LoadBufferRequest, PasteBufferRequest, SaveBufferRequest, SetBufferRequest, ShowBufferRequest,
100};
101
102#[path = "request/web.rs"]
103mod web;
104pub use web::{
105    CreateWebShareRequest, ListWebSharesRequest, LookupWebShareRequest, StopAllWebSharesRequest,
106    StopWebShareRequest, WebShareConfigRequest, WebShareRequest, WebShareScope, WebShareUrlOptions,
107    WebTerminalPalette, WebTerminalTheme,
108};
109
110/// All detached public command and internal RPC requests supported by the wire
111/// protocol.
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
113pub enum Request {
114    /// `new-session`
115    NewSession(NewSessionRequest),
116    /// `has-session`
117    HasSession(HasSessionRequest),
118    /// `kill-session`
119    KillSession(KillSessionRequest),
120    /// `new-window`
121    NewWindow(NewWindowRequest),
122    /// `kill-window`
123    KillWindow(KillWindowRequest),
124    /// `select-window`
125    SelectWindow(SelectWindowRequest),
126    /// `rename-window`
127    RenameWindow(RenameWindowRequest),
128    /// `next-window`
129    NextWindow(NextWindowRequest),
130    /// `previous-window`
131    PreviousWindow(PreviousWindowRequest),
132    /// `last-window`
133    LastWindow(LastWindowRequest),
134    /// `list-windows`
135    ListWindows(ListWindowsRequest),
136    /// `move-window`
137    MoveWindow(MoveWindowRequest),
138    /// `swap-window`
139    SwapWindow(SwapWindowRequest),
140    /// `rotate-window`
141    RotateWindow(RotateWindowRequest),
142    /// `split-window`
143    SplitWindow(SplitWindowRequest),
144    /// `swap-pane`
145    SwapPane(SwapPaneRequest),
146    /// `last-pane`
147    LastPane(LastPaneRequest),
148    /// `join-pane`
149    JoinPane(JoinPaneRequest),
150    /// `break-pane`
151    BreakPane(BreakPaneRequest),
152    /// `kill-pane`
153    KillPane(KillPaneRequest),
154    /// `select-layout`
155    SelectLayout(SelectLayoutRequest),
156    /// `resize-pane`
157    ResizePane(ResizePaneRequest),
158    /// `display-panes`
159    DisplayPanes(DisplayPanesRequest),
160    /// `select-pane`
161    SelectPane(SelectPaneRequest),
162    /// `select-pane -U/-D/-L/-R`
163    SelectPaneAdjacent(SelectPaneAdjacentRequest),
164    /// `send-keys`
165    SendKeys(SendKeysRequest),
166    /// `attach-session`
167    AttachSession(AttachSessionRequest),
168    /// `switch-client`
169    SwitchClient(SwitchClientRequest),
170    /// `detach-client`
171    DetachClient(DetachClientRequest),
172    /// `set-option`
173    SetOption(SetOptionRequest),
174    /// `set-environment`
175    SetEnvironment(SetEnvironmentRequest),
176    /// `set-hook`
177    SetHook(SetHookRequest),
178    /// `next-layout`
179    NextLayout(NextLayoutRequest),
180    /// `previous-layout`
181    PreviousLayout(PreviousLayoutRequest),
182    /// `show-options`
183    ShowOptions(ShowOptionsRequest),
184    /// `show-environment`
185    ShowEnvironment(ShowEnvironmentRequest),
186    /// `set-buffer`
187    SetBuffer(SetBufferRequest),
188    /// `show-buffer`
189    ShowBuffer(ShowBufferRequest),
190    /// `paste-buffer`
191    PasteBuffer(PasteBufferRequest),
192    /// `list-buffers`
193    ListBuffers(ListBuffersRequest),
194    /// `delete-buffer`
195    DeleteBuffer(DeleteBufferRequest),
196    /// `load-buffer`
197    LoadBuffer(LoadBufferRequest),
198    /// `save-buffer`
199    SaveBuffer(SaveBufferRequest),
200    /// `capture-pane`
201    CapturePane(CapturePaneRequest),
202    /// `display-message`
203    DisplayMessage(DisplayMessageRequest),
204    /// `run-shell`
205    RunShell(RunShellRequest),
206    /// `if-shell`
207    IfShell(IfShellRequest),
208    /// `wait-for`
209    WaitFor(WaitForRequest),
210    /// `rename-session`
211    RenameSession(RenameSessionRequest),
212    /// `list-sessions`
213    ListSessions(ListSessionsRequest),
214    /// `list-panes`
215    ListPanes(ListPanesRequest),
216    /// `source-file`
217    SourceFile(SourceFileRequest),
218    /// `set-option` using an open string-based option name.
219    SetOptionByName(SetOptionByNameRequest),
220    /// Extended `set-hook` mutation semantics.
221    SetHookMutation(SetHookMutationRequest),
222    /// `show-hooks`
223    ShowHooks(ShowHooksRequest),
224    /// Extended `send-keys` semantics including key-table dispatch and format expansion.
225    SendKeysExt(SendKeysExtRequest),
226    /// Extended `switch-client` semantics including `-T key-table`.
227    SwitchClientExt(SwitchClientExtRequest),
228    /// `bind-key`
229    BindKey(BindKeyRequest),
230    /// `unbind-key`
231    UnbindKey(UnbindKeyRequest),
232    /// `list-keys`
233    ListKeys(ListKeysRequest),
234    /// `send-prefix`
235    SendPrefix(SendPrefixRequest),
236    /// `clear-history`
237    ClearHistory(ClearHistoryRequest),
238    /// `copy-mode`
239    CopyMode(CopyModeRequest),
240    /// Internal detached upgrade into tmux-compatible control mode.
241    ControlMode(ControlModeRequest),
242    /// `clock-mode`
243    ClockMode(ClockModeRequest),
244    /// `show-messages`
245    ShowMessages(ShowMessagesRequest),
246    /// Extended `new-session` semantics including grouped sessions and attach-if-exists.
247    NewSessionExt(NewSessionExtRequest),
248    /// Extended `attach-session` semantics including client flags and detach-others.
249    AttachSessionExt(AttachSessionExtRequest),
250    /// Extended `switch-client` semantics including `-l`, `-n`, `-p`, and readonly toggles.
251    SwitchClientExt2(SwitchClientExt2Request),
252    /// `select-layout` with a tmux custom layout string.
253    SelectCustomLayout(SelectCustomLayoutRequest),
254    /// `select-layout -o`
255    SelectOldLayout(SelectOldLayoutRequest),
256    /// `select-layout -E`
257    SpreadLayout(SpreadLayoutRequest),
258    /// `kill-server`
259    KillServer(KillServerRequest),
260    /// `lock-server`
261    LockServer(LockServerRequest),
262    /// `lock-session`
263    LockSession(LockSessionRequest),
264    /// `lock-client`
265    LockClient(LockClientRequest),
266    /// `server-access`
267    ServerAccess(ServerAccessRequest),
268    /// `refresh-client`
269    RefreshClient(RefreshClientRequest),
270    /// `list-clients`
271    ListClients(ListClientsRequest),
272    /// `suspend-client`
273    SuspendClient(SuspendClientRequest),
274    /// Extended `detach-client` semantics including `-a`, `-s`, `-P`, and `-E`.
275    DetachClientExt(DetachClientExtRequest),
276    /// Further-extended `attach-session` semantics including `-c working-directory`.
277    AttachSessionExt2(AttachSessionExt2Request),
278    /// Further-extended `switch-client` semantics including `-c target-client` and `-Z`.
279    SwitchClientExt3(SwitchClientExt3Request),
280    /// `resize-window`
281    ResizeWindow(ResizeWindowRequest),
282    /// `respawn-window`
283    RespawnWindow(RespawnWindowRequest),
284    /// `move-pane`
285    MovePane(MovePaneRequest),
286    /// `pipe-pane`
287    PipePane(PipePaneRequest),
288    /// `respawn-pane`
289    RespawnPane(RespawnPaneRequest),
290    /// `link-window`
291    LinkWindow(LinkWindowRequest),
292    /// `unlink-window`
293    UnlinkWindow(UnlinkWindowRequest),
294    /// `select-pane -m` / `select-pane -M`
295    SelectPaneMark(SelectPaneMarkRequest),
296    /// Internal detached target resolution for tmux-style raw target text.
297    ResolveTarget(ResolveTargetRequest),
298    /// Extended `split-window` semantics including an explicit shell command.
299    SplitWindowExt(SplitWindowExtRequest),
300    /// Internal SDK/daemon version and capability negotiation.
301    Handshake(HandshakeRequest),
302    /// Internal daemon-backed structured pane snapshot endpoint.
303    PaneSnapshot(PaneSnapshotRequest),
304    /// Internal daemon-backed pane output subscription endpoint.
305    SubscribePaneOutput(SubscribePaneOutputRequest),
306    /// Internal daemon-backed pane output unsubscription endpoint.
307    UnsubscribePaneOutput(UnsubscribePaneOutputRequest),
308    /// Internal daemon-backed pane output cursor polling endpoint.
309    PaneOutputCursor(PaneOutputCursorRequest),
310    /// Internal daemon-backed SDK byte wait endpoint.
311    SdkWaitForOutput(SdkWaitForOutputRequest),
312    /// Internal daemon-backed SDK wait cancellation endpoint.
313    CancelSdkWait(CancelSdkWaitRequest),
314    /// SDK pane input endpoint with stable pane-id targeting.
315    PaneInput(PaneInputRequest),
316    /// SDK pane resize endpoint with stable pane-id targeting.
317    PaneResize(PaneResizeRequest),
318    /// SDK pane kill endpoint with stable pane-id targeting.
319    PaneKill(PaneKillRequest),
320    /// SDK pane respawn endpoint with stable pane-id targeting.
321    PaneRespawn(PaneRespawnRequest),
322    /// SDK pane snapshot endpoint with stable pane-id targeting.
323    PaneSnapshotRef(PaneSnapshotRefRequest),
324    /// SDK pane select/title endpoint with stable pane-id targeting.
325    PaneSelect(PaneSelectRequest),
326    /// SDK pane input broadcast endpoint with stable pane-id targeting.
327    PaneBroadcastInput(PaneBroadcastInputRequest),
328    /// SDK app-owned session lease create endpoint.
329    CreateSessionLease(CreateSessionLeaseRequest),
330    /// SDK app-owned session lease renewal endpoint.
331    RenewSessionLease(RenewSessionLeaseRequest),
332    /// SDK app-owned session lease release endpoint.
333    ReleaseSessionLease(ReleaseSessionLeaseRequest),
334    /// Internal daemon-backed pane output subscription endpoint with stable pane-id targeting.
335    SubscribePaneOutputRef(SubscribePaneOutputRefRequest),
336    /// Internal daemon-backed SDK byte wait endpoint with stable pane-id targeting.
337    SdkWaitForOutputRef(SdkWaitForOutputRefRequest),
338    /// Internal daemon version and activity status endpoint.
339    DaemonStatus(DaemonStatusRequest),
340    /// Internal idle-only shutdown endpoint used by seamless upgrades.
341    ShutdownIfIdle(ShutdownIfIdleRequest),
342    /// Browser-visible pane sharing command family.
343    WebShare(WebShareRequest),
344    /// `display-message` extension with target-client context.
345    DisplayMessageExt(DisplayMessageExtRequest),
346    /// `send-keys` extension with target-client context.
347    SendKeysExt2(SendKeysExt2Request),
348    /// Attach-session extension with attach-stream client capabilities.
349    AttachSessionExt3(AttachSessionExt3Request),
350}
351
352impl Request {
353    /// Returns the stable routing name for the request variant.
354    #[must_use]
355    pub const fn command_name(&self) -> &'static str {
356        match self {
357            Self::NewSession(_) => "new-session",
358            Self::HasSession(_) => "has-session",
359            Self::KillSession(_) => "kill-session",
360            Self::NewWindow(_) => "new-window",
361            Self::KillWindow(_) => "kill-window",
362            Self::SelectWindow(_) => "select-window",
363            Self::RenameWindow(_) => "rename-window",
364            Self::NextWindow(_) => "next-window",
365            Self::PreviousWindow(_) => "previous-window",
366            Self::LastWindow(_) => "last-window",
367            Self::ListWindows(_) => "list-windows",
368            Self::LinkWindow(_) => "link-window",
369            Self::MoveWindow(_) => "move-window",
370            Self::SwapWindow(_) => "swap-window",
371            Self::RotateWindow(_) => "rotate-window",
372            Self::SplitWindow(_) | Self::SplitWindowExt(_) => "split-window",
373            Self::SwapPane(_) => "swap-pane",
374            Self::LastPane(_) => "last-pane",
375            Self::JoinPane(_) => "join-pane",
376            Self::BreakPane(_) => "break-pane",
377            Self::KillPane(_) => "kill-pane",
378            Self::SelectLayout(_) => "select-layout",
379            Self::ResizePane(_) => "resize-pane",
380            Self::DisplayPanes(_) => "display-panes",
381            Self::SelectPane(_) | Self::SelectPaneAdjacent(_) | Self::SelectPaneMark(_) => {
382                "select-pane"
383            }
384            Self::SendKeys(_) | Self::SendKeysExt2(_) => "send-keys",
385            Self::AttachSession(_) => "attach-session",
386            Self::SwitchClient(_) => "switch-client",
387            Self::DetachClient(_) => "detach-client",
388            Self::SetOption(_) => "set-option",
389            Self::SetEnvironment(_) => "set-environment",
390            Self::SetHook(_) => "set-hook",
391            Self::NextLayout(_) => "next-layout",
392            Self::PreviousLayout(_) => "previous-layout",
393            Self::ShowOptions(_) => "show-options",
394            Self::ShowEnvironment(_) => "show-environment",
395            Self::SetBuffer(_) => "set-buffer",
396            Self::ShowBuffer(_) => "show-buffer",
397            Self::PasteBuffer(_) => "paste-buffer",
398            Self::ListBuffers(_) => "list-buffers",
399            Self::DeleteBuffer(_) => "delete-buffer",
400            Self::LoadBuffer(_) => "load-buffer",
401            Self::SaveBuffer(_) => "save-buffer",
402            Self::CapturePane(_) => "capture-pane",
403            Self::PaneSnapshot(_) => "pane-snapshot",
404            Self::SubscribePaneOutput(_) | Self::SubscribePaneOutputRef(_) => {
405                "subscribe-pane-output"
406            }
407            Self::UnsubscribePaneOutput(_) => "unsubscribe-pane-output",
408            Self::PaneOutputCursor(_) => "pane-output-cursor",
409            Self::SdkWaitForOutput(_) | Self::SdkWaitForOutputRef(_) => "sdk-wait-output",
410            Self::CancelSdkWait(_) => "cancel-sdk-wait",
411            Self::PaneInput(_) => "send-keys",
412            Self::PaneBroadcastInput(_) => "send-keys",
413            Self::CreateSessionLease(_) => "create-session-lease",
414            Self::RenewSessionLease(_) => "renew-session-lease",
415            Self::ReleaseSessionLease(_) => "release-session-lease",
416            Self::PaneResize(_) => "resize-pane",
417            Self::PaneKill(_) => "kill-pane",
418            Self::PaneRespawn(_) => "respawn-pane",
419            Self::PaneSnapshotRef(_) => "pane-snapshot",
420            Self::PaneSelect(_) => "select-pane",
421            Self::DisplayMessage(_) | Self::DisplayMessageExt(_) => "display-message",
422            Self::ResolveTarget(_) => "resolve-target",
423            Self::RunShell(_) => "run-shell",
424            Self::IfShell(_) => "if-shell",
425            Self::WaitFor(_) => "wait-for",
426            Self::RenameSession(_) => "rename-session",
427            Self::ListSessions(_) => "list-sessions",
428            Self::ListPanes(_) => "list-panes",
429            Self::SourceFile(_) => "source-file",
430            Self::UnlinkWindow(_) => "unlink-window",
431            Self::SetOptionByName(_) => "set-option",
432            Self::SetHookMutation(_) => "set-hook",
433            Self::ShowHooks(_) => "show-hooks",
434            Self::SendKeysExt(_) => "send-keys",
435            Self::SwitchClientExt(_) => "switch-client",
436            Self::BindKey(_) => "bind-key",
437            Self::UnbindKey(_) => "unbind-key",
438            Self::ListKeys(_) => "list-keys",
439            Self::SendPrefix(_) => "send-prefix",
440            Self::ClearHistory(_) => "clear-history",
441            Self::CopyMode(_) => "copy-mode",
442            Self::ControlMode(_) => "control-mode",
443            Self::ClockMode(_) => "clock-mode",
444            Self::ShowMessages(_) => "show-messages",
445            Self::NewSessionExt(_) => "new-session",
446            Self::AttachSessionExt(_) => "attach-session",
447            Self::SwitchClientExt2(_) => "switch-client",
448            Self::SelectCustomLayout(_) => "select-layout",
449            Self::SelectOldLayout(_) => "select-layout",
450            Self::SpreadLayout(_) => "select-layout",
451            Self::ResizeWindow(_) => "resize-window",
452            Self::RespawnWindow(_) => "respawn-window",
453            Self::MovePane(_) => "move-pane",
454            Self::PipePane(_) => "pipe-pane",
455            Self::RespawnPane(_) => "respawn-pane",
456            Self::KillServer(_) => "kill-server",
457            Self::LockServer(_) => "lock-server",
458            Self::LockSession(_) => "lock-session",
459            Self::LockClient(_) => "lock-client",
460            Self::ServerAccess(_) => "server-access",
461            Self::RefreshClient(_) => "refresh-client",
462            Self::ListClients(_) => "list-clients",
463            Self::SuspendClient(_) => "suspend-client",
464            Self::DetachClientExt(_) => "detach-client",
465            Self::AttachSessionExt2(_) | Self::AttachSessionExt3(_) => "attach-session",
466            Self::SwitchClientExt3(_) => "switch-client",
467            Self::Handshake(_) => "handshake",
468            Self::DaemonStatus(_) => "daemon-status",
469            Self::ShutdownIfIdle(_) => "shutdown-if-idle",
470            Self::WebShare(_) => "web-share",
471        }
472    }
473}
474
475/// Request payload for `show-messages`.
476#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
477pub struct ShowMessagesRequest {
478    /// Whether to print the server job summary.
479    pub jobs: bool,
480    /// Whether to print terminal information.
481    pub terminals: bool,
482    /// The optional target client filter used by terminal and job summaries.
483    pub target_client: Option<String>,
484}
485
486/// Request payload for `run-shell`.
487#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
488pub struct RunShellRequest {
489    /// The server-local shell command passed to `sh -c`.
490    pub command: String,
491    /// Whether the command should run fire-and-forget without output capture.
492    pub background: bool,
493    /// Whether the command should be executed as tmux commands instead of `sh -c`.
494    #[serde(default)]
495    pub as_commands: bool,
496    /// Whether stderr should be captured alongside stdout.
497    #[serde(default)]
498    pub show_stderr: bool,
499    /// Optional delay, in seconds, before the command runs.
500    #[serde(default)]
501    pub delay_seconds: Option<RunShellDelaySeconds>,
502    /// Optional explicit working directory.
503    #[serde(default)]
504    pub start_directory: Option<PathBuf>,
505    /// Optional explicit target pane used for format and session context.
506    #[serde(default)]
507    pub target: Option<PaneTarget>,
508    /// Internal source-file recursion depth inherited by queued run-shell.
509    #[serde(default)]
510    pub source_depth: Option<usize>,
511}
512
513/// Losslessly serializable `run-shell -d` seconds value with stable `Eq`.
514#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
515pub struct RunShellDelaySeconds(pub f64);
516
517impl RunShellDelaySeconds {
518    /// Returns the raw seconds value.
519    #[must_use]
520    pub const fn as_secs_f64(self) -> f64 {
521        self.0
522    }
523}
524
525impl PartialEq for RunShellDelaySeconds {
526    fn eq(&self, other: &Self) -> bool {
527        self.0.to_bits() == other.0.to_bits()
528    }
529}
530
531impl Eq for RunShellDelaySeconds {}
532
533/// Request payload for `if-shell`.
534#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
535pub struct IfShellRequest {
536    /// The condition string expanded through the shared formatter before evaluation.
537    pub condition: String,
538    /// Whether to evaluate the expanded condition with format truthiness instead of `sh -c`.
539    pub format_mode: bool,
540    /// The nested RMUX command string dispatched when the condition is true.
541    pub then_command: String,
542    /// The optional nested RMUX command string dispatched when the condition is false.
543    pub else_command: Option<String>,
544    /// Optional exact target used as shared-format context.
545    pub target: Option<Target>,
546    /// The caller working directory used to resolve nested relative file paths.
547    pub caller_cwd: Option<PathBuf>,
548    /// Whether the condition should be evaluated asynchronously.
549    #[serde(default)]
550    pub background: bool,
551}
552
553/// Request payload for `source-file`.
554#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
555pub struct SourceFileRequest {
556    /// The path arguments to expand, glob, parse, and optionally execute.
557    pub paths: Vec<String>,
558    /// Whether missing files and glob misses should be suppressed.
559    pub quiet: bool,
560    /// Whether to parse only without executing parsed commands.
561    pub parse_only: bool,
562    /// Whether parsed commands should be printed to stdout.
563    pub verbose: bool,
564    /// Whether each path argument should be format-expanded before globbing.
565    pub expand_paths: bool,
566    /// Optional pane target used as the `-F` format context.
567    pub target: Option<PaneTarget>,
568    /// The caller working directory used to resolve relative paths.
569    pub caller_cwd: Option<PathBuf>,
570    /// Content read from client stdin for `source-file -`.
571    pub stdin: Option<String>,
572}
573
574/// Request payload for `unlink-window`.
575#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
576pub struct UnlinkWindowRequest {
577    /// The window slot to remove.
578    pub target: WindowTarget,
579    /// Whether removing the final link is allowed (`-k`).
580    #[serde(default)]
581    pub kill_if_last: bool,
582}
583
584/// The supported `wait-for` operation modes.
585#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
586pub enum WaitForMode {
587    /// Wait for the next signal on the named channel.
588    Wait,
589    /// Signal all current plain waiters on the named channel.
590    Signal,
591    /// Acquire the named server-local lock, waiting in FIFO order when held.
592    Lock,
593    /// Release the named server-local lock.
594    Unlock,
595}
596
597/// Request payload for `wait-for`.
598#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
599pub struct WaitForRequest {
600    /// The server-local wait channel name.
601    pub channel: String,
602    /// The selected wait operation.
603    pub mode: WaitForMode,
604}
605
606/// Request payload for a daemon-backed SDK byte wait.
607///
608/// This is intentionally distinct from tmux-compatible [`WaitForRequest`].
609/// SDK waits are pane-output waits with typed IDs used only for cancellation
610/// and teardown bookkeeping; they never signal or lock tmux `wait-for`
611/// channels.
612#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
613pub struct SdkWaitForOutputRequest {
614    /// Opaque SDK transport owner for this wait.
615    pub owner_id: SdkWaitOwnerId,
616    /// Wait ID allocated by the SDK under `owner_id`.
617    pub wait_id: SdkWaitId,
618    /// Pane whose raw output stream is observed.
619    pub target: PaneTarget,
620    /// Raw byte sequence to search for in pane output.
621    pub bytes: Vec<u8>,
622    /// Cursor position used when arming the wait.
623    pub start: PaneOutputSubscriptionStart,
624}
625
626/// Request payload for a daemon-backed SDK byte wait by slot or stable id.
627#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
628pub struct SdkWaitForOutputRefRequest {
629    /// Opaque SDK transport owner for this wait.
630    pub owner_id: SdkWaitOwnerId,
631    /// Wait ID allocated by the SDK under `owner_id`.
632    pub wait_id: SdkWaitId,
633    /// Pane whose raw output stream is observed.
634    pub target: PaneTargetRef,
635    /// Raw byte sequence to search for in pane output.
636    pub bytes: Vec<u8>,
637    /// Cursor position used when arming the wait.
638    pub start: PaneOutputSubscriptionStart,
639}
640
641/// Request payload for best-effort cancellation of a daemon-backed SDK wait.
642#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
643pub struct CancelSdkWaitRequest {
644    /// Opaque SDK transport owner for the wait being cancelled.
645    pub owner_id: SdkWaitOwnerId,
646    /// Wait ID allocated by the SDK under `owner_id`.
647    pub wait_id: SdkWaitId,
648}
649
650/// Request payload for `list-panes`.
651#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
652pub struct ListPanesRequest {
653    /// The exact target session name.
654    pub target: SessionName,
655    /// Optional exact target window index.
656    #[serde(default)]
657    pub target_window_index: Option<u32>,
658    /// An optional server-side format template.
659    pub format: Option<String>,
660}
661
662#[cfg(test)]
663#[path = "request/compat_tests.rs"]
664mod compat_tests;
665
666#[cfg(test)]
667mod tests {
668    use super::*;
669    use crate::{
670        OptionScopeSelector, PaneTarget, ScopeSelector, SelectPaneDirection, SetOptionMode,
671        SplitDirection,
672    };
673
674    fn alpha() -> SessionName {
675        SessionName::new("alpha").expect("valid session")
676    }
677
678    fn pane() -> PaneTarget {
679        PaneTarget::new(alpha(), 0)
680    }
681
682    #[test]
683    fn request_command_names_cover_extended_aliases_and_internal_tags() {
684        assert_eq!(
685            Request::NewSessionExt(NewSessionExtRequest {
686                session_name: None,
687                working_directory: None,
688                detached: true,
689                size: None,
690                environment: None,
691                group_target: None,
692                attach_if_exists: false,
693                detach_other_clients: false,
694                kill_other_clients: false,
695                flags: None,
696                window_name: None,
697                print_session_info: false,
698                print_format: None,
699                command: None,
700                process_command: None,
701                client_environment: None,
702                skip_environment_update: false,
703            })
704            .command_name(),
705            "new-session"
706        );
707        assert_eq!(
708            Request::SplitWindowExt(SplitWindowExtRequest {
709                target: SplitWindowTarget::Pane(pane()),
710                direction: SplitDirection::Vertical,
711                before: false,
712                environment: None,
713                command: None,
714                process_command: None,
715                start_directory: None,
716                keep_alive_on_exit: None,
717                detached: false,
718                size: None,
719                preserve_zoom: false,
720                full_size: false,
721                stdin_payload: None,
722            })
723            .command_name(),
724            "split-window"
725        );
726        assert_eq!(
727            Request::SelectPaneAdjacent(SelectPaneAdjacentRequest {
728                target: pane(),
729                direction: SelectPaneDirection::Right,
730                preserve_zoom: false,
731            })
732            .command_name(),
733            "select-pane"
734        );
735        assert_eq!(
736            Request::SelectPaneMark(SelectPaneMarkRequest {
737                target: pane(),
738                clear: false,
739                title: None,
740            })
741            .command_name(),
742            "select-pane"
743        );
744        assert_eq!(
745            Request::SetOptionByName(SetOptionByNameRequest {
746                scope: OptionScopeSelector::ServerGlobal,
747                name: "status".to_owned(),
748                value: Some("on".to_owned()),
749                mode: SetOptionMode::Replace,
750                only_if_unset: false,
751                unset: false,
752                unset_pane_overrides: false,
753                format: false,
754                format_target: None,
755            })
756            .command_name(),
757            "set-option"
758        );
759        assert_eq!(
760            Request::SetHookMutation(SetHookMutationRequest {
761                scope: ScopeSelector::Global,
762                hook: crate::HookName::AfterNewSession,
763                command: None,
764                lifecycle: crate::HookLifecycle::Persistent,
765                append: false,
766                unset: true,
767                run_immediately: false,
768                index: None,
769            })
770            .command_name(),
771            "set-hook"
772        );
773        assert_eq!(
774            Request::AttachSessionExt2(AttachSessionExt2Request {
775                target: Some(alpha()),
776                target_spec: None,
777                detach_other_clients: false,
778                kill_other_clients: false,
779                read_only: false,
780                skip_environment_update: false,
781                flags: None,
782                working_directory: None,
783                client_terminal: crate::ClientTerminalContext::default(),
784                client_size: None,
785            })
786            .command_name(),
787            "attach-session"
788        );
789        assert_eq!(
790            Request::AttachSessionExt3(AttachSessionExt3Request::from_ext2(
791                AttachSessionExt2Request {
792                    target: Some(alpha()),
793                    target_spec: None,
794                    detach_other_clients: false,
795                    kill_other_clients: false,
796                    read_only: false,
797                    skip_environment_update: false,
798                    flags: None,
799                    working_directory: None,
800                    client_terminal: crate::ClientTerminalContext::default(),
801                    client_size: None,
802                },
803                vec![crate::CAPABILITY_ATTACH_RENDER.to_owned()],
804            ))
805            .command_name(),
806            "attach-session"
807        );
808        assert_eq!(
809            Request::SwitchClientExt3(SwitchClientExt3Request {
810                target_client: None,
811                target: Some("alpha:0.0".to_owned()),
812                key_table: None,
813                last_session: false,
814                next_session: false,
815                previous_session: false,
816                toggle_read_only: false,
817                sort_order: None,
818                skip_environment_update: false,
819                zoom: false,
820            })
821            .command_name(),
822            "switch-client"
823        );
824        assert_eq!(
825            Request::ResolveTarget(ResolveTargetRequest {
826                target: Some("alpha:0.0".to_owned()),
827                target_type: ResolveTargetType::Pane,
828                window_index: false,
829                prefer_unattached: false,
830            })
831            .command_name(),
832            "resolve-target"
833        );
834        assert_eq!(
835            Request::Handshake(HandshakeRequest::current()).command_name(),
836            "handshake"
837        );
838        assert_eq!(
839            Request::SdkWaitForOutput(SdkWaitForOutputRequest {
840                owner_id: SdkWaitOwnerId::new(1),
841                wait_id: SdkWaitId::new(1),
842                target: pane(),
843                bytes: b"ready".to_vec(),
844                start: PaneOutputSubscriptionStart::Now,
845            })
846            .command_name(),
847            "sdk-wait-output"
848        );
849        assert_eq!(
850            Request::CancelSdkWait(CancelSdkWaitRequest {
851                owner_id: SdkWaitOwnerId::new(1),
852                wait_id: SdkWaitId::new(1),
853            })
854            .command_name(),
855            "cancel-sdk-wait"
856        );
857        assert_eq!(
858            Request::WaitFor(WaitForRequest {
859                channel: "ready".to_owned(),
860                mode: WaitForMode::Wait,
861            })
862            .command_name(),
863            "wait-for"
864        );
865    }
866}