herdr-remote 0.2.39 → 0.3.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +4 -2
  2. package/README.zh-CN.md +2 -2
  3. package/dist/chunks/3FQAXVGC.js +3 -0
  4. package/dist/chunks/42KARAL4.js +31 -0
  5. package/dist/chunks/44IGVPMA.js +5 -0
  6. package/dist/chunks/5LZFJ6UZ.js +3 -0
  7. package/dist/chunks/7SMZ43LI.js +9 -0
  8. package/dist/chunks/AX4EPJCP.js +4 -0
  9. package/dist/chunks/B76DFI4H.js +16 -0
  10. package/dist/chunks/BYGDPZZD.js +3 -0
  11. package/dist/chunks/C2UUZY6J.js +2 -0
  12. package/dist/chunks/CVJ4HBMP.js +3 -0
  13. package/dist/chunks/DGUZCGQN.js +3 -0
  14. package/dist/chunks/FONUO5O5.js +4 -0
  15. package/dist/chunks/H24ZP5D5.js +16 -0
  16. package/dist/chunks/I6NRCYLI.js +3 -0
  17. package/dist/chunks/JJGEV4EZ.js +3 -0
  18. package/dist/chunks/K5RFPHSF.js +5 -0
  19. package/dist/chunks/KGOQKOHH.js +5 -0
  20. package/dist/chunks/KRVF37OW.js +3 -0
  21. package/dist/chunks/KUDCC7ZO.js +6 -0
  22. package/dist/chunks/LPR662JH.js +77 -0
  23. package/dist/chunks/LY2ZGCXN.js +4 -0
  24. package/dist/chunks/M52LJEES.js +6 -0
  25. package/dist/chunks/MCUVFSSK.js +3 -0
  26. package/dist/chunks/MEFDV375.js +10 -0
  27. package/dist/chunks/MKAEXKXG.js +10 -0
  28. package/dist/chunks/O2LNVFUT.js +3 -0
  29. package/dist/chunks/PBMBFWLQ.js +3 -0
  30. package/dist/chunks/PBRQ3HEG.js +3 -0
  31. package/dist/chunks/QCSILMFB.js +3 -0
  32. package/dist/chunks/QRGNEKME.js +5 -0
  33. package/dist/chunks/RMHH3IAX.js +63 -0
  34. package/dist/chunks/TNLCNVA2.js +3 -0
  35. package/dist/chunks/U6QAYG3E.js +3 -0
  36. package/dist/chunks/VD54DF7R.js +45 -0
  37. package/dist/chunks/WY7WHEJ4.js +3 -0
  38. package/dist/cli.js +21 -294
  39. package/dist/connector/admin-broker.js +3 -303
  40. package/dist/connector/main.js +20 -18
  41. package/dist/harfbuzz-subset.wasm +0 -0
  42. package/dist/postinstall.js +17 -0
  43. package/herdr-plugin.toml +1 -1
  44. package/node_modules/herdr-remote-relay/bin/herdr-remote-relay.js +33 -92
  45. package/node_modules/herdr-remote-relay/package.json +3 -46
  46. package/package.json +8 -27
  47. package/dist/agent-status.js +0 -130
  48. package/dist/binary-plist.js +0 -157
  49. package/dist/config.js +0 -278
  50. package/dist/connector/admin-broker-setup.js +0 -40
  51. package/dist/connector/admin-shell.js +0 -67
  52. package/dist/connector/admin-tab.js +0 -90
  53. package/dist/connector/adminBrokerEndpoint.js +0 -68
  54. package/dist/connector/agent-watch.js +0 -123
  55. package/dist/connector/elevatedPty.js +0 -141
  56. package/dist/connector/font-server.js +0 -165
  57. package/dist/connector/herdr-prefix-keys.js +0 -28
  58. package/dist/connector/host-connector.js +0 -433
  59. package/dist/connector/host-hello.js +0 -32
  60. package/dist/connector/lock.js +0 -73
  61. package/dist/connector/sessions.js +0 -381
  62. package/dist/connector/update-report.js +0 -70
  63. package/dist/connector/wire.js +0 -42
  64. package/dist/exit-codes.js +0 -13
  65. package/dist/font-subset.js +0 -116
  66. package/dist/herdr-api.js +0 -254
  67. package/dist/herdr-command.js +0 -245
  68. package/dist/herdr-plugin.js +0 -120
  69. package/dist/herdr-server.js +0 -190
  70. package/dist/i18n/en.js +0 -257
  71. package/dist/i18n/index.js +0 -48
  72. package/dist/i18n/zh.js +0 -256
  73. package/dist/keepalive/detached.js +0 -97
  74. package/dist/keepalive/environment.js +0 -44
  75. package/dist/keepalive/index.js +0 -129
  76. package/dist/keepalive/launchd.js +0 -117
  77. package/dist/keepalive/systemd.js +0 -112
  78. package/dist/keepalive/types.js +0 -1
  79. package/dist/keepalive/windows-service-setup.js +0 -138
  80. package/dist/keepalive/windows-service.js +0 -272
  81. package/dist/keepalive/windows-task.js +0 -191
  82. package/dist/lib/process.js +0 -132
  83. package/dist/lifecycle.js +0 -79
  84. package/dist/net-interfaces.js +0 -82
  85. package/dist/node-pty-loader.js +0 -80
  86. package/dist/pasted-files.js +0 -115
  87. package/dist/paths.js +0 -74
  88. package/dist/pty-session.js +0 -133
  89. package/dist/relay-client.js +0 -106
  90. package/dist/relay-urls.js +0 -99
  91. package/dist/runtime.js +0 -107
  92. package/dist/service.js +0 -267
  93. package/dist/settings-model.js +0 -298
  94. package/dist/socket-discovery.js +0 -70
  95. package/dist/supervisor.js +0 -323
  96. package/dist/terminal-font/deps.js +0 -83
  97. package/dist/terminal-font/files.js +0 -357
  98. package/dist/terminal-font/formats.js +0 -355
  99. package/dist/terminal-font/index.js +0 -187
  100. package/dist/terminal-font/readers.js +0 -373
  101. package/dist/terminal-font/terminals.js +0 -147
  102. package/dist/terminal-palette.js +0 -399
  103. package/dist/tui/App.js +0 -199
  104. package/dist/tui/api.js +0 -91
  105. package/dist/tui/components/TextField.js +0 -50
  106. package/dist/tui/components/common.js +0 -80
  107. package/dist/tui/index.js +0 -36
  108. package/dist/tui/mouse/index.js +0 -164
  109. package/dist/tui/mouse/protocol.js +0 -96
  110. package/dist/tui/mouse/source.js +0 -79
  111. package/dist/tui/screens/About.js +0 -172
  112. package/dist/tui/screens/Herdr.js +0 -75
  113. package/dist/tui/screens/Keepalive.js +0 -141
  114. package/dist/tui/screens/Overview.js +0 -36
  115. package/dist/tui/screens/Pair.js +0 -74
  116. package/dist/tui/screens/Relay.js +0 -243
  117. package/dist/tui/screens/Services.js +0 -63
  118. package/dist/tui/screens/Wizard.js +0 -241
  119. package/dist/tui/terminal.js +0 -39
  120. package/dist/tui/theme.js +0 -33
  121. package/dist/update-command.js +0 -118
  122. package/dist/updater/in-place.js +0 -83
  123. package/dist/updater/npm.js +0 -62
  124. package/dist/updater.js +0 -401
  125. package/node_modules/herdr-remote-relay/README.md +0 -76
  126. package/node_modules/herdr-remote-relay/README.zh-CN.md +0 -76
  127. package/node_modules/herdr-remote-relay/config.example.json +0 -18
  128. package/node_modules/herdr-remote-relay/deploy/cloudflared/config.yml.example +0 -9
  129. package/node_modules/herdr-remote-relay/deploy/docker/compose.yaml +0 -23
  130. package/node_modules/herdr-remote-relay/deploy/nginx/herdr-remote-relay.conf.example +0 -49
  131. package/node_modules/herdr-remote-relay/deploy/systemd/herdr-remote-relay.service +0 -41
  132. package/node_modules/herdr-remote-relay/deploy/systemd/relay.env.example +0 -25
  133. package/node_modules/herdr-remote-relay/dist/auth-store.d.ts +0 -114
  134. package/node_modules/herdr-remote-relay/dist/auth-store.js +0 -289
  135. package/node_modules/herdr-remote-relay/dist/metrics.d.ts +0 -76
  136. package/node_modules/herdr-remote-relay/dist/metrics.js +0 -249
  137. package/node_modules/herdr-remote-relay/dist/protocol/frames.d.ts +0 -27
  138. package/node_modules/herdr-remote-relay/dist/protocol/frames.js +0 -110
  139. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.d.ts +0 -8
  140. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.js +0 -34
  141. package/node_modules/herdr-remote-relay/dist/protocol/http.d.ts +0 -165
  142. package/node_modules/herdr-remote-relay/dist/protocol/http.js +0 -3
  143. package/node_modules/herdr-remote-relay/dist/protocol/index.d.ts +0 -7
  144. package/node_modules/herdr-remote-relay/dist/protocol/index.js +0 -9
  145. package/node_modules/herdr-remote-relay/dist/protocol/messages.d.ts +0 -362
  146. package/node_modules/herdr-remote-relay/dist/protocol/messages.js +0 -20
  147. package/node_modules/herdr-remote-relay/dist/protocol/paste.d.ts +0 -14
  148. package/node_modules/herdr-remote-relay/dist/protocol/paste.js +0 -50
  149. package/node_modules/herdr-remote-relay/dist/protocol/release.d.ts +0 -8
  150. package/node_modules/herdr-remote-relay/dist/protocol/release.js +0 -17
  151. package/node_modules/herdr-remote-relay/dist/protocol/terminal.d.ts +0 -79
  152. package/node_modules/herdr-remote-relay/dist/protocol/terminal.js +0 -150
  153. package/node_modules/herdr-remote-relay/dist/relay-config.d.ts +0 -74
  154. package/node_modules/herdr-remote-relay/dist/relay-config.js +0 -316
  155. package/node_modules/herdr-remote-relay/dist/relay-server.d.ts +0 -67
  156. package/node_modules/herdr-remote-relay/dist/relay-server.js +0 -196
  157. package/node_modules/herdr-remote-relay/dist/scroll-input.d.ts +0 -17
  158. package/node_modules/herdr-remote-relay/dist/scroll-input.js +0 -124
  159. package/node_modules/herdr-remote-relay/dist/server/client-channel.d.ts +0 -3
  160. package/node_modules/herdr-remote-relay/dist/server/client-channel.js +0 -396
  161. package/node_modules/herdr-remote-relay/dist/server/font-proxy.d.ts +0 -19
  162. package/node_modules/herdr-remote-relay/dist/server/font-proxy.js +0 -155
  163. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.d.ts +0 -27
  164. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.js +0 -83
  165. package/node_modules/herdr-remote-relay/dist/server/host-channel.d.ts +0 -3
  166. package/node_modules/herdr-remote-relay/dist/server/host-channel.js +0 -271
  167. package/node_modules/herdr-remote-relay/dist/server/http-api.d.ts +0 -4
  168. package/node_modules/herdr-remote-relay/dist/server/http-api.js +0 -219
  169. package/node_modules/herdr-remote-relay/dist/server/maintenance.d.ts +0 -5
  170. package/node_modules/herdr-remote-relay/dist/server/maintenance.js +0 -64
  171. package/node_modules/herdr-remote-relay/dist/server/requests.d.ts +0 -34
  172. package/node_modules/herdr-remote-relay/dist/server/requests.js +0 -142
  173. package/node_modules/herdr-remote-relay/dist/server/sessions.d.ts +0 -36
  174. package/node_modules/herdr-remote-relay/dist/server/sessions.js +0 -230
  175. package/node_modules/herdr-remote-relay/dist/server/sockets.d.ts +0 -12
  176. package/node_modules/herdr-remote-relay/dist/server/sockets.js +0 -64
  177. package/node_modules/herdr-remote-relay/dist/server/status.d.ts +0 -11
  178. package/node_modules/herdr-remote-relay/dist/server/status.js +0 -99
  179. package/node_modules/herdr-remote-relay/dist/server/transport.d.ts +0 -16
  180. package/node_modules/herdr-remote-relay/dist/server/transport.js +0 -110
  181. package/node_modules/herdr-remote-relay/dist/server/types.d.ts +0 -133
  182. package/node_modules/herdr-remote-relay/dist/server/types.js +0 -3
  183. package/node_modules/herdr-remote-relay/dist/server/version.d.ts +0 -2
  184. package/node_modules/herdr-remote-relay/dist/server/version.js +0 -5
  185. package/node_modules/herdr-remote-relay/dist/state.d.ts +0 -6
  186. package/node_modules/herdr-remote-relay/dist/state.js +0 -47
  187. package/node_modules/ws/LICENSE +0 -20
  188. package/node_modules/ws/README.md +0 -548
  189. package/node_modules/ws/browser.js +0 -8
  190. package/node_modules/ws/index.js +0 -22
  191. package/node_modules/ws/lib/buffer-util.js +0 -131
  192. package/node_modules/ws/lib/constants.js +0 -19
  193. package/node_modules/ws/lib/event-target.js +0 -292
  194. package/node_modules/ws/lib/extension.js +0 -203
  195. package/node_modules/ws/lib/limiter.js +0 -55
  196. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  197. package/node_modules/ws/lib/receiver.js +0 -743
  198. package/node_modules/ws/lib/sender.js +0 -607
  199. package/node_modules/ws/lib/stream.js +0 -161
  200. package/node_modules/ws/lib/subprotocol.js +0 -62
  201. package/node_modules/ws/lib/validation.js +0 -152
  202. package/node_modules/ws/lib/websocket-server.js +0 -562
  203. package/node_modules/ws/lib/websocket.js +0 -1407
  204. package/node_modules/ws/package.json +0 -74
  205. package/node_modules/ws/wrapper.mjs +0 -21
