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