Module: Terminalwire::V2::Frames

Defined in:
lib/terminalwire/v2/frames.rb

Overview

Builders for each frame type. Keep frame construction in one place so the wire shape is defined once. All builders return a Hash with string keys.

Constant Summary collapse

DEFAULT_FLOW =

The client's initial flow-control offer: how many bytes of output it will accept per stream before the server must wait for a window_adjust.

{ "window" => Protocol::DEFAULT_WINDOW }.freeze
DEFAULT_TERMINAL =

The client's terminal at connect time (structured per TERMINAL.md: per-stream kinds + a device block); resize/mode frames update the device thereafter.

{
  "stdin" => { "kind" => "tty" },
  "stdout" => { "kind" => "tty" },
  "stderr" => { "kind" => "tty" },
  "device" => {
    "cols" => 80, "rows" => 24, "xpixels" => 0, "ypixels" => 0,
    "term" => "", "color" => "none", "encoding" => "UTF-8", "mode" => "cooked"
  }
}.freeze

Class Method Summary collapse

Class Method Details

.close(sid:) ⇒ Object



85
86
87
# File 'lib/terminalwire/v2/frames.rb', line 85

def close(sid:)
  { "t" => Protocol::Type::CLOSE, "sid" => sid }
end

.data(sid:, bytes:) ⇒ Object



81
82
83
# File 'lib/terminalwire/v2/frames.rb', line 81

def data(sid:, bytes:)
  { "t" => Protocol::Type::DATA, "sid" => sid, "bytes" => bytes.b }
end

.exit(status:) ⇒ Object



71
72
73
# File 'lib/terminalwire/v2/frames.rb', line 71

def exit(status:)
  { "t" => Protocol::Type::EXIT, "sid" => Protocol::CONTROL_SID, "status" => status }
end

.hello(protocol:, capabilities:, program:, entitlement:, terminal: DEFAULT_TERMINAL, flow: DEFAULT_FLOW) ⇒ Object



9
10
11
12
13
14
15
16
# File 'lib/terminalwire/v2/frames.rb', line 9

def hello(protocol:, capabilities:, program:, entitlement:, terminal: DEFAULT_TERMINAL, flow: DEFAULT_FLOW)
  {
    "t" => Protocol::Type::HELLO, "sid" => Protocol::CONTROL_SID,
    "protocol" => protocol, "capabilities" => capabilities,
    "program" => program, "entitlement" => entitlement,
    "terminal" => terminal, "flow" => flow
  }
end

.incompatible(supported:, message:) ⇒ Object



60
61
62
63
64
65
66
67
68
69
# File 'lib/terminalwire/v2/frames.rb', line 60

def incompatible(supported:, message:)
  # Normalize to string keys so every wire frame is uniformly string-keyed
  # (the negotiator hands us a symbol-keyed Ruby hash).
  min = supported[:min] || supported["min"]
  max = supported[:max] || supported["max"]
  {
    "t" => Protocol::Type::INCOMPATIBLE, "sid" => Protocol::CONTROL_SID,
    "supported" => { "min" => min, "max" => max }, "message" => message
  }
end

.interruptObject



49
50
51
# File 'lib/terminalwire/v2/frames.rb', line 49

def interrupt
  signal(Protocol::Signal::INTERRUPT)
end

.open(sid:, stream:, mode: nil) ⇒ Object



75
76
77
78
79
# File 'lib/terminalwire/v2/frames.rb', line 75

def open(sid:, stream:, mode: nil)
  frame = { "t" => Protocol::Type::OPEN, "sid" => sid, "stream" => stream }
  frame["mode"] = mode if mode # input streams carry the line-discipline mode
  frame
end

.request(sid:, resource:, method:, params: {}) ⇒ Object



89
90
91
92
93
94
# File 'lib/terminalwire/v2/frames.rb', line 89

def request(sid:, resource:, method:, params: {})
  {
    "t" => Protocol::Type::REQUEST, "sid" => sid,
    "resource" => resource, "method" => method, "params" => params
  }
end

.resize(cols:, rows:) ⇒ Object



45
46
47
# File 'lib/terminalwire/v2/frames.rb', line 45

def resize(cols:, rows:)
  signal(Protocol::Signal::RESIZE, { "cols" => cols, "rows" => rows })
end

.response_error(sid:, code:, message:) ⇒ Object



100
101
102
103
104
105
# File 'lib/terminalwire/v2/frames.rb', line 100

def response_error(sid:, code:, message:)
  {
    "t" => Protocol::Type::RESPONSE, "sid" => sid, "ok" => false,
    "error" => { "code" => code, "message" => message }
  }
end

.response_ok(sid:, value:) ⇒ Object



96
97
98
# File 'lib/terminalwire/v2/frames.rb', line 96

def response_ok(sid:, value:)
  { "t" => Protocol::Type::RESPONSE, "sid" => sid, "ok" => true, "value" => value }
end

.signal(name, payload = {}) ⇒ Object

Generic async terminal signal (client -> server). resize/interrupt are the named variants; collapsing them into one frame type keeps the protocol small.



41
42
43
# File 'lib/terminalwire/v2/frames.rb', line 41

def signal(name, payload = {})
  { "t" => Protocol::Type::SIGNAL, "sid" => Protocol::CONTROL_SID, "name" => name }.merge(payload)
end

.welcome(protocol:, capabilities:) ⇒ Object



53
54
55
56
57
58
# File 'lib/terminalwire/v2/frames.rb', line 53

def welcome(protocol:, capabilities:)
  {
    "t" => Protocol::Type::WELCOME, "sid" => Protocol::CONTROL_SID,
    "protocol" => protocol, "capabilities" => capabilities
  }
end

.window_adjust(sid:, bytes:) ⇒ Object

Client -> server: extend the output window for a stream by bytes.



23
24
25
# File 'lib/terminalwire/v2/frames.rb', line 23

def window_adjust(sid:, bytes:)
  { "t" => Protocol::Type::WINDOW_ADJUST, "sid" => sid, "bytes" => bytes }
end