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::{
17    DisplayMessageDurationMillis, DisplayMessageDurationParseError, DisplayMessageExtRequest,
18    DisplayMessageRequest,
19};
20
21#[path = "request/show.rs"]
22mod show;
23pub use show::ShowHooksRequest;
24pub use show::{ShowEnvironmentRequest, ShowOptionsRequest};
25
26#[path = "request/layout.rs"]
27mod layout;
28pub use layout::{
29    NextLayoutRequest, PreviousLayoutRequest, SelectCustomLayoutRequest, SelectLayoutRequest,
30    SelectLayoutTarget, SelectOldLayoutRequest, SpreadLayoutRequest,
31};
32
33#[path = "request/pane.rs"]
34mod pane;
35pub use pane::{
36    BreakPaneRequest, DisplayPanesRequest, JoinPaneRequest, KillPaneRequest, LastPaneRequest,
37    MovePaneRequest, PaneBroadcastInputRequest, PaneForegroundStateRequest, PaneInputRequest,
38    PaneKillRequest, PaneOptionGetRequest, PaneOptionSetRequest, PaneOutputCursorRequest,
39    PaneOutputSubscriptionStart, PaneResizeRequest, PaneRespawnRequest, PaneSelectRequest,
40    PaneSnapshotRefRequest, PaneSnapshotRequest, PaneSplitSize, PaneStateCursorRequest,
41    PaneStreamCursorRequest, PaneStreamMode, PipePaneRequest, ResizePaneRequest,
42    ResizePaneTargetActionRequest, RespawnPaneRequest, SelectPaneAdjacentRequest,
43    SelectPaneDirection, SelectPaneMarkRequest, SelectPaneRequest, SendKeysExt2Request,
44    SendKeysExtRequest, SendKeysRequest, SplitWindowExtRequest, SplitWindowIdentityRequest,
45    SplitWindowRequest, SplitWindowTarget, SplitWindowTargetActionRequest,
46    SubscribePaneOutputRefRequest, SubscribePaneOutputRequest, SubscribePaneStateRequest,
47    SubscribePaneStreamRequest, SwapPaneDirection, SwapPaneRequest, UnsubscribePaneOutputRequest,
48    UnsubscribePaneStateRequest, UnsubscribePaneStreamRequest,
49};
50
51#[path = "request/window.rs"]
52mod window;
53pub use window::{
54    KillWindowRequest, LastWindowRequest, LinkWindowRequest, ListWindowsRequest, MoveWindowRequest,
55    MoveWindowTarget, NewWindowRequest, NextWindowRequest, PreviousWindowRequest,
56    RenameWindowRequest, ResizeWindowAdjustment, ResizeWindowRequest, RespawnWindowRequest,
57    RotateWindowDirection, RotateWindowRequest, SelectWindowRequest, SwapWindowRequest,
58};
59
60#[path = "request/target.rs"]
61mod target;
62pub use target::{ResolveTargetRequest, ResolveTargetType};
63
64#[path = "request/session.rs"]
65mod session;
66pub use session::{
67    CreateSessionLeaseRequest, HasSessionRequest, KillSessionRequest, ListSessionsRequest,
68    NewSessionExtRequest, NewSessionRequest, ReleaseSessionLeaseRequest, RenameSessionRequest,
69    RenewSessionLeaseRequest,
70};
71
72#[path = "request/server.rs"]
73mod server;
74pub use server::{
75    DaemonStatusRequest, KillServerRequest, LockClientRequest, LockServerRequest,
76    LockSessionRequest, ServerAccessRequest, ShutdownIfIdleRequest,
77};
78
79#[path = "request/client.rs"]
80mod client;
81pub use client::{
82    AttachSessionExt2Request, AttachSessionExt3Request, AttachSessionExtRequest,
83    AttachSessionRequest, DetachClientExtRequest, DetachClientRequest, ListClientsRequest,
84    RefreshClientRequest, SuspendClientRequest, SwitchClientExt2Request, SwitchClientExt3Request,
85    SwitchClientExtRequest, SwitchClientRequest,
86};
87
88#[path = "request/keys.rs"]
89mod keys;
90pub use keys::{
91    BindKeyRequest, ClockModeRequest, CopyModeRequest, ListKeysRequest, SendPrefixRequest,
92    UnbindKeyRequest,
93};
94
95#[path = "request/options.rs"]
96mod options;
97pub use options::{
98    SetEnvironmentMode, SetEnvironmentRequest, SetHookMutationRequest, SetHookRequest,
99    SetOptionByNameRequest, SetOptionRequest,
100};
101
102#[path = "request/buffer.rs"]
103mod buffer;
104pub use buffer::{
105    CapturePaneRequest, CapturePaneTargetActionRequest, ClearHistoryRequest, DeleteBufferRequest,
106    ListBuffersRequest, LoadBufferRequest, PasteBufferRequest, SaveBufferRequest, SetBufferRequest,
107    ShowBufferRequest,
108};
109
110#[path = "request/web.rs"]
111mod web;
112pub use web::{
113    CreateWebShareRequest, ListWebSharesRequest, LookupWebShareRequest, StopAllWebSharesRequest,
114    StopWebShareRequest, WebShareConfigRequest, WebShareRequest, WebShareScope, WebShareUrlOptions,
115    WebTerminalPalette, WebTerminalTheme,
116};
117
118/// All detached public command and internal RPC requests supported by the wire
119/// protocol.
120#[non_exhaustive]
121#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
122pub enum Request {
123    /// `new-session`
124    NewSession(NewSessionRequest),
125    /// `has-session`
126    HasSession(HasSessionRequest),
127    /// `kill-session`
128    KillSession(KillSessionRequest),
129    /// `new-window`
130    NewWindow(Box<NewWindowRequest>),
131    /// `kill-window`
132    KillWindow(KillWindowRequest),
133    /// `select-window`
134    SelectWindow(SelectWindowRequest),
135    /// `rename-window`
136    RenameWindow(RenameWindowRequest),
137    /// `next-window`
138    NextWindow(NextWindowRequest),
139    /// `previous-window`
140    PreviousWindow(PreviousWindowRequest),
141    /// `last-window`
142    LastWindow(LastWindowRequest),
143    /// `list-windows`
144    ListWindows(Box<ListWindowsRequest>),
145    /// `move-window`
146    MoveWindow(MoveWindowRequest),
147    /// `swap-window`
148    SwapWindow(SwapWindowRequest),
149    /// `rotate-window`
150    RotateWindow(RotateWindowRequest),
151    /// `split-window`
152    SplitWindow(SplitWindowRequest),
153    /// `swap-pane`
154    SwapPane(SwapPaneRequest),
155    /// `last-pane`
156    LastPane(LastPaneRequest),
157    /// `join-pane`
158    JoinPane(JoinPaneRequest),
159    /// `break-pane`
160    BreakPane(Box<BreakPaneRequest>),
161    /// `kill-pane`
162    KillPane(KillPaneRequest),
163    /// `select-layout`
164    SelectLayout(SelectLayoutRequest),
165    /// `resize-pane`
166    ResizePane(ResizePaneRequest),
167    /// `display-panes`
168    DisplayPanes(Box<DisplayPanesRequest>),
169    /// `select-pane`
170    SelectPane(Box<SelectPaneRequest>),
171    /// `select-pane -U/-D/-L/-R`
172    SelectPaneAdjacent(SelectPaneAdjacentRequest),
173    /// `send-keys`
174    SendKeys(SendKeysRequest),
175    /// `attach-session`
176    AttachSession(AttachSessionRequest),
177    /// `switch-client`
178    SwitchClient(SwitchClientRequest),
179    /// `detach-client`
180    DetachClient(DetachClientRequest),
181    /// `set-option`
182    SetOption(SetOptionRequest),
183    /// `set-environment`
184    SetEnvironment(Box<SetEnvironmentRequest>),
185    /// `set-hook`
186    SetHook(SetHookRequest),
187    /// `next-layout`
188    NextLayout(NextLayoutRequest),
189    /// `previous-layout`
190    PreviousLayout(PreviousLayoutRequest),
191    /// `show-options`
192    ShowOptions(ShowOptionsRequest),
193    /// `show-environment`
194    ShowEnvironment(ShowEnvironmentRequest),
195    /// `set-buffer`
196    SetBuffer(Box<SetBufferRequest>),
197    /// `show-buffer`
198    ShowBuffer(ShowBufferRequest),
199    /// `paste-buffer`
200    PasteBuffer(Box<PasteBufferRequest>),
201    /// `list-buffers`
202    ListBuffers(ListBuffersRequest),
203    /// `delete-buffer`
204    DeleteBuffer(DeleteBufferRequest),
205    /// `load-buffer`
206    LoadBuffer(Box<LoadBufferRequest>),
207    /// `save-buffer`
208    SaveBuffer(SaveBufferRequest),
209    /// `capture-pane`
210    CapturePane(Box<CapturePaneRequest>),
211    /// `display-message`
212    DisplayMessage(DisplayMessageRequest),
213    /// `run-shell`
214    RunShell(Box<RunShellRequest>),
215    /// `if-shell`
216    IfShell(Box<IfShellRequest>),
217    /// `wait-for`
218    WaitFor(WaitForRequest),
219    /// `rename-session`
220    RenameSession(RenameSessionRequest),
221    /// `list-sessions`
222    ListSessions(ListSessionsRequest),
223    /// `list-panes`
224    ListPanes(Box<ListPanesRequest>),
225    /// `source-file`
226    SourceFile(Box<SourceFileRequest>),
227    /// `set-option` using an open string-based option name.
228    SetOptionByName(Box<SetOptionByNameRequest>),
229    /// Extended `set-hook` mutation semantics.
230    SetHookMutation(SetHookMutationRequest),
231    /// `show-hooks`
232    ShowHooks(ShowHooksRequest),
233    /// Extended `send-keys` semantics including key-table dispatch and format expansion.
234    SendKeysExt(SendKeysExtRequest),
235    /// Extended `switch-client` semantics including `-T key-table`.
236    SwitchClientExt(SwitchClientExtRequest),
237    /// `bind-key`
238    BindKey(Box<BindKeyRequest>),
239    /// `unbind-key`
240    UnbindKey(UnbindKeyRequest),
241    /// `list-keys`
242    ListKeys(Box<ListKeysRequest>),
243    /// `send-prefix`
244    SendPrefix(SendPrefixRequest),
245    /// `clear-history`
246    ClearHistory(ClearHistoryRequest),
247    /// `copy-mode`
248    CopyMode(CopyModeRequest),
249    /// Internal detached upgrade into tmux-compatible control mode.
250    ControlMode(ControlModeRequest),
251    /// `clock-mode`
252    ClockMode(ClockModeRequest),
253    /// `show-messages`
254    ShowMessages(ShowMessagesRequest),
255    /// Extended `new-session` semantics including grouped sessions and attach-if-exists.
256    NewSessionExt(Box<NewSessionExtRequest>),
257    /// Extended `attach-session` semantics including client flags and detach-others.
258    AttachSessionExt(AttachSessionExtRequest),
259    /// Extended `switch-client` semantics including `-l`, `-n`, `-p`, and readonly toggles.
260    SwitchClientExt2(Box<SwitchClientExt2Request>),
261    /// `select-layout` with a tmux custom layout string.
262    SelectCustomLayout(SelectCustomLayoutRequest),
263    /// `select-layout -o`
264    SelectOldLayout(SelectOldLayoutRequest),
265    /// `select-layout -E`
266    SpreadLayout(SpreadLayoutRequest),
267    /// `kill-server`
268    KillServer(KillServerRequest),
269    /// `lock-server`
270    LockServer(LockServerRequest),
271    /// `lock-session`
272    LockSession(LockSessionRequest),
273    /// `lock-client`
274    LockClient(LockClientRequest),
275    /// `server-access`
276    ServerAccess(ServerAccessRequest),
277    /// `refresh-client`
278    RefreshClient(Box<RefreshClientRequest>),
279    /// `list-clients`
280    ListClients(Box<ListClientsRequest>),
281    /// `suspend-client`
282    SuspendClient(SuspendClientRequest),
283    /// Extended `detach-client` semantics including `-a`, `-s`, `-P`, and `-E`.
284    DetachClientExt(DetachClientExtRequest),
285    /// Further-extended `attach-session` semantics including `-c working-directory`.
286    AttachSessionExt2(Box<AttachSessionExt2Request>),
287    /// Further-extended `switch-client` semantics including `-c target-client` and `-Z`.
288    SwitchClientExt3(Box<SwitchClientExt3Request>),
289    /// `resize-window`
290    ResizeWindow(ResizeWindowRequest),
291    /// `respawn-window`
292    RespawnWindow(Box<RespawnWindowRequest>),
293    /// `move-pane`
294    MovePane(MovePaneRequest),
295    /// `pipe-pane`
296    PipePane(PipePaneRequest),
297    /// `respawn-pane`
298    RespawnPane(Box<RespawnPaneRequest>),
299    /// `link-window`
300    LinkWindow(LinkWindowRequest),
301    /// `unlink-window`
302    UnlinkWindow(UnlinkWindowRequest),
303    /// `select-pane -m` / `select-pane -M`
304    SelectPaneMark(SelectPaneMarkRequest),
305    /// Internal detached target resolution for tmux-style raw target text.
306    ResolveTarget(ResolveTargetRequest),
307    /// Extended `split-window` semantics including an explicit shell command.
308    SplitWindowExt(Box<SplitWindowExtRequest>),
309    /// Internal SDK/daemon version and capability negotiation.
310    Handshake(HandshakeRequest),
311    /// Internal daemon-backed structured pane snapshot endpoint.
312    PaneSnapshot(PaneSnapshotRequest),
313    /// Internal daemon-backed pane output subscription endpoint.
314    SubscribePaneOutput(SubscribePaneOutputRequest),
315    /// Internal daemon-backed pane output unsubscription endpoint.
316    UnsubscribePaneOutput(UnsubscribePaneOutputRequest),
317    /// Internal daemon-backed pane output cursor polling endpoint.
318    PaneOutputCursor(PaneOutputCursorRequest),
319    /// Internal daemon-backed SDK byte wait endpoint.
320    SdkWaitForOutput(SdkWaitForOutputRequest),
321    /// Internal daemon-backed SDK wait cancellation endpoint.
322    CancelSdkWait(CancelSdkWaitRequest),
323    /// SDK pane input endpoint with stable pane-id targeting.
324    PaneInput(PaneInputRequest),
325    /// SDK pane resize endpoint with stable pane-id targeting.
326    PaneResize(PaneResizeRequest),
327    /// SDK pane kill endpoint with stable pane-id targeting.
328    PaneKill(PaneKillRequest),
329    /// SDK pane respawn endpoint with stable pane-id targeting.
330    PaneRespawn(Box<PaneRespawnRequest>),
331    /// SDK pane snapshot endpoint with stable pane-id targeting.
332    PaneSnapshotRef(PaneSnapshotRefRequest),
333    /// SDK pane select/title endpoint with stable pane-id targeting.
334    PaneSelect(PaneSelectRequest),
335    /// SDK pane input broadcast endpoint with stable pane-id targeting.
336    PaneBroadcastInput(PaneBroadcastInputRequest),
337    /// SDK app-owned session lease create endpoint.
338    CreateSessionLease(CreateSessionLeaseRequest),
339    /// SDK app-owned session lease renewal endpoint.
340    RenewSessionLease(RenewSessionLeaseRequest),
341    /// SDK app-owned session lease release endpoint.
342    ReleaseSessionLease(ReleaseSessionLeaseRequest),
343    /// Internal daemon-backed pane output subscription endpoint with stable pane-id targeting.
344    SubscribePaneOutputRef(SubscribePaneOutputRefRequest),
345    /// Internal daemon-backed SDK byte wait endpoint with stable pane-id targeting.
346    SdkWaitForOutputRef(SdkWaitForOutputRefRequest),
347    /// Internal daemon version and activity status endpoint.
348    DaemonStatus(DaemonStatusRequest),
349    /// Internal idle-only shutdown endpoint used by seamless upgrades.
350    ShutdownIfIdle(ShutdownIfIdleRequest),
351    /// Browser-visible pane sharing command family.
352    WebShare(Box<WebShareRequest>),
353    /// `display-message` extension with target-client context.
354    DisplayMessageExt(Box<DisplayMessageExtRequest>),
355    /// `send-keys` extension with target-client context.
356    SendKeysExt2(Box<SendKeysExt2Request>),
357    /// Attach-session extension with attach-stream client capabilities.
358    AttachSessionExt3(Box<AttachSessionExt3Request>),
359    /// `split-window` with raw target text resolved server-side.
360    SplitWindowTargetAction(Box<SplitWindowTargetActionRequest>),
361    /// `resize-pane` with raw target text resolved server-side.
362    ResizePaneTargetAction(ResizePaneTargetActionRequest),
363    /// `capture-pane` with raw target text resolved server-side.
364    CapturePaneTargetAction(Box<CapturePaneTargetActionRequest>),
365    /// SDK pane option mutation endpoint with stable pane-id targeting.
366    PaneOptionSet(PaneOptionSetRequest),
367    /// SDK pane option lookup endpoint with stable pane-id targeting.
368    PaneOptionGet(PaneOptionGetRequest),
369    /// SDK pane-state subscription endpoint with stable pane-id targeting.
370    SubscribePaneState(SubscribePaneStateRequest),
371    /// SDK pane-state cursor endpoint.
372    PaneStateCursor(PaneStateCursorRequest),
373    /// SDK pane-state unsubscription endpoint.
374    UnsubscribePaneState(UnsubscribePaneStateRequest),
375    /// SDK pane foreground-state endpoint with stable pane-id targeting.
376    PaneForegroundState(PaneForegroundStateRequest),
377    /// SDK split endpoint returning visible and stable pane identity atomically.
378    SplitWindowIdentity(Box<SplitWindowIdentityRequest>),
379    /// Opens a recoverable raw or surface pane stream.
380    SubscribePaneStream(SubscribePaneStreamRequest),
381    /// Polls a recoverable raw or surface pane stream.
382    PaneStreamCursor(PaneStreamCursorRequest),
383    /// Closes a recoverable raw or surface pane stream.
384    UnsubscribePaneStream(UnsubscribePaneStreamRequest),
385}
386
387impl Request {
388    /// Returns the stable routing name for the request variant.
389    #[must_use]
390    pub const fn command_name(&self) -> &'static str {
391        match self {
392            Self::NewSession(_) => "new-session",
393            Self::HasSession(_) => "has-session",
394            Self::KillSession(_) => "kill-session",
395            Self::NewWindow(_) => "new-window",
396            Self::KillWindow(_) => "kill-window",
397            Self::SelectWindow(_) => "select-window",
398            Self::RenameWindow(_) => "rename-window",
399            Self::NextWindow(_) => "next-window",
400            Self::PreviousWindow(_) => "previous-window",
401            Self::LastWindow(_) => "last-window",
402            Self::ListWindows(_) => "list-windows",
403            Self::LinkWindow(_) => "link-window",
404            Self::MoveWindow(_) => "move-window",
405            Self::SwapWindow(_) => "swap-window",
406            Self::RotateWindow(_) => "rotate-window",
407            Self::SplitWindow(_)
408            | Self::SplitWindowExt(_)
409            | Self::SplitWindowTargetAction(_)
410            | Self::SplitWindowIdentity(_) => "split-window",
411            Self::SwapPane(_) => "swap-pane",
412            Self::LastPane(_) => "last-pane",
413            Self::JoinPane(_) => "join-pane",
414            Self::BreakPane(_) => "break-pane",
415            Self::KillPane(_) => "kill-pane",
416            Self::SelectLayout(_) => "select-layout",
417            Self::ResizePane(_) => "resize-pane",
418            Self::DisplayPanes(_) => "display-panes",
419            Self::SelectPane(_) | Self::SelectPaneAdjacent(_) | Self::SelectPaneMark(_) => {
420                "select-pane"
421            }
422            Self::SendKeys(_) | Self::SendKeysExt2(_) => "send-keys",
423            Self::AttachSession(_) => "attach-session",
424            Self::SwitchClient(_) => "switch-client",
425            Self::DetachClient(_) => "detach-client",
426            Self::SetOption(_) => "set-option",
427            Self::SetEnvironment(_) => "set-environment",
428            Self::SetHook(_) => "set-hook",
429            Self::NextLayout(_) => "next-layout",
430            Self::PreviousLayout(_) => "previous-layout",
431            Self::ShowOptions(_) => "show-options",
432            Self::ShowEnvironment(_) => "show-environment",
433            Self::SetBuffer(_) => "set-buffer",
434            Self::ShowBuffer(_) => "show-buffer",
435            Self::PasteBuffer(_) => "paste-buffer",
436            Self::ListBuffers(_) => "list-buffers",
437            Self::DeleteBuffer(_) => "delete-buffer",
438            Self::LoadBuffer(_) => "load-buffer",
439            Self::SaveBuffer(_) => "save-buffer",
440            Self::CapturePane(_) | Self::CapturePaneTargetAction(_) => "capture-pane",
441            Self::PaneSnapshot(_) => "pane-snapshot",
442            Self::SubscribePaneOutput(_) | Self::SubscribePaneOutputRef(_) => {
443                "subscribe-pane-output"
444            }
445            Self::UnsubscribePaneOutput(_) => "unsubscribe-pane-output",
446            Self::PaneOutputCursor(_) => "pane-output-cursor",
447            Self::SubscribePaneStream(_) => "subscribe-pane-stream",
448            Self::PaneStreamCursor(_) => "pane-stream-cursor",
449            Self::UnsubscribePaneStream(_) => "unsubscribe-pane-stream",
450            Self::SdkWaitForOutput(_) | Self::SdkWaitForOutputRef(_) => "sdk-wait-output",
451            Self::CancelSdkWait(_) => "cancel-sdk-wait",
452            Self::PaneInput(_) => "send-keys",
453            Self::PaneBroadcastInput(_) => "send-keys",
454            Self::CreateSessionLease(_) => "create-session-lease",
455            Self::RenewSessionLease(_) => "renew-session-lease",
456            Self::ReleaseSessionLease(_) => "release-session-lease",
457            Self::PaneResize(_) | Self::ResizePaneTargetAction(_) => "resize-pane",
458            Self::PaneKill(_) => "kill-pane",
459            Self::PaneRespawn(_) => "respawn-pane",
460            Self::PaneSnapshotRef(_) => "pane-snapshot",
461            Self::PaneSelect(_) => "select-pane",
462            Self::PaneOptionSet(_) => "pane-option-set",
463            Self::PaneOptionGet(_) => "pane-option-get",
464            Self::SubscribePaneState(_) => "subscribe-pane-state",
465            Self::PaneStateCursor(_) => "pane-state-cursor",
466            Self::UnsubscribePaneState(_) => "unsubscribe-pane-state",
467            Self::PaneForegroundState(_) => "pane-foreground-state",
468            Self::DisplayMessage(_) | Self::DisplayMessageExt(_) => "display-message",
469            Self::ResolveTarget(_) => "resolve-target",
470            Self::RunShell(_) => "run-shell",
471            Self::IfShell(_) => "if-shell",
472            Self::WaitFor(_) => "wait-for",
473            Self::RenameSession(_) => "rename-session",
474            Self::ListSessions(_) => "list-sessions",
475            Self::ListPanes(_) => "list-panes",
476            Self::SourceFile(_) => "source-file",
477            Self::UnlinkWindow(_) => "unlink-window",
478            Self::SetOptionByName(_) => "set-option",
479            Self::SetHookMutation(_) => "set-hook",
480            Self::ShowHooks(_) => "show-hooks",
481            Self::SendKeysExt(_) => "send-keys",
482            Self::SwitchClientExt(_) => "switch-client",
483            Self::BindKey(_) => "bind-key",
484            Self::UnbindKey(_) => "unbind-key",
485            Self::ListKeys(_) => "list-keys",
486            Self::SendPrefix(_) => "send-prefix",
487            Self::ClearHistory(_) => "clear-history",
488            Self::CopyMode(_) => "copy-mode",
489            Self::ControlMode(_) => "control-mode",
490            Self::ClockMode(_) => "clock-mode",
491            Self::ShowMessages(_) => "show-messages",
492            Self::NewSessionExt(_) => "new-session",
493            Self::AttachSessionExt(_) => "attach-session",
494            Self::SwitchClientExt2(_) => "switch-client",
495            Self::SelectCustomLayout(_) => "select-layout",
496            Self::SelectOldLayout(_) => "select-layout",
497            Self::SpreadLayout(_) => "select-layout",
498            Self::ResizeWindow(_) => "resize-window",
499            Self::RespawnWindow(_) => "respawn-window",
500            Self::MovePane(_) => "move-pane",
501            Self::PipePane(_) => "pipe-pane",
502            Self::RespawnPane(_) => "respawn-pane",
503            Self::KillServer(_) => "kill-server",
504            Self::LockServer(_) => "lock-server",
505            Self::LockSession(_) => "lock-session",
506            Self::LockClient(_) => "lock-client",
507            Self::ServerAccess(_) => "server-access",
508            Self::RefreshClient(_) => "refresh-client",
509            Self::ListClients(_) => "list-clients",
510            Self::SuspendClient(_) => "suspend-client",
511            Self::DetachClientExt(_) => "detach-client",
512            Self::AttachSessionExt2(_) | Self::AttachSessionExt3(_) => "attach-session",
513            Self::SwitchClientExt3(_) => "switch-client",
514            Self::Handshake(_) => "handshake",
515            Self::DaemonStatus(_) => "daemon-status",
516            Self::ShutdownIfIdle(_) => "shutdown-if-idle",
517            Self::WebShare(_) => "web-share",
518        }
519    }
520}
521
522/// Request payload for `show-messages`.
523#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
524pub struct ShowMessagesRequest {
525    /// Whether to print the server job summary.
526    pub jobs: bool,
527    /// Whether to print terminal information.
528    pub terminals: bool,
529    /// The optional target client filter used by terminal and job summaries.
530    pub target_client: Option<String>,
531}
532
533/// Request payload for `run-shell`.
534#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
535pub struct RunShellRequest {
536    /// The server-local shell command passed to `sh -c`.
537    pub command: String,
538    /// Literal positional format arguments exposed as `#{1}`, `#{2}`, and so on.
539    #[serde(default)]
540    pub arguments: Vec<String>,
541    /// Whether the command should run fire-and-forget without output capture.
542    pub background: bool,
543    /// Whether the command should be executed as tmux commands instead of `sh -c`.
544    #[serde(default)]
545    pub as_commands: bool,
546    /// Whether stderr should be captured alongside stdout.
547    #[serde(default)]
548    pub show_stderr: bool,
549    /// Optional delay, in seconds, before the command runs.
550    #[serde(default)]
551    pub delay_seconds: Option<RunShellDelaySeconds>,
552    /// Optional explicit working directory.
553    #[serde(default)]
554    pub start_directory: Option<PathBuf>,
555    /// Optional explicit target pane used for format and session context.
556    #[serde(default)]
557    pub target: Option<PaneTarget>,
558    /// Internal source-file recursion depth inherited by queued run-shell.
559    #[serde(default)]
560    pub source_depth: Option<usize>,
561}
562
563/// Losslessly serializable `run-shell -d` seconds value with stable `Eq`.
564#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
565pub struct RunShellDelaySeconds(pub f64);
566
567impl RunShellDelaySeconds {
568    /// Returns the raw seconds value.
569    #[must_use]
570    pub const fn as_secs_f64(self) -> f64 {
571        self.0
572    }
573}
574
575impl PartialEq for RunShellDelaySeconds {
576    fn eq(&self, other: &Self) -> bool {
577        self.0.to_bits() == other.0.to_bits()
578    }
579}
580
581impl Eq for RunShellDelaySeconds {}
582
583/// Request payload for `if-shell`.
584#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
585pub struct IfShellRequest {
586    /// The condition string expanded through the shared formatter before evaluation.
587    pub condition: String,
588    /// Whether to evaluate the expanded condition with format truthiness instead of `sh -c`.
589    pub format_mode: bool,
590    /// The nested RMUX command string dispatched when the condition is true.
591    pub then_command: String,
592    /// The optional nested RMUX command string dispatched when the condition is false.
593    pub else_command: Option<String>,
594    /// Optional exact target used as shared-format context.
595    pub target: Option<Target>,
596    /// The caller working directory used to resolve nested relative file paths.
597    pub caller_cwd: Option<PathBuf>,
598    /// Whether the condition should be evaluated asynchronously.
599    #[serde(default)]
600    pub background: bool,
601}
602
603/// Request payload for `source-file`.
604#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
605pub struct SourceFileRequest {
606    /// The path arguments to expand, glob, parse, and optionally execute.
607    pub paths: Vec<String>,
608    /// Whether missing files and glob misses should be suppressed.
609    pub quiet: bool,
610    /// Whether to parse only without executing parsed commands.
611    pub parse_only: bool,
612    /// Whether parsed commands should be printed to stdout.
613    pub verbose: bool,
614    /// Whether each path argument should be format-expanded before globbing.
615    pub expand_paths: bool,
616    /// Optional pane target used as the `-F` format context.
617    pub target: Option<PaneTarget>,
618    /// The caller working directory used to resolve relative paths.
619    pub caller_cwd: Option<PathBuf>,
620    /// Content read from client stdin for `source-file -`.
621    pub stdin: Option<String>,
622}
623
624/// Request payload for `unlink-window`.
625#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
626pub struct UnlinkWindowRequest {
627    /// The window slot to remove.
628    pub target: WindowTarget,
629    /// Whether removing the final link is allowed (`-k`).
630    #[serde(default)]
631    pub kill_if_last: bool,
632}
633
634/// The supported `wait-for` operation modes.
635#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
636pub enum WaitForMode {
637    /// Wait for the next signal on the named channel.
638    Wait,
639    /// Signal all current plain waiters on the named channel.
640    Signal,
641    /// Acquire the named server-local lock, waiting in FIFO order when held.
642    Lock,
643    /// Release the named server-local lock.
644    Unlock,
645}
646
647/// Request payload for `wait-for`.
648#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
649pub struct WaitForRequest {
650    /// The server-local wait channel name.
651    pub channel: String,
652    /// The selected wait operation.
653    pub mode: WaitForMode,
654}
655
656/// Request payload for a daemon-backed SDK byte wait.
657///
658/// This is intentionally distinct from tmux-compatible [`WaitForRequest`].
659/// SDK waits are pane-output waits with typed IDs used only for cancellation
660/// and teardown bookkeeping; they never signal or lock tmux `wait-for`
661/// channels.
662#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
663pub struct SdkWaitForOutputRequest {
664    /// Opaque SDK transport owner for this wait.
665    pub owner_id: SdkWaitOwnerId,
666    /// Wait ID allocated by the SDK under `owner_id`.
667    pub wait_id: SdkWaitId,
668    /// Pane whose raw output stream is observed.
669    pub target: PaneTarget,
670    /// Raw byte sequence to search for in pane output.
671    pub bytes: Vec<u8>,
672    /// Cursor position used when arming the wait.
673    pub start: PaneOutputSubscriptionStart,
674}
675
676/// Request payload for a daemon-backed SDK byte wait by slot or stable id.
677#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
678pub struct SdkWaitForOutputRefRequest {
679    /// Opaque SDK transport owner for this wait.
680    pub owner_id: SdkWaitOwnerId,
681    /// Wait ID allocated by the SDK under `owner_id`.
682    pub wait_id: SdkWaitId,
683    /// Pane whose raw output stream is observed.
684    pub target: PaneTargetRef,
685    /// Raw byte sequence to search for in pane output.
686    pub bytes: Vec<u8>,
687    /// Cursor position used when arming the wait.
688    pub start: PaneOutputSubscriptionStart,
689}
690
691/// Request payload for best-effort cancellation of a daemon-backed SDK wait.
692#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
693pub struct CancelSdkWaitRequest {
694    /// Opaque SDK transport owner for the wait being cancelled.
695    pub owner_id: SdkWaitOwnerId,
696    /// Wait ID allocated by the SDK under `owner_id`.
697    pub wait_id: SdkWaitId,
698}
699
700/// Request payload for `list-panes`.
701#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
702pub struct ListPanesRequest {
703    /// The exact target session name.
704    pub target: SessionName,
705    /// Optional exact target window index.
706    #[serde(default)]
707    pub target_window_index: Option<u32>,
708    /// An optional server-side format template.
709    pub format: Option<String>,
710    /// Optional filter expression.
711    #[serde(default)]
712    pub filter: Option<String>,
713    /// Optional sort-order token.
714    #[serde(default)]
715    pub sort_order: Option<String>,
716    /// Whether the selected sort order should be reversed.
717    #[serde(default)]
718    pub reversed: bool,
719}
720
721#[cfg(test)]
722#[path = "request/compat_tests.rs"]
723mod compat_tests;
724
725#[cfg(test)]
726mod tests {
727    use super::*;
728    use crate::{
729        OptionScopeSelector, PaneTarget, ScopeSelector, SelectPaneDirection, SetOptionMode,
730        SplitDirection, WebShareScope,
731    };
732    use serde::Serialize;
733    use std::mem::size_of;
734
735    fn alpha() -> SessionName {
736        SessionName::new("alpha").expect("valid session")
737    }
738
739    fn pane() -> PaneTarget {
740        PaneTarget::new(alpha(), 0)
741    }
742
743    #[test]
744    fn request_command_names_cover_extended_aliases_and_internal_tags() {
745        assert_eq!(
746            Request::NewSessionExt(Box::new(NewSessionExtRequest {
747                session_name: None,
748                working_directory: None,
749                detached: true,
750                size: None,
751                environment: None,
752                group_target: None,
753                attach_if_exists: false,
754                detach_other_clients: false,
755                kill_other_clients: false,
756                flags: None,
757                window_name: None,
758                print_session_info: false,
759                print_format: None,
760                command: None,
761                process_command: None,
762                client_environment: None,
763                skip_environment_update: false,
764            }))
765            .command_name(),
766            "new-session"
767        );
768        assert_eq!(
769            Request::SplitWindowExt(Box::new(SplitWindowExtRequest {
770                target: SplitWindowTarget::Pane(pane()),
771                direction: SplitDirection::Vertical,
772                before: false,
773                environment: None,
774                command: None,
775                process_command: None,
776                start_directory: None,
777                keep_alive_on_exit: None,
778                detached: false,
779                size: None,
780                preserve_zoom: false,
781                full_size: false,
782                stdin_payload: None,
783            }))
784            .command_name(),
785            "split-window"
786        );
787        assert_eq!(
788            Request::SelectPaneAdjacent(SelectPaneAdjacentRequest {
789                target: pane(),
790                direction: SelectPaneDirection::Right,
791                preserve_zoom: false,
792            })
793            .command_name(),
794            "select-pane"
795        );
796        assert_eq!(
797            Request::SelectPaneMark(SelectPaneMarkRequest {
798                target: pane(),
799                clear: false,
800                title: None,
801            })
802            .command_name(),
803            "select-pane"
804        );
805        assert_eq!(
806            Request::SetOptionByName(Box::new(SetOptionByNameRequest {
807                scope: OptionScopeSelector::ServerGlobal,
808                name: "status".to_owned(),
809                value: Some("on".to_owned()),
810                mode: SetOptionMode::Replace,
811                only_if_unset: false,
812                unset: false,
813                unset_pane_overrides: false,
814                format: false,
815                format_target: None,
816            }))
817            .command_name(),
818            "set-option"
819        );
820        assert_eq!(
821            Request::SetHookMutation(SetHookMutationRequest {
822                scope: ScopeSelector::Global,
823                hook: crate::HookName::AfterNewSession,
824                command: None,
825                lifecycle: crate::HookLifecycle::Persistent,
826                append: false,
827                unset: true,
828                run_immediately: false,
829                index: None,
830            })
831            .command_name(),
832            "set-hook"
833        );
834        assert_eq!(
835            Request::AttachSessionExt2(Box::new(AttachSessionExt2Request {
836                target: Some(alpha()),
837                target_spec: None,
838                detach_other_clients: false,
839                kill_other_clients: false,
840                read_only: false,
841                skip_environment_update: false,
842                flags: None,
843                working_directory: None,
844                client_terminal: crate::ClientTerminalContext::default(),
845                client_size: None,
846            }))
847            .command_name(),
848            "attach-session"
849        );
850        assert_eq!(
851            Request::AttachSessionExt3(Box::new(AttachSessionExt3Request::from_ext2(
852                AttachSessionExt2Request {
853                    target: Some(alpha()),
854                    target_spec: None,
855                    detach_other_clients: false,
856                    kill_other_clients: false,
857                    read_only: false,
858                    skip_environment_update: false,
859                    flags: None,
860                    working_directory: None,
861                    client_terminal: crate::ClientTerminalContext::default(),
862                    client_size: None,
863                },
864                vec![crate::CAPABILITY_ATTACH_RENDER.to_owned()],
865            )))
866            .command_name(),
867            "attach-session"
868        );
869        assert_eq!(
870            Request::SwitchClientExt3(Box::new(SwitchClientExt3Request {
871                target_client: None,
872                target: Some("alpha:0.0".to_owned()),
873                key_table: None,
874                last_session: false,
875                next_session: false,
876                previous_session: false,
877                toggle_read_only: false,
878                sort_order: None,
879                skip_environment_update: false,
880                zoom: false,
881            }))
882            .command_name(),
883            "switch-client"
884        );
885        assert_eq!(
886            Request::ResolveTarget(ResolveTargetRequest {
887                target: Some("alpha:0.0".to_owned()),
888                target_type: ResolveTargetType::Pane,
889                window_index: false,
890                prefer_unattached: false,
891            })
892            .command_name(),
893            "resolve-target"
894        );
895        assert_eq!(
896            Request::Handshake(HandshakeRequest::current()).command_name(),
897            "handshake"
898        );
899        assert_eq!(
900            Request::SdkWaitForOutput(SdkWaitForOutputRequest {
901                owner_id: SdkWaitOwnerId::new(1),
902                wait_id: SdkWaitId::new(1),
903                target: pane(),
904                bytes: b"ready".to_vec(),
905                start: PaneOutputSubscriptionStart::Now,
906            })
907            .command_name(),
908            "sdk-wait-output"
909        );
910        assert_eq!(
911            Request::CancelSdkWait(CancelSdkWaitRequest {
912                owner_id: SdkWaitOwnerId::new(1),
913                wait_id: SdkWaitId::new(1),
914            })
915            .command_name(),
916            "cancel-sdk-wait"
917        );
918        assert_eq!(
919            Request::WaitFor(WaitForRequest {
920                channel: "ready".to_owned(),
921                mode: WaitForMode::Wait,
922            })
923            .command_name(),
924            "wait-for"
925        );
926    }
927
928    #[test]
929    fn pr6g_request_boxing_keeps_request_size_bounded() {
930        let request_size = size_of::<Request>();
931        let box_size = size_of::<Box<NewSessionExtRequest>>();
932
933        eprintln!("PR6G size baseline: Request={request_size} Box<T>={box_size}");
934        eprintln!(
935            "PR6G candidate sizes: NewSessionExt={} NewWindow={} SetOptionByName={} \
936             AttachSessionExt3={} SwitchClientExt3={} SendKeysExt2={} SourceFile={} WebShare={}",
937            size_of::<NewSessionExtRequest>(),
938            size_of::<NewWindowRequest>(),
939            size_of::<SetOptionByNameRequest>(),
940            size_of::<AttachSessionExt3Request>(),
941            size_of::<SwitchClientExt3Request>(),
942            size_of::<SendKeysExt2Request>(),
943            size_of::<SourceFileRequest>(),
944            size_of::<WebShareRequest>(),
945        );
946        let mut remaining = [
947            ("AttachSessionExt2", size_of::<AttachSessionExt2Request>()),
948            ("BindKey", size_of::<BindKeyRequest>()),
949            ("BreakPane", size_of::<BreakPaneRequest>()),
950            ("CapturePane", size_of::<CapturePaneRequest>()),
951            ("ClearHistory", size_of::<ClearHistoryRequest>()),
952            ("ClockMode", size_of::<ClockModeRequest>()),
953            ("ControlMode", size_of::<ControlModeRequest>()),
954            ("CopyMode", size_of::<CopyModeRequest>()),
955            ("CreateSessionLease", size_of::<CreateSessionLeaseRequest>()),
956            ("DeleteBuffer", size_of::<DeleteBufferRequest>()),
957            ("DetachClientExt", size_of::<DetachClientExtRequest>()),
958            ("DisplayPanes", size_of::<DisplayPanesRequest>()),
959            ("DisplayMessage", size_of::<DisplayMessageRequest>()),
960            ("DisplayMessageExt", size_of::<DisplayMessageExtRequest>()),
961            ("Handshake", size_of::<HandshakeRequest>()),
962            ("IfShell", size_of::<IfShellRequest>()),
963            ("JoinPane", size_of::<JoinPaneRequest>()),
964            ("KillPane", size_of::<KillPaneRequest>()),
965            ("KillWindow", size_of::<KillWindowRequest>()),
966            ("LinkWindow", size_of::<LinkWindowRequest>()),
967            ("ListClients", size_of::<ListClientsRequest>()),
968            ("ListKeys", size_of::<ListKeysRequest>()),
969            ("ListPanes", size_of::<ListPanesRequest>()),
970            ("ListSessions", size_of::<ListSessionsRequest>()),
971            ("ListWindows", size_of::<ListWindowsRequest>()),
972            ("LoadBuffer", size_of::<LoadBufferRequest>()),
973            ("MovePane", size_of::<MovePaneRequest>()),
974            ("MoveWindow", size_of::<MoveWindowRequest>()),
975            ("NewSession", size_of::<NewSessionRequest>()),
976            ("PasteBuffer", size_of::<PasteBufferRequest>()),
977            ("PaneInput", size_of::<PaneInputRequest>()),
978            ("PaneKill", size_of::<PaneKillRequest>()),
979            ("PaneResize", size_of::<PaneResizeRequest>()),
980            ("PaneRespawn", size_of::<PaneRespawnRequest>()),
981            ("PaneSelect", size_of::<PaneSelectRequest>()),
982            ("PaneSnapshot", size_of::<PaneSnapshotRequest>()),
983            ("PaneSnapshotRef", size_of::<PaneSnapshotRefRequest>()),
984            ("PipePane", size_of::<PipePaneRequest>()),
985            ("RefreshClient", size_of::<RefreshClientRequest>()),
986            ("RespawnPane", size_of::<RespawnPaneRequest>()),
987            ("RespawnWindow", size_of::<RespawnWindowRequest>()),
988            ("ResizePane", size_of::<ResizePaneRequest>()),
989            ("ResizeWindow", size_of::<ResizeWindowRequest>()),
990            ("RotateWindow", size_of::<RotateWindowRequest>()),
991            ("RunShell", size_of::<RunShellRequest>()),
992            ("SaveBuffer", size_of::<SaveBufferRequest>()),
993            ("SelectCustomLayout", size_of::<SelectCustomLayoutRequest>()),
994            ("SelectLayout", size_of::<SelectLayoutRequest>()),
995            ("SelectPane", size_of::<SelectPaneRequest>()),
996            ("SelectPaneAdjacent", size_of::<SelectPaneAdjacentRequest>()),
997            ("SelectPaneMark", size_of::<SelectPaneMarkRequest>()),
998            ("SetEnvironment", size_of::<SetEnvironmentRequest>()),
999            ("SetBuffer", size_of::<SetBufferRequest>()),
1000            ("SetHook", size_of::<SetHookRequest>()),
1001            ("SetHookMutation", size_of::<SetHookMutationRequest>()),
1002            ("SetOption", size_of::<SetOptionRequest>()),
1003            ("ShowBuffer", size_of::<ShowBufferRequest>()),
1004            ("ShowEnvironment", size_of::<ShowEnvironmentRequest>()),
1005            ("ShowHooks", size_of::<ShowHooksRequest>()),
1006            ("ShowMessages", size_of::<ShowMessagesRequest>()),
1007            ("ShowOptions", size_of::<ShowOptionsRequest>()),
1008            ("SplitWindow", size_of::<SplitWindowRequest>()),
1009            ("SplitWindowExt", size_of::<SplitWindowExtRequest>()),
1010            (
1011                "SubscribePaneOutput",
1012                size_of::<SubscribePaneOutputRequest>(),
1013            ),
1014            (
1015                "SubscribePaneOutputRef",
1016                size_of::<SubscribePaneOutputRefRequest>(),
1017            ),
1018            ("SwitchClientExt2", size_of::<SwitchClientExt2Request>()),
1019        ];
1020        remaining.sort_by_key(|(_, size)| std::cmp::Reverse(*size));
1021        eprintln!("PR6G largest remaining candidates: {remaining:?}");
1022
1023        assert!(
1024            request_size <= 96,
1025            "Request grew past the PR6G boxed payload budget: {request_size}"
1026        );
1027        assert_eq!(box_size, size_of::<usize>());
1028    }
1029
1030    #[test]
1031    fn pr6g_boxed_payloads_are_bincode_transparent_for_request_candidates() {
1032        assert_box_serializes_like_value(NewSessionExtRequest {
1033            session_name: Some(alpha()),
1034            working_directory: Some("/tmp".to_owned()),
1035            detached: true,
1036            size: Some(crate::TerminalSize {
1037                cols: 120,
1038                rows: 40,
1039            }),
1040            environment: Some(vec!["A=B".to_owned()]),
1041            group_target: None,
1042            attach_if_exists: false,
1043            detach_other_clients: false,
1044            kill_other_clients: false,
1045            flags: Some(vec!["read-only".to_owned()]),
1046            window_name: Some("main".to_owned()),
1047            print_session_info: true,
1048            print_format: Some("#{session_name}".to_owned()),
1049            command: Some(vec!["sh".to_owned(), "-lc".to_owned(), "true".to_owned()]),
1050            process_command: None,
1051            client_environment: Some(vec!["PATH=/bin".to_owned()]),
1052            skip_environment_update: false,
1053        });
1054        assert_box_serializes_like_value(SetOptionByNameRequest {
1055            scope: OptionScopeSelector::ServerGlobal,
1056            name: "@plugin".to_owned(),
1057            value: Some("enabled".to_owned()),
1058            mode: SetOptionMode::Replace,
1059            only_if_unset: false,
1060            unset: false,
1061            unset_pane_overrides: false,
1062            format: true,
1063            format_target: Some(crate::Target::Session(alpha())),
1064        });
1065        assert_box_serializes_like_value(ListWindowsRequest {
1066            target: alpha(),
1067            format: Some("#{window_index}".to_owned()),
1068            filter: Some("#{==:#{window_active},1}".to_owned()),
1069            sort_order: Some("index".to_owned()),
1070            reversed: true,
1071        });
1072        assert_box_serializes_like_value(ListPanesRequest {
1073            target: alpha(),
1074            target_window_index: Some(1),
1075            format: Some("#{pane_index}".to_owned()),
1076            filter: Some("#{==:#{pane_active},1}".to_owned()),
1077            sort_order: Some("index".to_owned()),
1078            reversed: true,
1079        });
1080        assert_box_serializes_like_value(DisplayPanesRequest {
1081            target: alpha(),
1082            duration_ms: Some(1_000),
1083            non_blocking: true,
1084            no_command: false,
1085            template: Some("select-pane -t '%%'".to_owned()),
1086            target_client: Some("/dev/pts/1".to_owned()),
1087        });
1088        assert_box_serializes_like_value(SetBufferRequest {
1089            name: Some("named".to_owned()),
1090            content: b"buffer".to_vec(),
1091            append: false,
1092            new_name: None,
1093            set_clipboard: true,
1094            target_client: Some("/dev/pts/1".to_owned()),
1095        });
1096        assert_box_serializes_like_value(LoadBufferRequest {
1097            path: "/tmp/input".to_owned(),
1098            cwd: Some(PathBuf::from("/tmp")),
1099            name: Some("loaded".to_owned()),
1100            set_clipboard: true,
1101            target_client: Some("/dev/pts/1".to_owned()),
1102        });
1103        assert_box_serializes_like_value(AttachSessionExt3Request::from_ext2(
1104            AttachSessionExt2Request {
1105                target: Some(alpha()),
1106                target_spec: Some("alpha:0.0".to_owned()),
1107                detach_other_clients: false,
1108                kill_other_clients: false,
1109                read_only: true,
1110                skip_environment_update: false,
1111                flags: Some(vec!["active-pane".to_owned()]),
1112                working_directory: Some("/tmp".to_owned()),
1113                client_terminal: crate::ClientTerminalContext::default(),
1114                client_size: Some(crate::TerminalSize { cols: 80, rows: 24 }),
1115            },
1116            vec![crate::CAPABILITY_ATTACH_RENDER.to_owned()],
1117        ));
1118        assert_box_serializes_like_value(WebShareRequest::Create(CreateWebShareRequest {
1119            scope: WebShareScope::Session(alpha()),
1120            public_base_url: Some("https://example.invalid".to_owned()),
1121            tunnel_provider: None,
1122            frontend_url: Some("https://share.example.invalid".to_owned()),
1123            ttl_seconds: Some(60),
1124            expires_at_unix: None,
1125            max_spectators: Some(8),
1126            max_operators: Some(1),
1127            url_options: WebShareUrlOptions::default(),
1128            require_pin: true,
1129            operator_pin: Some("123456".to_owned()),
1130            spectator_pin: Some("654321".to_owned()),
1131            terminal_palette: None,
1132            operator: true,
1133            spectator: true,
1134            controls: true,
1135            kill_session_on_expire: false,
1136        }));
1137    }
1138
1139    fn assert_box_serializes_like_value<T>(value: T)
1140    where
1141        T: Clone + Serialize,
1142    {
1143        let boxed = Box::new(value.clone());
1144        assert_eq!(
1145            bincode::serialize(&value).expect("value encodes"),
1146            bincode::serialize(&boxed).expect("boxed value encodes"),
1147            "Box<T> must be transparent before PR6G boxes enum variants"
1148        );
1149    }
1150}