@@ -1,249 +0,0 @@
1
- import os from 'node:os';
2
- import { monitorEventLoopDelay } from 'node:perf_hooks';
3
- function finite(value, fallback = 0) {
4
- return Number.isFinite(value) ? value : fallback;
5
- }
6
- function bytesPerSecond(current, previous, elapsedMs) {
7
- if (!elapsedMs || elapsedMs <= 0)
8
- return 0;
9
- return Math.max(0, ((current - previous) * 1000) / elapsedMs);
10
- }
11
- /**
12
- * How many people are attached, rather than how many sockets are open.
13
- *
14
- * A phone and a laptop belonging to one person are two connections but one
15
- * paired device each, and one person opening three browser windows is three
16
- * connections and one device — so the distinct paired device is the closest
17
- * thing the relay has to a user. Connections that predate device pairing carry
18
- * no `deviceId`; they fall back to their own connection id so they still count
19
- * once instead of collapsing into a single anonymous user.
20
- */
21
- function countActiveUsers(clients = []) {
22
- const identities = new Set();
23
- for (const client of clients) {
24
- if (!client)
25
- continue;
26
- identities.add(typeof client.deviceId === 'string' && client.deviceId
27
- ? `device:${client.deviceId}`
28
- : `client:${client.id}`);
29
- }
30
- return identities.size;
31
- }
32
- class RelayMetrics {
33
- version;
34
- protocolVersion;
35
- startedAt;
36
- bytesIn;
37
- bytesOut;
38
- framesIn;
39
- framesOut;
40
- cleanup;
41
- lastSample;
42
- hostTraffic;
43
- cpuSampleAt;
44
- cpuSample;
45
- eventLoop;
46
- constructor({ version = '0.1.0', protocolVersion = 1 } = {}) {
47
- this.version = version;
48
- this.protocolVersion = protocolVersion;
49
- this.startedAt = Date.now();
50
- this.bytesIn = 0;
51
- this.bytesOut = 0;
52
- this.framesIn = 0;
53
- this.framesOut = 0;
54
- this.cleanup = {
55
- staleClientsPurged: 0,
56
- closedPtysCleaned: 0,
57
- deadConnectionsClosed: 0,
58
- idleHostsTerminated: 0,
59
- slowClientsDropped: 0,
60
- lastCleanupAt: null,
61
- };
62
- this.lastSample = { at: this.startedAt, bytesIn: 0, bytesOut: 0, framesIn: 0, framesOut: 0 };
63
- this.hostTraffic = new Map();
64
- this.cpuSampleAt = process.hrtime.bigint();
65
- this.cpuSample = process.cpuUsage();
66
- this.eventLoop = monitorEventLoopDelay({ resolution: 20 });
67
- this.eventLoop.enable();
68
- }
69
- hostCounter(hostId) {
70
- if (typeof hostId !== 'string' || hostId.length === 0)
71
- return null;
72
- let counter = this.hostTraffic.get(hostId);
73
- if (!counter) {
74
- counter = {
75
- bytesIn: 0,
76
- bytesOut: 0,
77
- framesIn: 0,
78
- framesOut: 0,
79
- sampleAt: Date.now(),
80
- sampleBytesIn: 0,
81
- sampleBytesOut: 0,
82
- sampleFramesIn: 0,
83
- sampleFramesOut: 0,
84
- };
85
- this.hostTraffic.set(hostId, counter);
86
- }
87
- return counter;
88
- }
89
- recordIn(bytes, hostId = null) {
90
- const amount = Math.max(0, Number(bytes) || 0);
91
- this.bytesIn += amount;
92
- this.framesIn += 1;
93
- const counter = this.hostCounter(hostId);
94
- if (counter) {
95
- counter.bytesIn += amount;
96
- counter.framesIn += 1;
97
- }
98
- }
99
- recordOut(bytes, hostId = null) {
100
- const amount = Math.max(0, Number(bytes) || 0);
101
- this.bytesOut += amount;
102
- this.framesOut += 1;
103
- const counter = this.hostCounter(hostId);
104
- if (counter) {
105
- counter.bytesOut += amount;
106
- counter.framesOut += 1;
107
- }
108
- }
109
- forgetHost(hostId) {
110
- if (typeof hostId === 'string')
111
- this.hostTraffic.delete(hostId);
112
- }
113
- hostThroughput(hostId, now = Date.now()) {
114
- const counter = this.hostCounter(hostId);
115
- if (!counter)
116
- return {
117
- bytesIn: 0,
118
- bytesOut: 0,
119
- bytesInPerSec: 0,
120
- bytesOutPerSec: 0,
121
- framesIn: 0,
122
- framesOut: 0,
123
- framesInPerSec: 0,
124
- framesOutPerSec: 0,
125
- };
126
- const elapsedMs = now - counter.sampleAt;
127
- const throughput = {
128
- bytesIn: counter.bytesIn,
129
- bytesOut: counter.bytesOut,
130
- bytesInPerSec: bytesPerSecond(counter.bytesIn, counter.sampleBytesIn, elapsedMs),
131
- bytesOutPerSec: bytesPerSecond(counter.bytesOut, counter.sampleBytesOut, elapsedMs),
132
- framesIn: counter.framesIn,
133
- framesOut: counter.framesOut,
134
- framesInPerSec: bytesPerSecond(counter.framesIn, counter.sampleFramesIn, elapsedMs),
135
- framesOutPerSec: bytesPerSecond(counter.framesOut, counter.sampleFramesOut, elapsedMs),
136
- };
137
- counter.sampleAt = now;
138
- counter.sampleBytesIn = counter.bytesIn;
139
- counter.sampleBytesOut = counter.bytesOut;
140
- counter.sampleFramesIn = counter.framesIn;
141
- counter.sampleFramesOut = counter.framesOut;
142
- return throughput;
143
- }
144
- recordCleanup(name, amount = 1) {
145
- if (Object.hasOwn(this.cleanup, name) && typeof this.cleanup[name] === 'number') {
146
- this.cleanup[name] += amount;
147
- }
148
- this.cleanup.lastCleanupAt = new Date().toISOString();
149
- }
150
- cpuPercent() {
151
- const now = process.hrtime.bigint();
152
- const elapsedNs = Number(now - this.cpuSampleAt);
153
- const usage = process.cpuUsage(this.cpuSample);
154
- this.cpuSampleAt = now;
155
- this.cpuSample = process.cpuUsage();
156
- if (elapsedNs <= 0)
157
- return 0;
158
- const percent = ((usage.user + usage.system) * 100000) / elapsedNs;
159
- return Math.max(0, Math.round(percent * 100) / 100);
160
- }
161
- eventLoopDelay() {
162
- const percentile = (value) => Math.round(finite(value / 1e6) * 100) / 100;
163
- return {
164
- p50Ms: percentile(this.eventLoop.percentile(50)),
165
- p90Ms: percentile(this.eventLoop.percentile(90)),
166
- p99Ms: percentile(this.eventLoop.percentile(99)),
167
- maxMs: percentile(this.eventLoop.max),
168
- meanMs: percentile(this.eventLoop.mean),
169
- };
170
- }
171
- /**
172
- * `activeUserCount` is passed in rather than derived from `clients`: the
173
- * public `/api/status` rows deliberately omit `deviceId`, so only the caller
174
- * still holding the full connection records can count devices correctly.
175
- * Omitting it falls back to counting whatever the given rows identify.
176
- */
177
- snapshot({ clients = [], hosts = [], ptys = [], sample = true, scopeHostId = null, activeUserCount = null, } = {}) {
178
- const now = Date.now();
179
- const elapsedMs = now - this.lastSample.at;
180
- const throughput = {
181
- bytesIn: this.bytesIn,
182
- bytesOut: this.bytesOut,
183
- bytesInPerSec: bytesPerSecond(this.bytesIn, this.lastSample.bytesIn, elapsedMs),
184
- bytesOutPerSec: bytesPerSecond(this.bytesOut, this.lastSample.bytesOut, elapsedMs),
185
- framesIn: this.framesIn,
186
- framesOut: this.framesOut,
187
- framesInPerSec: bytesPerSecond(this.framesIn, this.lastSample.framesIn, elapsedMs),
188
- framesOutPerSec: bytesPerSecond(this.framesOut, this.lastSample.framesOut, elapsedMs),
189
- };
190
- if (sample) {
191
- this.lastSample = {
192
- at: now,
193
- bytesIn: this.bytesIn,
194
- bytesOut: this.bytesOut,
195
- framesIn: this.framesIn,
196
- framesOut: this.framesOut,
197
- };
198
- }
199
- const memory = process.memoryUsage();
200
- const load = os.loadavg();
201
- // Every attached window has full input, so there is no "active controller"
202
- // to name: reporting the first of them as one had an operator reading a
203
- // distinction that does not exist. The host is still worth naming, and any
204
- // client knows which workstation it is on.
205
- const anyClient = clients[0];
206
- const scopedThroughput = scopeHostId ? this.hostThroughput(scopeHostId, now) : throughput;
207
- return {
208
- version: this.version,
209
- protocolVersion: this.protocolVersion,
210
- uptimeSeconds: Math.floor((now - this.startedAt) / 1000),
211
- startTime: new Date(this.startedAt).toISOString(),
212
- serverTime: new Date(now).toISOString(),
213
- activeControllerId: null,
214
- activeHostId: anyClient?.hostId || hosts[0]?.id || null,
215
- clients,
216
- hosts,
217
- ptys,
218
- clientCount: clients.length,
219
- hostCount: hosts.length,
220
- ptyCount: ptys.length,
221
- activeUserCount: Number.isFinite(activeUserCount)
222
- ? activeUserCount
223
- : countActiveUsers(clients),
224
- throughput: scopedThroughput,
225
- cpu: {
226
- load1m: finite(load[0]),
227
- load5m: finite(load[1]),
228
- load15m: finite(load[2]),
229
- cpuPercent: this.cpuPercent(),
230
- cores: os.cpus().length,
231
- },
232
- memory: {
233
- rssBytes: memory.rss,
234
- heapUsedBytes: memory.heapUsed,
235
- heapTotalBytes: memory.heapTotal,
236
- externalBytes: memory.external,
237
- systemTotalBytes: os.totalmem(),
238
- systemFreeBytes: os.freemem(),
239
- },
240
- eventLoopDelay: this.eventLoopDelay(),
241
- cleanup: { ...this.cleanup },
242
- };
243
- }
244
- close() {
245
- this.eventLoop.disable();
246
- this.hostTraffic.clear();
247
- }
248
- }
249
- export { RelayMetrics, countActiveUsers };
@@ -1,27 +0,0 @@
1
- export declare const PROTOCOL_VERSION = 1;
2
- export declare const MAX_HEADER_BYTES: number;
3
- export declare const FRAME_V2_MAGIC = 255;
4
- export declare const FRAME_TYPE_OUTPUT = 0;
5
- export declare const FRAME_TYPE_INPUT = 1;
6
- export type FrameTypeCode = typeof FRAME_TYPE_OUTPUT | typeof FRAME_TYPE_INPUT;
7
- export type FrameBytes = Buffer | Uint8Array | ArrayBuffer | readonly number[] | string;
8
- /** A v1 frame routes by a JSON header naming the stream. */
9
- export interface StreamFrameV1 {
10
- version: 1;
11
- type: string;
12
- streamId: string;
13
- payload: Buffer;
14
- }
15
- /** A v2 frame routes by a 16-bit stream index. */
16
- export interface StreamFrameV2 {
17
- version: 2;
18
- type: 'output' | 'input';
19
- typeCode: FrameTypeCode;
20
- streamIndex: number;
21
- payload: Buffer;
22
- }
23
- export type StreamFrame = StreamFrameV1 | StreamFrameV2;
24
- export declare function packStreamFrame(type: string, streamId: string, payload?: FrameBytes): Buffer;
25
- export declare function packStreamFrameV2(type: 'output' | 'input' | FrameTypeCode, streamIndex: number, payload?: FrameBytes): Buffer;
26
- export declare function unpackStreamFrameV2(buffer: FrameBytes): StreamFrameV2;
27
- export declare function unpackStreamFrame(value: FrameBytes): StreamFrame;
@@ -1,110 +0,0 @@
1
- // Binary stream frames between the host connector and the relay. Node-only
2
- // (Buffer); browsers receive raw terminal bytes and never parse these.
3
- export const PROTOCOL_VERSION = 1;
4
- export const MAX_HEADER_BYTES = 8 * 1024;
5
- // Framing constants for compact binary frame protocol v2.
6
- // With permessage-deflate already enabled on the WebSocket connection, the repeated
7
- // JSON routing header in v1 frames was already compressed down to near-zero network overhead.
8
- // The actual motivation for v2 framing is reducing CPU consumption: eliminating the per-frame
9
- // JSON.stringify / JSON.parse serialization and string decoding overhead under heavy terminal output.
10
- export const FRAME_V2_MAGIC = 0xff;
11
- export const FRAME_TYPE_OUTPUT = 0;
12
- export const FRAME_TYPE_INPUT = 1;
13
- function toBuffer(value) {
14
- if (Buffer.isBuffer(value))
15
- return value;
16
- if (typeof value === 'string')
17
- return Buffer.from(value);
18
- if (value instanceof ArrayBuffer)
19
- return Buffer.from(value);
20
- return Buffer.from(value);
21
- }
22
- export function packStreamFrame(type, streamId, payload = Buffer.alloc(0)) {
23
- if (typeof type !== 'string' || !type || typeof streamId !== 'string' || !streamId) {
24
- throw new TypeError('type and streamId must be non-empty strings');
25
- }
26
- const body = toBuffer(payload);
27
- const header = Buffer.from(JSON.stringify({ type, streamId }), 'utf8');
28
- if (header.length > MAX_HEADER_BYTES)
29
- throw new RangeError('stream frame header is too large');
30
- const length = Buffer.allocUnsafe(4);
31
- length.writeUInt32BE(header.length, 0);
32
- return Buffer.concat([length, header, body]);
33
- }
34
- function unpackStreamFrameV1(value) {
35
- const frame = toBuffer(value);
36
- if (frame.length < 4)
37
- throw new Error('stream frame is truncated');
38
- const headerLength = frame.readUInt32BE(0);
39
- if (headerLength === 0 || headerLength > MAX_HEADER_BYTES || frame.length < 4 + headerLength) {
40
- throw new Error('stream frame header is invalid');
41
- }
42
- let header;
43
- try {
44
- header = JSON.parse(frame.subarray(4, 4 + headerLength).toString('utf8'));
45
- }
46
- catch (error) {
47
- throw new Error(`stream frame header is not JSON: ${error.message}`);
48
- }
49
- if (!header || typeof header.type !== 'string' || typeof header.streamId !== 'string') {
50
- throw new Error('stream frame header is missing routing fields');
51
- }
52
- return {
53
- version: 1,
54
- type: header.type,
55
- streamId: header.streamId,
56
- payload: frame.subarray(4 + headerLength),
57
- };
58
- }
59
- export function packStreamFrameV2(type, streamIndex, payload = Buffer.alloc(0)) {
60
- let typeCode = type;
61
- if (type === 'output')
62
- typeCode = FRAME_TYPE_OUTPUT;
63
- else if (type === 'input')
64
- typeCode = FRAME_TYPE_INPUT;
65
- if (typeCode !== FRAME_TYPE_OUTPUT && typeCode !== FRAME_TYPE_INPUT) {
66
- throw new TypeError('type must be FRAME_TYPE_OUTPUT (0) or FRAME_TYPE_INPUT (1)');
67
- }
68
- if (!Number.isInteger(streamIndex) || streamIndex < 0 || streamIndex > 0xffff) {
69
- throw new RangeError('streamIndex must be an unsigned 16-bit integer (0-65535)');
70
- }
71
- const body = toBuffer(payload);
72
- const frame = Buffer.allocUnsafe(4 + body.length);
73
- frame.writeUInt8(FRAME_V2_MAGIC, 0);
74
- frame.writeUInt8(typeCode, 1);
75
- frame.writeUInt16BE(streamIndex, 2);
76
- body.copy(frame, 4);
77
- return frame;
78
- }
79
- export function unpackStreamFrameV2(buffer) {
80
- const frame = toBuffer(buffer);
81
- if (frame.length < 4)
82
- throw new Error('v2 stream frame is truncated');
83
- const magic = frame.readUInt8(0);
84
- if (magic !== FRAME_V2_MAGIC) {
85
- throw new Error('invalid v2 stream frame magic byte');
86
- }
87
- const typeCode = frame.readUInt8(1);
88
- if (typeCode !== FRAME_TYPE_OUTPUT && typeCode !== FRAME_TYPE_INPUT) {
89
- throw new Error(`unknown v2 stream frame type: ${typeCode}`);
90
- }
91
- const streamIndex = frame.readUInt16BE(2);
92
- return {
93
- version: 2,
94
- type: typeCode === FRAME_TYPE_OUTPUT ? 'output' : 'input',
95
- typeCode,
96
- streamIndex,
97
- payload: frame.subarray(4),
98
- };
99
- }
100
- export function unpackStreamFrame(value) {
101
- const frame = toBuffer(value);
102
- if (frame.length < 4)
103
- throw new Error('stream frame is truncated');
104
- // v1's headerLength is a 32-bit big-endian integer bounded by MAX_HEADER_BYTES (8KB),
105
- // so its first byte is always 0x00. A first byte of 0xFF unambiguously identifies v2 framing.
106
- if (frame[0] === FRAME_V2_MAGIC) {
107
- return unpackStreamFrameV2(frame);
108
- }
109
- return unpackStreamFrameV1(frame);
110
- }
@@ -1,8 +0,0 @@
1
- /**
2
- * Validates the Herdr prefix keys crossing the wire, or read from a config
3
- * file, into one canonical list: lowercase, no whitespace, no duplicates, at
4
- * most `MAX_HERDR_PREFIX_KEYS` short entries. Herdr ignores entries it cannot
5
- * read and so does this. `undefined` when nothing usable is left, so a host
6
- * that says nothing and one that says garbage look the same.
7
- */
8
- export declare function sanitizeHerdrPrefixKeys(value: unknown): string[] | undefined;
@@ -1,34 +0,0 @@
1
- // The keys that put the workstation's Herdr into prefix mode, as a host reads
2
- // them from Herdr's config and a browser receives them. Browser-safe: no Node
3
- // APIs, so the web app imports it.
4
- /** Herdr accepts any number of prefix keys; a window has no use for more than this. */
5
- const MAX_HERDR_PREFIX_KEYS = 8;
6
- /** Longest real combo is `ctrl+shift+double_quote`; this leaves room for spelling variants. */
7
- const MAX_HERDR_PREFIX_KEY_LENGTH = 32;
8
- /**
9
- * What the browser can act on: a key combo is printable ASCII (modifiers and
10
- * key names joined by `+`). Whitespace is removed before this is tested.
11
- */
12
- const PREFIX_KEY = /^[\x21-\x7e]+$/;
13
- /**
14
- * Validates the Herdr prefix keys crossing the wire, or read from a config
15
- * file, into one canonical list: lowercase, no whitespace, no duplicates, at
16
- * most `MAX_HERDR_PREFIX_KEYS` short entries. Herdr ignores entries it cannot
17
- * read and so does this. `undefined` when nothing usable is left, so a host
18
- * that says nothing and one that says garbage look the same.
19
- */
20
- export function sanitizeHerdrPrefixKeys(value) {
21
- if (!Array.isArray(value))
22
- return undefined;
23
- const keys = new Set();
24
- for (const entry of value) {
25
- if (keys.size >= MAX_HERDR_PREFIX_KEYS)
26
- break;
27
- if (typeof entry !== 'string')
28
- continue;
29
- const key = entry.replace(/\s+/g, '').toLowerCase();
30
- if (key.length <= MAX_HERDR_PREFIX_KEY_LENGTH && PREFIX_KEY.test(key))
31
- keys.add(key);
32
- }
33
- return keys.size > 0 ? [...keys] : undefined;
34
- }
@@ -1,165 +0,0 @@
1
- export interface RelayInfoResponse {
2
- ok: boolean;
3
- version: string;
4
- protocol: number;
5
- relayMode: 'local' | 'remote';
6
- adminConfigured: boolean;
7
- adminPath: string;
8
- adminStatusPath: string;
9
- publicUrl?: string;
10
- }
11
- export interface RelayMetadata {
12
- mode: 'local' | 'remote';
13
- adminConfigured: boolean;
14
- adminPath?: string;
15
- adminStatusPath?: string;
16
- dashboardPath?: string;
17
- publicUrl?: string;
18
- /** The address the relay listens on, and its port. */
19
- bind?: string;
20
- port?: number;
21
- maxClientsPerHost?: number;
22
- maxHosts?: number;
23
- maxPendingHandshakes?: number;
24
- maxBufferedBytesPerClient?: number;
25
- hostReconnectGraceMs?: number;
26
- }
27
- export interface ConnectedClientInfo {
28
- id: string;
29
- role: 'controller' | 'viewer';
30
- /** Absent from a host-scoped response, where every row belongs to one host. */
31
- hostId?: string;
32
- /** Operator-only: which paired device this connection authenticated as. */
33
- deviceId?: string;
34
- ip?: string | null;
35
- userAgent?: string;
36
- connectedAt: string;
37
- lastPingAt?: string | null;
38
- bytesReceived?: number;
39
- bytesSent?: number;
40
- }
41
- /**
42
- * A device that completed pairing and holds a long-lived token. Served only on
43
- * `/api/admin/status`, never on `/api/status`.
44
- */
45
- export interface PairedDeviceInfo {
46
- deviceId: string;
47
- hostId: string;
48
- createdAt: string;
49
- lastSeenAt: string;
50
- expiresAt: number;
51
- expiresAtIso: string;
52
- userAgent?: string | null;
53
- lastIp?: string | null;
54
- }
55
- export type HostStatus = 'online' | 'busy' | 'reconnecting' | 'offline';
56
- export interface HostInfo {
57
- id: string;
58
- hostname: string;
59
- platform: string;
60
- arch?: string;
61
- status: HostStatus;
62
- connectedAt: string;
63
- activePtyCount: number;
64
- /** Distinct devices attached to this host right now, not open sockets. */
65
- connectedDeviceCount?: number;
66
- /** Devices holding a valid pairing token for this host, online or not. */
67
- pairedDeviceCount?: number;
68
- /** The herdr-remote release the host runs; null when it is too old to say. */
69
- version?: string | null;
70
- /** A newer release the host found on npm, if it found one. */
71
- latestVersion?: string | null;
72
- }
73
- export interface PtyInfo {
74
- id: string;
75
- /** Absent from a host-scoped response, where every row belongs to one host. */
76
- hostId?: string;
77
- pid: number | null;
78
- command: string;
79
- cols: number;
80
- rows: number;
81
- cwd?: string;
82
- createdAt?: string;
83
- activeClients: number;
84
- }
85
- export interface ThroughputMetrics {
86
- bytesIn: number;
87
- bytesOut: number;
88
- bytesInPerSec: number;
89
- bytesOutPerSec: number;
90
- framesIn: number;
91
- framesOut: number;
92
- framesInPerSec: number;
93
- framesOutPerSec: number;
94
- }
95
- export interface CpuLoadMetrics {
96
- load1m: number;
97
- load5m: number;
98
- load15m: number;
99
- cpuPercent: number;
100
- cores: number;
101
- }
102
- export interface MemoryMetrics {
103
- rssBytes: number;
104
- heapUsedBytes: number;
105
- heapTotalBytes: number;
106
- externalBytes?: number;
107
- systemTotalBytes?: number;
108
- systemFreeBytes?: number;
109
- }
110
- export interface EventLoopDelayMetrics {
111
- p50Ms: number;
112
- p90Ms?: number;
113
- p99Ms: number;
114
- maxMs: number;
115
- meanMs?: number;
116
- }
117
- export interface CleanupCounters {
118
- staleClientsPurged: number;
119
- closedPtysCleaned: number;
120
- deadConnectionsClosed: number;
121
- idleHostsTerminated: number;
122
- slowClientsDropped?: number;
123
- lastCleanupAt?: string | null;
124
- }
125
- export interface AdminStatusResponse {
126
- version: string;
127
- uptimeSeconds: number;
128
- startTime: string;
129
- serverTime: string;
130
- activeControllerId?: string | null;
131
- activeHostId?: string | null;
132
- clients: ConnectedClientInfo[];
133
- hosts: HostInfo[];
134
- ptys: PtyInfo[];
135
- /**
136
- * Distinct people attached, counted by paired device rather than by socket,
137
- * so several windows of one browser stay one user.
138
- */
139
- activeUserCount?: number;
140
- /** Operator-only roster of paired devices; absent from `/api/status`. */
141
- devices?: PairedDeviceInfo[];
142
- throughput: ThroughputMetrics;
143
- cpu: CpuLoadMetrics;
144
- memory: MemoryMetrics;
145
- eventLoopDelay: EventLoopDelayMetrics;
146
- cleanup: CleanupCounters;
147
- protocolVersion: number;
148
- relay?: RelayMetadata;
149
- isRemoteRelay?: boolean;
150
- remoteAdminUrl?: string;
151
- relayMode?: 'local' | 'remote';
152
- [key: string]: unknown;
153
- }
154
- /** POST /api/pair/start, answered to a workstation's own host token. */
155
- export interface PairStartResponse {
156
- ok: true;
157
- code: string;
158
- hostId: string;
159
- publicUrl: string;
160
- /** Epoch milliseconds. */
161
- expiresAt: number;
162
- expiresAtIso: string;
163
- /** The web app's address with the code filled in. */
164
- pairUrl: string;
165
- }
@@ -1,3 +0,0 @@
1
- // Bodies of the relay's HTTP API: GET /api/info, /api/status and
2
- // /api/admin/status, and POST /api/pair/start. Browser-safe: types only.
3
- export {};
@@ -1,7 +0,0 @@
1
- export * from './frames.js';
2
- export * from './terminal.js';
3
- export * from './herdr-keys.js';
4
- export * from './messages.js';
5
- export * from './paste.js';
6
- export * from './release.js';
7
- export type * from './http.js';
@@ -1,9 +0,0 @@
1
- // `herdr-remote-relay/protocol`: everything that crosses the wire between the
2
- // host connector, the relay and the browser. The single definition both
3
- // packages build against; nothing here may be copied into the CLI or the web.
4
- export * from './frames.js';
5
- export * from './terminal.js';
6
- export * from './herdr-keys.js';
7
- export * from './messages.js';
8
- export * from './paste.js';
9
- export * from './release.js';