herdr-remote 0.2.38 → 0.3.31

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 (203) hide show
  1. package/dist/chunks/353DMT35.js +3 -0
  2. package/dist/chunks/42KARAL4.js +31 -0
  3. package/dist/chunks/7AUQHB3O.js +10 -0
  4. package/dist/chunks/7SMZ43LI.js +9 -0
  5. package/dist/chunks/AM3OEMFK.js +16 -0
  6. package/dist/chunks/ANNJESOL.js +10 -0
  7. package/dist/chunks/AWB4A65P.js +63 -0
  8. package/dist/chunks/C2UUZY6J.js +2 -0
  9. package/dist/chunks/DGUZCGQN.js +3 -0
  10. package/dist/chunks/H24ZP5D5.js +16 -0
  11. package/dist/chunks/I6NRCYLI.js +3 -0
  12. package/dist/chunks/JJGEV4EZ.js +3 -0
  13. package/dist/chunks/KRSURB5L.js +5 -0
  14. package/dist/chunks/KRVF37OW.js +3 -0
  15. package/dist/chunks/KUDCC7ZO.js +6 -0
  16. package/dist/chunks/M3NRPJK2.js +77 -0
  17. package/dist/chunks/M4AFZHKH.js +3 -0
  18. package/dist/chunks/M52LJEES.js +6 -0
  19. package/dist/chunks/O2LNVFUT.js +3 -0
  20. package/dist/chunks/OJPBI7KB.js +3 -0
  21. package/dist/chunks/OMN6C7J6.js +3 -0
  22. package/dist/chunks/PBMBFWLQ.js +3 -0
  23. package/dist/chunks/PBRQ3HEG.js +3 -0
  24. package/dist/chunks/PFFVGFPL.js +45 -0
  25. package/dist/chunks/QRGNEKME.js +5 -0
  26. package/dist/chunks/TNLCNVA2.js +3 -0
  27. package/dist/chunks/U27JJKJ6.js +3 -0
  28. package/dist/chunks/U6QAYG3E.js +3 -0
  29. package/dist/chunks/UEOQE77N.js +4 -0
  30. package/dist/chunks/WSACSE45.js +4 -0
  31. package/dist/chunks/WY7WHEJ4.js +3 -0
  32. package/dist/chunks/XJKWLK43.js +4 -0
  33. package/dist/chunks/XN6D5S55.js +3 -0
  34. package/dist/chunks/YD3MIP7B.js +5 -0
  35. package/dist/chunks/YXDQ62BB.js +4 -0
  36. package/dist/cli.js +21 -294
  37. package/dist/connector/admin-broker.js +3 -302
  38. package/dist/connector/main.js +20 -18
  39. package/dist/harfbuzz-subset.wasm +0 -0
  40. package/dist/postinstall.js +17 -0
  41. package/herdr-plugin.toml +1 -1
  42. package/node_modules/herdr-remote-relay/bin/herdr-remote-relay.js +33 -92
  43. package/node_modules/herdr-remote-relay/package.json +3 -46
  44. package/package.json +8 -27
  45. package/dist/agent-status.js +0 -130
  46. package/dist/binary-plist.js +0 -157
  47. package/dist/config.js +0 -278
  48. package/dist/connector/admin-broker-setup.js +0 -40
  49. package/dist/connector/admin-shell.js +0 -67
  50. package/dist/connector/admin-tab.js +0 -90
  51. package/dist/connector/adminBrokerEndpoint.js +0 -68
  52. package/dist/connector/agent-watch.js +0 -123
  53. package/dist/connector/elevatedPty.js +0 -141
  54. package/dist/connector/font-server.js +0 -165
  55. package/dist/connector/herdr-prefix-keys.js +0 -28
  56. package/dist/connector/host-connector.js +0 -433
  57. package/dist/connector/host-hello.js +0 -32
  58. package/dist/connector/lock.js +0 -73
  59. package/dist/connector/sessions.js +0 -381
  60. package/dist/connector/update-report.js +0 -70
  61. package/dist/connector/wire.js +0 -42
  62. package/dist/exit-codes.js +0 -13
  63. package/dist/font-subset.js +0 -110
  64. package/dist/herdr-api.js +0 -254
  65. package/dist/herdr-command.js +0 -245
  66. package/dist/herdr-plugin.js +0 -120
  67. package/dist/herdr-server.js +0 -190
  68. package/dist/i18n/en.js +0 -257
  69. package/dist/i18n/index.js +0 -48
  70. package/dist/i18n/zh.js +0 -256
  71. package/dist/keepalive/detached.js +0 -97
  72. package/dist/keepalive/environment.js +0 -44
  73. package/dist/keepalive/index.js +0 -129
  74. package/dist/keepalive/launchd.js +0 -117
  75. package/dist/keepalive/systemd.js +0 -112
  76. package/dist/keepalive/types.js +0 -1
  77. package/dist/keepalive/windows-service-setup.js +0 -138
  78. package/dist/keepalive/windows-service.js +0 -272
  79. package/dist/keepalive/windows-task.js +0 -191
  80. package/dist/lib/process.js +0 -132
  81. package/dist/lifecycle.js +0 -79
  82. package/dist/net-interfaces.js +0 -82
  83. package/dist/node-pty-loader.js +0 -80
  84. package/dist/pasted-files.js +0 -115
  85. package/dist/paths.js +0 -74
  86. package/dist/pty-session.js +0 -133
  87. package/dist/relay-client.js +0 -106
  88. package/dist/relay-urls.js +0 -99
  89. package/dist/runtime.js +0 -107
  90. package/dist/service.js +0 -267
  91. package/dist/settings-model.js +0 -298
  92. package/dist/socket-discovery.js +0 -70
  93. package/dist/supervisor.js +0 -323
  94. package/dist/terminal-font/deps.js +0 -83
  95. package/dist/terminal-font/files.js +0 -357
  96. package/dist/terminal-font/formats.js +0 -355
  97. package/dist/terminal-font/index.js +0 -187
  98. package/dist/terminal-font/readers.js +0 -373
  99. package/dist/terminal-font/terminals.js +0 -147
  100. package/dist/terminal-palette.js +0 -399
  101. package/dist/tui/App.js +0 -199
  102. package/dist/tui/api.js +0 -91
  103. package/dist/tui/components/TextField.js +0 -50
  104. package/dist/tui/components/common.js +0 -80
  105. package/dist/tui/index.js +0 -36
  106. package/dist/tui/mouse/index.js +0 -164
  107. package/dist/tui/mouse/protocol.js +0 -96
  108. package/dist/tui/mouse/source.js +0 -79
  109. package/dist/tui/screens/About.js +0 -172
  110. package/dist/tui/screens/Herdr.js +0 -75
  111. package/dist/tui/screens/Keepalive.js +0 -141
  112. package/dist/tui/screens/Overview.js +0 -36
  113. package/dist/tui/screens/Pair.js +0 -74
  114. package/dist/tui/screens/Relay.js +0 -243
  115. package/dist/tui/screens/Services.js +0 -63
  116. package/dist/tui/screens/Wizard.js +0 -241
  117. package/dist/tui/terminal.js +0 -39
  118. package/dist/tui/theme.js +0 -33
  119. package/dist/update-command.js +0 -118
  120. package/dist/updater/in-place.js +0 -83
  121. package/dist/updater/npm.js +0 -62
  122. package/dist/updater.js +0 -401
  123. package/node_modules/herdr-remote-relay/README.md +0 -76
  124. package/node_modules/herdr-remote-relay/README.zh-CN.md +0 -76
  125. package/node_modules/herdr-remote-relay/config.example.json +0 -18
  126. package/node_modules/herdr-remote-relay/deploy/cloudflared/config.yml.example +0 -9
  127. package/node_modules/herdr-remote-relay/deploy/docker/compose.yaml +0 -23
  128. package/node_modules/herdr-remote-relay/deploy/nginx/herdr-remote-relay.conf.example +0 -49
  129. package/node_modules/herdr-remote-relay/deploy/systemd/herdr-remote-relay.service +0 -41
  130. package/node_modules/herdr-remote-relay/deploy/systemd/relay.env.example +0 -25
  131. package/node_modules/herdr-remote-relay/dist/auth-store.d.ts +0 -114
  132. package/node_modules/herdr-remote-relay/dist/auth-store.js +0 -289
  133. package/node_modules/herdr-remote-relay/dist/metrics.d.ts +0 -76
  134. package/node_modules/herdr-remote-relay/dist/metrics.js +0 -249
  135. package/node_modules/herdr-remote-relay/dist/protocol/frames.d.ts +0 -27
  136. package/node_modules/herdr-remote-relay/dist/protocol/frames.js +0 -110
  137. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.d.ts +0 -8
  138. package/node_modules/herdr-remote-relay/dist/protocol/herdr-keys.js +0 -34
  139. package/node_modules/herdr-remote-relay/dist/protocol/http.d.ts +0 -165
  140. package/node_modules/herdr-remote-relay/dist/protocol/http.js +0 -3
  141. package/node_modules/herdr-remote-relay/dist/protocol/index.d.ts +0 -7
  142. package/node_modules/herdr-remote-relay/dist/protocol/index.js +0 -9
  143. package/node_modules/herdr-remote-relay/dist/protocol/messages.d.ts +0 -362
  144. package/node_modules/herdr-remote-relay/dist/protocol/messages.js +0 -20
  145. package/node_modules/herdr-remote-relay/dist/protocol/paste.d.ts +0 -14
  146. package/node_modules/herdr-remote-relay/dist/protocol/paste.js +0 -50
  147. package/node_modules/herdr-remote-relay/dist/protocol/release.d.ts +0 -8
  148. package/node_modules/herdr-remote-relay/dist/protocol/release.js +0 -17
  149. package/node_modules/herdr-remote-relay/dist/protocol/terminal.d.ts +0 -79
  150. package/node_modules/herdr-remote-relay/dist/protocol/terminal.js +0 -150
  151. package/node_modules/herdr-remote-relay/dist/relay-config.d.ts +0 -74
  152. package/node_modules/herdr-remote-relay/dist/relay-config.js +0 -316
  153. package/node_modules/herdr-remote-relay/dist/relay-server.d.ts +0 -67
  154. package/node_modules/herdr-remote-relay/dist/relay-server.js +0 -196
  155. package/node_modules/herdr-remote-relay/dist/scroll-input.d.ts +0 -17
  156. package/node_modules/herdr-remote-relay/dist/scroll-input.js +0 -124
  157. package/node_modules/herdr-remote-relay/dist/server/client-channel.d.ts +0 -3
  158. package/node_modules/herdr-remote-relay/dist/server/client-channel.js +0 -396
  159. package/node_modules/herdr-remote-relay/dist/server/font-proxy.d.ts +0 -19
  160. package/node_modules/herdr-remote-relay/dist/server/font-proxy.js +0 -155
  161. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.d.ts +0 -27
  162. package/node_modules/herdr-remote-relay/dist/server/host-broadcasts.js +0 -83
  163. package/node_modules/herdr-remote-relay/dist/server/host-channel.d.ts +0 -3
  164. package/node_modules/herdr-remote-relay/dist/server/host-channel.js +0 -271
  165. package/node_modules/herdr-remote-relay/dist/server/http-api.d.ts +0 -4
  166. package/node_modules/herdr-remote-relay/dist/server/http-api.js +0 -219
  167. package/node_modules/herdr-remote-relay/dist/server/maintenance.d.ts +0 -5
  168. package/node_modules/herdr-remote-relay/dist/server/maintenance.js +0 -64
  169. package/node_modules/herdr-remote-relay/dist/server/requests.d.ts +0 -34
  170. package/node_modules/herdr-remote-relay/dist/server/requests.js +0 -142
  171. package/node_modules/herdr-remote-relay/dist/server/sessions.d.ts +0 -36
  172. package/node_modules/herdr-remote-relay/dist/server/sessions.js +0 -230
  173. package/node_modules/herdr-remote-relay/dist/server/sockets.d.ts +0 -12
  174. package/node_modules/herdr-remote-relay/dist/server/sockets.js +0 -64
  175. package/node_modules/herdr-remote-relay/dist/server/status.d.ts +0 -11
  176. package/node_modules/herdr-remote-relay/dist/server/status.js +0 -99
  177. package/node_modules/herdr-remote-relay/dist/server/transport.d.ts +0 -16
  178. package/node_modules/herdr-remote-relay/dist/server/transport.js +0 -110
  179. package/node_modules/herdr-remote-relay/dist/server/types.d.ts +0 -133
  180. package/node_modules/herdr-remote-relay/dist/server/types.js +0 -3
  181. package/node_modules/herdr-remote-relay/dist/server/version.d.ts +0 -2
  182. package/node_modules/herdr-remote-relay/dist/server/version.js +0 -5
  183. package/node_modules/herdr-remote-relay/dist/state.d.ts +0 -6
  184. package/node_modules/herdr-remote-relay/dist/state.js +0 -47
  185. package/node_modules/ws/LICENSE +0 -20
  186. package/node_modules/ws/README.md +0 -548
  187. package/node_modules/ws/browser.js +0 -8
  188. package/node_modules/ws/index.js +0 -22
  189. package/node_modules/ws/lib/buffer-util.js +0 -131
  190. package/node_modules/ws/lib/constants.js +0 -19
  191. package/node_modules/ws/lib/event-target.js +0 -292
  192. package/node_modules/ws/lib/extension.js +0 -203
  193. package/node_modules/ws/lib/limiter.js +0 -55
  194. package/node_modules/ws/lib/permessage-deflate.js +0 -530
  195. package/node_modules/ws/lib/receiver.js +0 -743
  196. package/node_modules/ws/lib/sender.js +0 -607
  197. package/node_modules/ws/lib/stream.js +0 -161
  198. package/node_modules/ws/lib/subprotocol.js +0 -62
  199. package/node_modules/ws/lib/validation.js +0 -152
  200. package/node_modules/ws/lib/websocket-server.js +0 -562
  201. package/node_modules/ws/lib/websocket.js +0 -1407
  202. package/node_modules/ws/package.json +0 -74
  203. package/node_modules/ws/wrapper.mjs +0 -21
@@ -1,18 +1,20 @@
1
- // Process entry for the host connector; service.ts spawns this file.
2
- import { EXIT_REPLACED } from '../exit-codes.js';
3
- import { HostConnector } from './host-connector.js';
4
- const connector = new HostConnector({ onFatal: (exitCode) => process.exit(exitCode) });
5
- try {
6
- connector.start();
7
- }
8
- catch (error) {
9
- const { code, message } = error;
10
- process.stderr.write(`herdr-remote host connector failed: ${message}\n`);
11
- process.exitCode = code === 'HOST_ALREADY_RUNNING' ? EXIT_REPLACED : 1;
12
- }
13
- const stop = () => {
14
- connector.stop();
15
- process.exit(0);
16
- };
17
- process.once('SIGINT', stop);
18
- process.once('SIGTERM', stop);
1
+ import { createRequire as __herdrCreateRequire } from 'node:module';
2
+ const require = __herdrCreateRequire(import.meta.url);
3
+ import"../chunks/C2UUZY6J.js";import{a as we,b as Te,d as Se}from"../chunks/7AUQHB3O.js";import"../chunks/XN6D5S55.js";import{a as g}from"../chunks/42KARAL4.js";import{a as d}from"../chunks/7SMZ43LI.js";import{a as fe,b as me,c as pe,d as k}from"../chunks/JJGEV4EZ.js";import{a as ge,d as M,e as ye,i as _e}from"../chunks/H24ZP5D5.js";import{m as be}from"../chunks/PBRQ3HEG.js";import"../chunks/U6QAYG3E.js";import"../chunks/M4AFZHKH.js";import{b as ne,e as ie,g as L,h as oe,i as ae,m as ce,n as he,o as le,p as de,q as ue}from"../chunks/UEOQE77N.js";import{h as Q,i as Z,o as ee}from"../chunks/M52LJEES.js";import{b as re,c as se}from"../chunks/KRVF37OW.js";import{a as p,d as te}from"../chunks/QRGNEKME.js";import{a as G,b as Y,c as W,h as X,j as w,k as q,l as j,m as J}from"../chunks/PBMBFWLQ.js";import{a as K,c as b}from"../chunks/TNLCNVA2.js";import"../chunks/I6NRCYLI.js";import Pt from"node:os";import De from"node:path";import Ke from"node:crypto";import y from"node:fs";import C from"node:path";var Ge=20,Ye=50*1024*1024,We=1440*60*1e3;function ke(){let s=C.join(b(),"pasted");return p(s),s}function I({dir:s=ke(),maxFiles:e=Ge,maxBytes:t=Ye,maxAgeMs:r=We,now:n=Date.now()}={}){let i;try{i=y.readdirSync(s,{withFileTypes:!0})}catch{return}let o=[];for(let h of i){if(!h.isFile())continue;let l=C.join(s,h.name);try{let c=y.statSync(l);if(n-c.mtimeMs>r){try{y.unlinkSync(l)}catch{}continue}o.push({path:l,size:c.size,mtimeMs:c.mtimeMs})}catch{}}o.sort((h,l)=>h.mtimeMs-l.mtimeMs);let a=o.reduce((h,l)=>h+l.size,0);for(;o.length>e||a>t;){let h=o.shift();if(!h)break;try{y.unlinkSync(h.path),a-=h.size}catch{}}}function Ie({mime:s,dataBase64:e,dir:t=ke()}){if(!j(s))throw new Error(`unsupported MIME type: ${s}`);if(typeof e!="string")throw new Error("missing or invalid dataBase64 payload");let r=Buffer.from(e,"base64");if(r.length>3145728)throw new Error(`file size (${r.length} bytes) exceeds maximum limit of 3 MB`);if(r.length===0)throw new Error("file payload is empty");if(!J(s,r))throw new Error(`file magic bytes do not match declared MIME type ${s}`);I({dir:t});let n=`${Ke.randomUUID()}${q[s]}`,i=C.join(t,n);y.writeFileSync(i,r,{mode:384,flag:"wx"});try{y.chmodSync(i,384)}catch{}return I({dir:t}),i}var T=["blocked","done","working","idle","unknown"];function Ee(s,e=48){if(typeof s!="string")return null;let t=s.replace(/[\u0000-\u001F\u007F-\u009F]/g," ").replace(/\s+/g," ").trim();return t?t.length>e?t.slice(0,e):t:null}function Xe(s){return T.includes(s)?s:"unknown"}function qe(){return Object.fromEntries(T.map(s=>[s,0]))}function Ae(s){let e=Array.isArray(s?.agents)?s.agents:[],t=Array.isArray(s?.panes)?s.panes:[],r=typeof s?.focused_pane_id=="string"&&s.focused_pane_id?s.focused_pane_id:null,n=(r?t.find(c=>c?.pane_id===r):t.find(c=>c?.focused===!0))||null,i=(r?e.find(c=>c?.pane_id===r):e.find(c=>c?.focused===!0))||null,o=r||(typeof n?.pane_id=="string"?n.pane_id:null)||(typeof i?.pane_id=="string"?i.pane_id:null),a=typeof n?.agent=="string"?n.agent:typeof i?.agent=="string"?i.agent:null,h=qe(),l=[];for(let c of e){if(!c||typeof c.pane_id!="string")continue;let f=Xe(c.agent_status);h[f]+=1,l.push({paneId:c.pane_id,workspaceId:typeof c.workspace_id=="string"?c.workspace_id:null,agent:Ee(c.display_agent||c.agent,24),title:Ee(c.terminal_title_stripped||c.terminal_title),status:f,focused:c.focused===!0})}return l.sort((c,f)=>T.indexOf(c.status)-T.indexOf(f.status)),{focusedPaneId:o,focusedAgent:a,counts:h,total:l.length,agents:l.slice(0,16)}}function He(s,e){if(!s||!e)return s===e;if(s.focusedPaneId!==e.focusedPaneId||s.focusedAgent!==e.focusedAgent||s.total!==e.total)return!1;for(let t of T)if((s.counts?.[t]||0)!==(e.counts?.[t]||0))return!1;return s.agents.length!==e.agents.length?!1:s.agents.every((t,r)=>{let n=e.agents[r];return n&&t.paneId===n.paneId&&t.status===n.status&&t.agent===n.agent&&t.title===n.title})}var je=5e3,Je=150,Qe=[{type:"pane.focused"},{type:"tab.focused"},{type:"workspace.focused"},{type:"pane.agent_detected"}],E=class{socketPath;request;subscribe;send;pollTimer=null;debounceTimer=null;subscription=null;pending=!1;stale=!1;lastSummary=null;constructor(e,t){this.socketPath=e.socketPath,this.request=e.requestHerdr||we,this.subscribe=e.subscribeHerdr||Te,this.send=t}start(){if(!this.pollTimer){this.pollTimer=setInterval(()=>this.read(),je),this.pollTimer.unref?.();try{this.subscription=this.subscribe(this.socketPath,Qe,()=>this.scheduleRead(),{onStarted:()=>this.scheduleRead()})}catch{}this.read()}}stop(){this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null),this.subscription&&(this.subscription.close(),this.subscription=null),this.stale=!1,this.lastSummary=null}scheduleRead(){this.pollTimer&&(this.debounceTimer&&clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout(()=>{this.debounceTimer=null,this.read()},Je),this.debounceTimer.unref?.())}async read(){if(!this.pollTimer)return;if(this.pending){this.stale=!0;return}this.pending=!0,this.stale=!1;let e;try{let r=await this.request(this.socketPath,"session.snapshot",{});e=r?.snapshot||r}catch{return}finally{this.pending=!1,this.stale&&this.scheduleRead()}if(!this.pollTimer)return;let t=Ae(e);He(this.lastSummary,t)||(this.lastSummary=t,this.send({type:"agent_status",...t}))}};import it from"node:crypto";import ot from"node:fs";import{createRequire as Ze}from"node:module";import R from"node:fs";import et from"node:path";var tt=6e4,rt=16384,st=1,N=null;function nt(){if(!N){let s=et.join(K,"dist","harfbuzz-subset.wasm"),e=R.existsSync(s)?s:Ze(import.meta.url).resolve("harfbuzzjs/dist/harfbuzz-subset.wasm");N=new WebAssembly.Module(R.readFileSync(e))}return N}var A=class{idleMs;instance=null;faces=new Map;idleTimer=null;constructor({idleMs:e=tt}={}){this.idleMs=e}exports(){return this.instance||(this.instance=new WebAssembly.Instance(nt(),{}).exports,this.instance._initialize()),this.instance}face(e){let t=`${e.path}\0${e.index||0}`,r=this.faces.get(t);if(r)return r.face;let n=this.exports(),i=R.readFileSync(e.path),o=n.malloc(i.length);if(!o)throw new Error("out of memory loading font");new Uint8Array(n.memory.buffer).set(i,o);let a=n.hb_blob_create(o,i.length,st,0,0),h=n.hb_face_create(a,e.index||0);return n.hb_blob_destroy(a),this.faces.set(t,{pointer:o,face:h}),h}subset(e,t){let r=this.exports(),n=this.face(e),i=r.hb_subset_input_create_or_fail();if(!i)throw new Error("font subsetting is unavailable");let o=0,a=0;try{let h=r.hb_subset_input_unicode_set(i);for(let f of t.slice(0,rt))r.hb_set_add(h,f);if(a=r.hb_subset_or_fail(n,i),!a)throw new Error("the font could not be subset");o=r.hb_face_reference_blob(a);let l=r.hb_blob_get_length(o),c=r.hb_blob_get_data(o,0);return Buffer.from(new Uint8Array(r.memory.buffer,c,l))}finally{o&&r.hb_blob_destroy(o),a&&r.hb_face_destroy(a),r.hb_subset_input_destroy(i),this.scheduleRelease()}}scheduleRelease(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=setTimeout(()=>this.release(),this.idleMs),this.idleTimer.unref?.()}release(){this.idleTimer&&clearTimeout(this.idleTimer),this.idleTimer=null,this.faces.clear(),this.instance=null}};var at=48*1024*1024,ve="The terminal font changed or is no longer on this workstation",v=class{font;load;subsetter;subsets=new Map;send;constructor(e,t){this.load=e.loadTerminalFont||_e,this.font=e.terminalFont!==void 0?e.terminalFont:this.load(),this.subsetter=e.fontSubsetter||new A,this.send=t}publicFont(){return M(this.font)}pickUp(){if(this.font)return;let e=null;try{e=this.load()}catch{return}e&&(this.font=e,this.send({type:"terminal_font",terminalFont:M(e)}))}refresh(){try{let e=this.load({refresh:!0});e&&(this.font=e)}catch(e){process.stderr.write(`herdr-remote host connector: could not read the terminal font: ${e.message}
4
+ `)}this.send({type:"terminal_font",terminalFont:this.publicFont()})}sendChunk(e,t){let r=this.subsetChunk(t.sha256,t.index)||ye(this.font,t.sha256,t.index);if(!r){this.send({type:"error",clientId:e,code:"host_font_unavailable",message:ve});return}this.send({type:"host_font_chunk",clientId:e,sha256:t.sha256,index:t.index,total:r.total,dataBase64:r.data.toString("base64")})}sendSubset(e,t){let r=(l,c)=>this.send({type:"error",clientId:e,code:l,message:c}),n=this.font?.subsets?.find(l=>l.sha256===t.sha256);if(!n||!this.unchanged(n)){r("host_font_unavailable",ve);return}let i=[...new Set(Array.from(String(t.text||""),l=>l.codePointAt(0)))],o;try{o=this.subsetter.subset(n,i)}catch(l){r("host_font_subset_failed",l.message||"The font could not be subset");return}let a=it.createHash("sha256").update(o).digest("hex"),h={type:"host_font_subset_ready",clientId:e,requestId:t.requestId,sha256:n.sha256,subsetSha:a,bytes:o.length};o.length<=262144?h.dataBase64=o.toString("base64"):this.hold(a,o),this.send(h)}unchanged(e){try{let t=ot.statSync(e.path);return t.size===e.bytes&&t.mtimeMs===e.mtimeMs}catch{return!1}}hold(e,t){this.subsets.delete(e),this.subsets.set(e,t);let r=[...this.subsets.values()].reduce((n,i)=>n+i.length,0);for(let[n,i]of this.subsets){if(r<=at||n===e)break;this.subsets.delete(n),r-=i.length}}subsetChunk(e,t){let r=this.subsets.get(e);if(!r)return null;let n=Math.ceil(r.length/262144);if(!Number.isInteger(t)||t<0||t>=n)return null;let i=t*262144;return{data:r.subarray(i,i+262144),total:n}}};import lt from"node:os";import ct from"node:fs";var Pe=["ctrl+b"];function Fe(s=process.env,e=process.platform){let t;try{t=ct.readFileSync(fe(s,e),"utf8")}catch{return[...Pe]}let r=ge(t)["keys.prefix"];return X(typeof r=="string"?[r]:r)??[...Pe]}function Oe(s){return{type:"host_hello",protocol:1,hostId:s.hostId,token:s.token,password:s.password||null,hostname:lt.hostname(),platform:process.platform,...process.platform==="win32"&&(process.env.MSYSTEM||/(?:^|[\\/])(?:bash|sh)(?:\.exe)?$/i.test(process.env.SHELL||""))?{shellProfile:"git-bash"}:{},arch:process.arch,version:s.version,terminalPalette:s.terminalPalette||null,terminalFont:s.terminalFont,herdrPrefixKeys:Fe(),capabilities:[w.hostHandoff,w.idleHeartbeat,w.binaryFrameV2,...process.platform==="win32"?[w.adminTabs]:[]]}}import _ from"node:fs";import dt from"node:path";function Le(s){try{return JSON.parse(_.readFileSync(s,"utf8"))}catch{return null}}function xe(s,e){let t=new Error(s);return t.code=e,t}function Me(s,e){p(dt.dirname(s));for(let t=0;t<2;t+=1)try{let r=_.openSync(s,"wx",384);return _.writeFileSync(r,`${JSON.stringify({pid:process.pid,processStart:re(process.pid),hostId:e,startedAt:new Date().toISOString()})}
5
+ `),r}catch(r){if(r.code!=="EEXIST")throw r;let n=Le(s);if(n&&se([n]).length>0)throw xe(`another host connector is already running (pid ${n.pid})`,"HOST_ALREADY_RUNNING");try{_.rmSync(s,{force:!0})}catch{}}throw xe("could not acquire host connector lock","HOST_LOCK_FAILED")}function Ce(s,e){try{_.closeSync(e)}catch{}let t=Le(s);if(!(t&&t.pid!==process.pid))try{_.rmSync(s,{force:!0})}catch{}}import P from"node:fs";import ut from"node:net";import ft from"node:path";import{spawn as mt}from"node:child_process";var pt=1e3,gt=2e4,yt=200;function D(s,{connect:e=ut.connect,timeout:t=pt}={}){let r=k(s);return r.ok?new Promise(n=>{let i,o=h=>{clearTimeout(a),i&&(i.removeAllListeners(),i.on("error",()=>{}),i.destroy()),n(h)},a=setTimeout(()=>o({state:"running"}),t);typeof a.unref=="function"&&a.unref(),i=e(me(s)),i.once("connect",()=>o({state:"running"})),i.once("error",h=>o(h.code==="ECONNREFUSED"||h.code==="ENOENT"?{state:"stopped",stale:!0}:{state:"unavailable",reason:h.message}))}):Promise.resolve(r.missing?{state:"stopped"}:{state:"unavailable",reason:r.reason})}function _t({platform:s=process.platform,readCgroup:e=()=>P.readFileSync("/proc/self/cgroup","utf8")}={}){if(s!=="linux")return!1;try{let t=e().split(`
6
+ `).find(r=>r.startsWith("0::"));return t!==void 0&&/\.service$/.test(t.trim())}catch{return!1}}function bt(s,e=process.env){let t={...e};for(let r of Object.keys(t))r.startsWith("HERDR_")&&delete t[r];return s&&(t.HERDR_SOCKET_PATH=s),t}function wt({command:s,args:e=[],inService:t=_t()}){let r=[s,...e,"server"];return t?{file:"systemd-run",args:["--user","--scope","--quiet","--collect","--description=Herdr server started by herdr-remote","--",...r]}:{file:r[0],args:r.slice(1)}}function Tt({command:s,args:e=[],socketPath:t,cwd:r,logPath:n,spawnProcess:i=mt,inService:o}){let{file:a,args:h}=wt({command:s,args:e,inService:o});p(ft.dirname(n));let l=P.openSync(n,"a"),c;try{c=i(a,h,{cwd:r,env:bt(t),detached:!0,windowsHide:!0,stdio:["ignore",l,l]})}finally{P.closeSync(l)}return c.on("error",()=>{}),c.unref(),c}function St(s,e=2e3){try{return P.readFileSync(s,"utf8").slice(-e).trim()}catch{return""}}async function Ne({command:s,args:e=[],socketPath:t,cwd:r,logPath:n,probe:i=D,launch:o=Tt,timeout:a=gt,interval:h=yt}){let l=await i(t);if(l.state==="running")return{started:!1};if(l.state==="unavailable")throw new Error(`Herdr socket at ${t} cannot be used: ${l.reason}`);let c=o({command:s,args:e,socketPath:t,cwd:r,logPath:n}),f=null;c.once("exit",(B,S)=>{f={code:B,signal:S}});let Ve=Date.now()+a;for(;;){if(await new Promise(Be=>setTimeout(Be,h)),(await i(t)).state==="running")return{started:!0};let S=St(n),z=S?`
7
+ ${S}`:"";if(f)throw new Error(`Herdr server exited (${f.signal||`code ${f.code}`}) before opening ${t}.${z}`);if(Date.now()>=Ve)throw new Error(`Herdr server did not open ${t} within ${Math.round(a/1e3)}s.${z}`)}}function m(s,e){s&&s.readyState===d.default.OPEN&&s.send(JSON.stringify(e))}function Re(s,e="host connector stopping"){if(!(!s||s.readyState===d.default.CLOSED||s.readyState===d.default.CLOSING))try{s.close(1e3,e)}catch{}}function U(s){if(!s||s.readyState===d.default.CLOSED)return;let e=s;try{typeof e.terminate=="function"?e.terminate():typeof e.destroy=="function"?e.destroy():e.close?.()}catch{}}function u(s){let e=s.clientId||s.streamId;return typeof e=="string"&&e?e:null}var It=1500,Et=4,At=3,F=class{byStream=new Map;streamIndexToId=new Map;waitingForHerdr=new Map;probingStarts=new Map;herdrLaunch=null;herdrVersionWarned=!1;fastFailures=0;herdrCommand;herdrArgs;socketPath;cwd;herdrLookup;herdrLogPath;readHerdrVersion;Pty;probeHerdr;ensureHerdr;link;constructor(e,t){this.socketPath=e.socketPath,this.herdrLookup=e.herdrLookup||{},this.herdrCommand=e.herdrCommand||ce(this.herdrLookup),this.herdrArgs=e.herdrArgs,this.cwd=e.cwd,this.herdrLogPath=e.herdrLogPath,this.readHerdrVersion=e.readHerdrVersion||le,this.Pty=e.PtySession||g,this.probeHerdr=e.probeHerdr||D,this.ensureHerdr=e.ensureHerdr||Ne,this.link=t}sendError(e,t,r){this.link.send({type:"error",clientId:e,code:t,message:r})}ensureHerdrCommand(){let e=he(this.herdrCommand,this.herdrLookup);return e.found?(this.herdrCommand=e.command,this.warnIfHerdrOutdated(),null):ue(this.herdrLookup)}warnIfHerdrOutdated(){if(this.herdrVersionWarned)return;let e=this.readHerdrVersion({command:this.herdrCommand});if(e.supported){e.version&&(this.herdrVersionWarned=!0);return}this.herdrVersionWarned=!0,process.stderr.write(`herdr-remote host connector: ${de(e.version)}
8
+ `)}start(e){let t=u(e);if(!t)return;this.stop(t);let r=this.ensureHerdrCommand();if(r){process.stderr.write(`herdr-remote host connector: ${r}
9
+ `),this.sendError(t,"herdr_not_found",r);return}let n=k(this.socketPath);if(!n.ok&&!n.missing){this.sendError(t,"herdr_socket_unavailable",n.reason);return}if(!n.ok){this.waitForHerdr(t,e);return}let i={message:e};return this.probingStarts.set(t,i),this.probeHerdr(this.socketPath).then(o=>{this.probingStarts.get(t)===i&&(this.probingStarts.delete(t),o.state==="running"?this.spawn(i.message):o.state==="stopped"?this.waitForHerdr(t,i.message):this.sendError(t,"herdr_socket_unavailable",o.reason))})}waitForHerdr(e,t){this.waitingForHerdr.set(e,t),this.sendError(e,"herdr_not_running",`Herdr is not running on this workstation (${this.socketPath}).`)}startHerdr(){return this.herdrLaunch||(this.herdrLaunch=Promise.resolve().then(()=>{let e=this.ensureHerdrCommand();if(e)throw new Error(e);return this.ensureHerdr({command:this.herdrCommand,args:this.herdrArgs,socketPath:this.socketPath,cwd:this.cwd,logPath:this.herdrLogPath})}).finally(()=>{this.herdrLaunch=null})),this.herdrLaunch}async startHerdrFor(e){try{(await this.startHerdr()).started&&process.stderr.write(`herdr-remote host connector: started Herdr at a browser's request
10
+ `)}catch(r){let{message:n}=r;process.stderr.write(`herdr-remote host connector: could not start Herdr: ${n}
11
+ `),e&&this.sendError(e,"herdr_start_failed",n);return}let t=[...this.waitingForHerdr.values()];this.waitingForHerdr.clear();for(let r of t)this.spawn(r)}spawn(e){let t=u(e),r=typeof e.streamIndex=="number"?e.streamIndex:null,n=new this.Pty({command:this.herdrCommand,args:this.herdrArgs,cwd:this.cwd,socketPath:this.socketPath}),i={id:t,streamIndex:r,pty:n,cols:Number(e.cols)||g.DEFAULT_COLS,rows:Number(e.rows)||g.DEFAULT_ROWS,createdAt:new Date().toISOString(),startedAtMs:Date.now(),pendingOutput:[],flushImmediate:null,flushTimeout:null},o=()=>{if(i.flushImmediate&&(clearImmediate(i.flushImmediate),i.flushImmediate=null),i.flushTimeout&&(clearTimeout(i.flushTimeout),i.flushTimeout=null),i.pendingOutput.length===0)return;let a=Buffer.concat(i.pendingOutput);i.pendingOutput=[],this.link.sendFrame(i.streamIndex!==null?Y(0,i.streamIndex,a):G("output",t,a))};try{n.start({cols:i.cols,rows:i.rows,onData:a=>{let h=Buffer.isBuffer(a)?a:Buffer.from(a,"utf8");h.length!==0&&(i.pendingOutput.push(h),!i.flushImmediate&&!i.flushTimeout&&(process.platform==="win32"?i.flushTimeout=setTimeout(o,Et):i.flushImmediate=setImmediate(o)))},onExit:({exitCode:a})=>{this.byStream.get(t)===i&&(this.forget(i),this.reportExit(i,a),this.link.changed())}})}catch(a){$(i),this.sendError(t,"pty_start_failed",a.message),n.kill();return}this.byStream.set(t,i),r!==null&&this.streamIndexToId.set(r,t),this.link.send({type:"session_ready",clientId:t}),this.link.changed()}reportExit(e,t){let r=e.id,n=Date.now()-e.startedAtMs,i=t!==0&&n<It;if(this.fastFailures=i?this.fastFailures+1:0,this.fastFailures<At){this.link.send({type:"session_exit",clientId:r,code:t});return}let o=`"${this.herdrCommand}" exited immediately with code ${t} on ${this.fastFailures} attempts in a row. Check that it runs from a terminal, and see the host connector log for what it printed.`;process.stderr.write(`herdr-remote host connector: ${o}
12
+ `),this.sendError(r,"herdr_start_failed",o)}write(e,t){let r=e.streamIndex!==void 0?this.streamIndexToId.get(e.streamIndex):e.streamId;r&&this.byStream.get(r)?.pty.write(t)}resize(e){let t=u(e);if(!t)return;let r=this.waitingForHerdr.get(t)||this.probingStarts.get(t)?.message;r&&(Number.isInteger(e.cols)&&(r.cols=e.cols),Number.isInteger(e.rows)&&(r.rows=e.rows));let n=this.byStream.get(t);n&&(Number.isInteger(e.cols)&&(n.cols=g.clampDimension(e.cols,0)),Number.isInteger(e.rows)&&(n.rows=g.clampDimension(e.rows,0)),n.pty.resize(n.cols,n.rows))}stop(e){this.waitingForHerdr.delete(e),this.probingStarts.delete(e);let t=this.byStream.get(e);t&&(this.forget(t),t.pty.kill(),this.link.changed())}stopAll(){for(let e of this.byStream.values())$(e),e.pty.kill();this.byStream.clear(),this.streamIndexToId.clear(),this.waitingForHerdr.clear(),this.probingStarts.clear()}summaries(){return[...this.byStream.values()].map(e=>({id:e.id,pid:e.pty.terminal?.pid||null,command:e.pty.command,cols:e.cols,rows:e.rows,cwd:e.pty.cwd,createdAt:e.createdAt,activeClients:1}))}forget(e){this.byStream.delete(e.id),e.streamIndex!==null&&this.streamIndexToId.delete(e.streamIndex),$(e)}};function $(s){s.flushImmediate&&(clearImmediate(s.flushImmediate),s.flushImmediate=null),s.flushTimeout&&(clearTimeout(s.flushTimeout),s.flushTimeout=null),s.pendingOutput=[]}var Ht=6e4,O=class{checkUpdate;readInstalledVersion;runningVersion;send;checkedAt=0;pending=null;constructor(e,t){this.checkUpdate=e.checkUpdate!==void 0?e.checkUpdate:ie()?oe:null,this.readInstalledVersion=e.readInstalledVersion||ae,this.runningVersion=e.runningVersion||ne(),this.send=t}report({now:e=Date.now()}={}){let t=this.checkUpdate;return!t||this.pending?this.pending:e-this.checkedAt<Ht?null:(this.checkedAt=e,this.pending=Promise.resolve().then(()=>t()).then(r=>{if(!r?.ok||!r.latest)return;let n=this.runningVersion,i=this.readInstalledVersion()||n;this.send({type:"update_status",current:n,installed:i,latest:r.latest,updateAvailable:L(r.latest,n)>0,restartPending:L(i,n)>0,updateCommand:!0})}).catch(()=>{}).finally(()=>{this.pending=null}),this.pending)}};var Ft=["relay_password_required","host_auth_failed","invalid_host_credentials"],x=class{config;relayUrl;hostId;hostToken;relayPassword;socketPath;terminalPalette;sessions;font;agents;updates;ws=null;reconnectTimer=null;heartbeatTimer=null;keepaliveTimer=null;reconnectAttempts=0;clientCount=0;legacyHeartbeat=!1;authFailure=!1;stopping=!1;lockPath;lockFd=null;onFatal;constructor(e={}){let t=e.config||ee();this.config=t,this.relayUrl=e.relayUrl||(process.env.RELAY_URL?Z(process.env.RELAY_URL):Q(t)),this.hostId=e.hostId||process.env.RELAY_HOST_ID||`host-${te(9)}`,this.hostToken=e.hostToken||process.env.RELAY_HOST_TOKEN||"",this.relayPassword=e.relayPassword??process.env.RELAY_PASSWORD??"",this.socketPath=e.socketPath||pe(t.herdr.socketPath),this.terminalPalette=e.terminalPalette!==void 0?e.terminalPalette:be();let r=n=>m(this.ws,n);this.sessions=new F({...e,socketPath:this.socketPath,herdrArgs:e.herdrArgs||t.herdr.args,cwd:e.cwd||t.herdr.cwd,herdrLogPath:e.herdrLogPath||De.join(b(),"herdr-server.log")},{send:r,sendFrame:n=>{this.ws?.readyState===d.default.OPEN&&this.ws.send(n)},changed:()=>this.sendHeartbeat()}),this.font=new v(e,r),this.agents=new E({...e,socketPath:this.socketPath},r),this.updates=new O(e,r),this.lockPath=e.lockPath||process.env.HERDR_REMOTE_HOST_LOCK||De.join(b(),"host-connector.lock"),this.onFatal=e.onFatal||(n=>process.exit(n));try{I()}catch{}}acquireLock(){this.lockFd===null&&(this.lockFd=Me(this.lockPath,this.hostId))}releaseLock(){this.lockFd!==null&&(Ce(this.lockPath,this.lockFd),this.lockFd=null)}start(){if(!this.hostToken)throw new Error("RELAY_HOST_TOKEN is required");this.acquireLock(),this.stopping=!1,this.connect(),this.config.herdr?.autoStart&&this.sessions.startHerdr().catch(e=>{process.stderr.write(`herdr-remote host connector: could not start Herdr: ${e.message}
13
+ `)})}stop(){this.stopping=!0,this.reconnectTimer&&clearTimeout(this.reconnectTimer),this.reconnectTimer=null,m(this.ws,{type:"host_shutdown"}),this.disconnected(),Re(this.ws,"host_shutdown"),this.ws=null,this.releaseLock()}disconnected(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null,this.stopKeepalive(),this.agents.stop(),this.clientCount=0,this.legacyHeartbeat=!1,this.sessions.stopAll()}connect(){if(this.stopping||this.ws?.readyState===d.default.OPEN||this.ws?.readyState===d.default.CONNECTING)return;this.stopKeepalive();let e;try{e=new d.default(this.relayUrl)}catch(t){this.scheduleReconnect(t);return}this.ws=e,e.isAlive=!0,e.on("open",()=>{try{e._socket?.setNoDelay(!0)}catch{}e.isAlive=!0,this.authFailure=!1,m(e,Oe({hostId:this.hostId,token:this.hostToken,password:this.relayPassword,version:this.updates.runningVersion,terminalPalette:this.terminalPalette,terminalFont:this.font.publicFont()}))}),e.on("pong",()=>{e.isAlive=!0}),e.on("message",(t,r)=>this.handleMessage(t,r)),e.on("close",(t,r)=>{if(this.ws!==e)return;if(this.ws=null,this.disconnected(),(r?r.toString():"")==="host_replaced"){process.stderr.write(`herdr-remote host connector: another instance took over this workstation; exiting
14
+ `),this.stop(),this.onFatal(12);return}if(this.authFailure){this.stopping=!0,process.stderr.write(`herdr-remote host connector: authentication failed; update relay credentials and restart the service
15
+ `),this.releaseLock(),this.onFatal(13);return}this.scheduleReconnect()}),e.on("error",t=>{process.stderr.write(`herdr-remote host connector: ${t.message}
16
+ `)})}scheduleReconnect(e=null){if(this.stopping||this.reconnectTimer)return;e&&process.stderr.write(`herdr-remote host connector: ${e.message}
17
+ `);let t=Math.min(3e4,500*1.5**this.reconnectAttempts+Math.floor(Math.random()*250));this.reconnectAttempts+=1,this.reconnectTimer=setTimeout(()=>{this.reconnectTimer=null,this.connect()},t)}handleMessage(e,t=!1){if(t){this.handleFrame(e);return}let r;try{r=JSON.parse(typeof e=="string"?e:e.toString("utf8"))}catch{return}switch(r.type){case"error":if(r.clientId)return;this.authFailure=Ft.includes(r.code),process.stderr.write(`herdr-remote host connector: relay rejected the connection: ${r.message||r.code}
18
+ `);return;case"host_ready":this.hostReady(r);return;case"client_count":this.setClientCount(r.clientCount);return;case"session_start":return this.startSession(r);case"herdr_start":return this.sessions.startHerdrFor(u(r));case"admin_tab_open":return Se(u(r),{socketPath:this.socketPath,language:this.config.ui?.language,send:n=>m(this.ws,n)});case"session_stop":{let n=u(r);n&&this.sessions.stop(n);return}case"resize":this.sessions.resize(r);return;case"paste_file":this.savePaste(u(r),r);return;case"host_font_chunk_request":this.font.sendChunk(u(r),r);return;case"host_font_subset_request":this.font.sendSubset(u(r),r);return;case"host_font_refresh":this.font.refresh();return;default:return}}handleFrame(e){let t;try{t=W(e)}catch(r){process.stderr.write(`herdr-remote host connector: invalid relay frame: ${r.message}
19
+ `);return}t.type==="input"&&this.sessions.write(t.version===2?{streamIndex:t.streamIndex}:{streamId:t.streamId},t.payload)}hostReady(e){if(this.reconnectAttempts=0,this.startKeepalive(),Object.hasOwn(e,"clientCount")){this.legacyHeartbeat=!1,this.setClientCount(e.clientCount);return}this.legacyHeartbeat=!0,this.sendHeartbeat(!0),this.heartbeatTimer=setInterval(()=>this.sendHeartbeat(),this.config.cleanup.heartbeatIntervalMs)}startSession(e){if(u(e))return this.updates.report(),this.font.pickUp(),this.sessions.start(e)}savePaste(e,t){try{let r=Ie({mime:t.mime,dataBase64:t.dataBase64});m(this.ws,{type:"paste_file_ready",clientId:e,path:r})}catch(r){m(this.ws,{type:"error",clientId:e,code:"paste_file_write_failed",message:r.message||"Failed to save pasted file"})}}startKeepalive(){if(this.stopKeepalive(),!this.ws||this.stopping)return;this.ws.isAlive=!0;let e=this.config?.cleanup?.heartbeatIntervalMs||3e4;this.keepaliveTimer=setInterval(()=>this.tickKeepalive(),e)}stopKeepalive(){this.keepaliveTimer&&(clearInterval(this.keepaliveTimer),this.keepaliveTimer=null)}tickKeepalive(){let e=this.ws;if(!(!e||this.stopping)){if(e.readyState===d.default.CLOSING){U(e);return}if(e.readyState===d.default.OPEN){if(!e.isAlive){U(e);return}e.isAlive=!1;try{typeof e.ping=="function"&&e.ping()}catch{}this.clientCount<=0&&!this.legacyHeartbeat&&m(e,{type:"heartbeat",load:{},ptys:[]})}}}setClientCount(e){let t=Number.isInteger(e)?Math.max(0,e):0;if(t===this.clientCount&&(t===0||this.heartbeatTimer))return;let r=this.clientCount>0;this.clientCount=t,this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.heartbeatTimer=null,t>0?(this.sendHeartbeat(!0),this.heartbeatTimer=setInterval(()=>this.sendHeartbeat(),this.config.cleanup.heartbeatIntervalMs)):r&&this.sendHeartbeat(!0),t>0&&!this.stopping?this.agents.start():this.agents.stop()}sendHeartbeat(e=!1){if(!e&&this.clientCount<=0&&!this.legacyHeartbeat)return;let t=process.memoryUsage(),r=Pt.loadavg();m(this.ws,{type:"heartbeat",load:{load1m:r[0]||0,load5m:r[1]||0,load15m:r[2]||0,rssBytes:t.rss,heapUsedBytes:t.heapUsed},ptys:this.sessions.summaries()})}};var Ue=new x({onFatal:s=>process.exit(s)});try{Ue.start()}catch(s){let{code:e,message:t}=s;process.stderr.write(`herdr-remote host connector failed: ${t}
20
+ `),process.exitCode=e==="HOST_ALREADY_RUNNING"?12:1}var $e=()=>{Ue.stop(),process.exit(0)};process.once("SIGINT",$e);process.once("SIGTERM",$e);
Binary file
@@ -0,0 +1,17 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createRequire } from 'node:module';
4
+ try {
5
+ const root = path.dirname(createRequire(import.meta.url).resolve('node-pty/package.json'));
6
+ const prebuilds = path.join(root, 'prebuilds');
7
+ const own = process.platform + '-' + process.arch;
8
+ for (const name of fs.existsSync(prebuilds) ? fs.readdirSync(prebuilds) : []) {
9
+ const directory = path.join(prebuilds, name);
10
+ if (name !== own) fs.rmSync(directory, { recursive: true, force: true });
11
+ else
12
+ for (const file of fs.readdirSync(directory, { recursive: true }))
13
+ if (String(file).endsWith('.pdb')) fs.rmSync(path.join(directory, String(file)), { force: true });
14
+ }
15
+ } catch (error) {
16
+ process.stderr.write('herdr-remote: could not prune node-pty prebuilds: ' + error.message + '\n');
17
+ }
package/herdr-plugin.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  id = "herdr.remote.web"
2
2
  name = "Herdr Remote Web"
3
- version = "0.2.38"
3
+ version = "0.3.31"
4
4
  min_herdr_version = "0.9.3"
5
5
  description = "Browser access to Herdr workspaces via local or self-hosted relay"
6
6
  platforms = ["linux", "macos", "windows"]
@@ -1,17 +1,21 @@
1
1
  #!/usr/bin/env node
2
-
3
- // Standalone entry point for the Herdr Remote relay.
4
- //
5
- // This binary has no knowledge of the herdr-remote plugin: it can be installed
6
- // and run on its own host (`npm i -g herdr-remote-relay`) with nothing but
7
- // environment variables for configuration.
8
-
9
- import fs from 'node:fs';
10
- import path from 'node:path';
11
- import { loadRelayConfig, PACKAGE_ROOT } from '../dist/relay-config.js';
12
- import { RelayServer, VERSION, PROTOCOL_VERSION } from '../dist/relay-server.js';
13
-
14
- const USAGE = `herdr-remote-relay ${VERSION} — standalone relay for Herdr Remote
2
+ import { createRequire as __herdrCreateRequire } from 'node:module';
3
+ const require = __herdrCreateRequire(import.meta.url);
4
+ var an=Object.create;var is=Object.defineProperty;var ln=Object.getOwnPropertyDescriptor;var cn=Object.getOwnPropertyNames;var fn=Object.getPrototypeOf,un=Object.prototype.hasOwnProperty;var S=(t=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(t,{get:(e,s)=>(typeof require<"u"?require:e)[s]}):t)(function(t){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+t+'" is not supported')});var v=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var dn=(t,e,s,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let n of cn(e))!un.call(t,n)&&n!==s&&is(t,n,{get:()=>e[n],enumerable:!(r=ln(e,n))||r.enumerable});return t};var G=(t,e,s)=>(s=t!=null?an(fn(t)):{},dn(e||!t||!t.__esModule?is(s,"default",{value:t,enumerable:!0}):s,t));var F=v((sa,cs)=>{"use strict";var as=["nodebuffer","arraybuffer","fragments"],ls=typeof Blob<"u";ls&&as.push("blob");cs.exports={BINARY_TYPES:as,CLOSE_TIMEOUT:3e4,EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",hasBlob:ls,kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}}});var Ae=v((ra,Ue)=>{"use strict";var{EMPTY_BUFFER:Tn}=F(),pt=Buffer[Symbol.species];function wn(t,e){if(t.length===0)return Tn;if(t.length===1)return t[0];let s=Buffer.allocUnsafe(e),r=0;for(let n=0;n<t.length;n++){let i=t[n];s.set(i,r),r+=i.length}return r<e?new pt(s.buffer,s.byteOffset,r):s}function fs(t,e,s,r,n){for(let i=0;i<n;i++)s[r+i]=t[i]^e[i&3]}function us(t,e){for(let s=0;s<t.length;s++)t[s]^=e[s&3]}function An(t){return t.length===t.buffer.byteLength?t.buffer:t.buffer.slice(t.byteOffset,t.byteOffset+t.length)}function _t(t){if(_t.readOnly=!0,Buffer.isBuffer(t))return t;let e;return t instanceof ArrayBuffer?e=new pt(t):ArrayBuffer.isView(t)?e=new pt(t.buffer,t.byteOffset,t.byteLength):(e=Buffer.from(t),_t.readOnly=!1),e}Ue.exports={concat:wn,mask:fs,toArrayBuffer:An,toBuffer:_t,unmask:us};if(!process.env.WS_NO_BUFFER_UTIL)try{let t=S("bufferutil");Ue.exports.mask=function(e,s,r,n,i){i<48?fs(e,s,r,n,i):t.mask(e,s,r,n,i)},Ue.exports.unmask=function(e,s){e.length<32?us(e,s):t.unmask(e,s)}}catch{}});var ms=v((na,hs)=>{"use strict";var ds=Symbol("kDone"),yt=Symbol("kRun"),gt=class{constructor(e){this[ds]=()=>{this.pending--,this[yt]()},this.concurrency=e||1/0,this.jobs=[],this.pending=0}add(e){this.jobs.push(e),this[yt]()}[yt](){if(this.pending!==this.concurrency&&this.jobs.length){let e=this.jobs.shift();this.pending++,e(this[ds])}}};hs.exports=gt});var fe=v((ia,gs)=>{"use strict";var ve=S("zlib"),ps=Ae(),vn=ms(),{kStatusCode:_s}=F(),In=Buffer[Symbol.species],On=Buffer.from([0,0,255,255]),He=Symbol("permessage-deflate"),H=Symbol("total-length"),le=Symbol("callback"),j=Symbol("buffers"),ce=Symbol("error"),Fe,St=class{constructor(e){if(this._options=e||{},this._threshold=this._options.threshold!==void 0?this._options.threshold:1024,this._maxPayload=this._options.maxPayload|0,this._isServer=!!this._options.isServer,this._deflate=null,this._inflate=null,this.params=null,!Fe){let s=this._options.concurrencyLimit!==void 0?this._options.concurrencyLimit:10;Fe=new vn(s)}}static get extensionName(){return"permessage-deflate"}offer(){let e={};return this._options.serverNoContextTakeover&&(e.server_no_context_takeover=!0),this._options.clientNoContextTakeover&&(e.client_no_context_takeover=!0),this._options.serverMaxWindowBits&&(e.server_max_window_bits=this._options.serverMaxWindowBits),this._options.clientMaxWindowBits?e.client_max_window_bits=this._options.clientMaxWindowBits:this._options.clientMaxWindowBits==null&&(e.client_max_window_bits=!0),e}accept(e){return e=this.normalizeParams(e),this.params=this._isServer?this.acceptAsServer(e):this.acceptAsClient(e),this.params}cleanup(){if(this._inflate&&(this._inflate.close(),this._inflate=null),this._deflate){let e=this._deflate[le];this._deflate.close(),this._deflate=null,e&&e(new Error("The deflate stream was closed while data was being processed"))}}acceptAsServer(e){let s=this._options,r=e.find(n=>!(s.serverNoContextTakeover===!1&&n.server_no_context_takeover||n.server_max_window_bits&&(s.serverMaxWindowBits===!1||typeof s.serverMaxWindowBits=="number"&&s.serverMaxWindowBits>n.server_max_window_bits)||typeof s.clientMaxWindowBits=="number"&&(typeof n.client_max_window_bits=="number"?s.clientMaxWindowBits>n.client_max_window_bits:!n.client_max_window_bits)));if(!r)throw new Error("None of the extension offers can be accepted");return s.serverNoContextTakeover&&(r.server_no_context_takeover=!0),s.clientNoContextTakeover&&(r.client_no_context_takeover=!0),typeof s.serverMaxWindowBits=="number"&&(r.server_max_window_bits=s.serverMaxWindowBits),typeof s.clientMaxWindowBits=="number"?r.client_max_window_bits=s.clientMaxWindowBits:(r.client_max_window_bits===!0||s.clientMaxWindowBits===!1)&&delete r.client_max_window_bits,r}acceptAsClient(e){let s=e[0];if(this._options.clientNoContextTakeover===!1&&s.client_no_context_takeover)throw new Error('Unexpected parameter "client_no_context_takeover"');if(!s.client_max_window_bits)typeof this._options.clientMaxWindowBits=="number"&&(s.client_max_window_bits=this._options.clientMaxWindowBits);else if(this._options.clientMaxWindowBits===!1||typeof this._options.clientMaxWindowBits=="number"&&s.client_max_window_bits>this._options.clientMaxWindowBits)throw new Error('Unexpected or invalid parameter "client_max_window_bits"');return s}normalizeParams(e){return e.forEach(s=>{Object.keys(s).forEach(r=>{let n=s[r];if(n.length>1)throw new Error(`Parameter "${r}" must have only a single value`);if(n=n[0],r==="client_max_window_bits"){if(n!==!0){let i=+n;if(!Number.isInteger(i)||i<8||i>15)throw new TypeError(`Invalid value for parameter "${r}": ${n}`);n=i}else if(!this._isServer)throw new TypeError(`Invalid value for parameter "${r}": ${n}`)}else if(r==="server_max_window_bits"){let i=+n;if(!Number.isInteger(i)||i<8||i>15)throw new TypeError(`Invalid value for parameter "${r}": ${n}`);n=i}else if(r==="client_no_context_takeover"||r==="server_no_context_takeover"){if(n!==!0)throw new TypeError(`Invalid value for parameter "${r}": ${n}`)}else throw new Error(`Unknown parameter "${r}"`);s[r]=n})}),e}decompress(e,s,r){Fe.add(n=>{this._decompress(e,s,(i,o)=>{n(),r(i,o)})})}compress(e,s,r){Fe.add(n=>{this._compress(e,s,(i,o)=>{n(),r(i,o)})})}_decompress(e,s,r){let n=this._isServer?"client":"server";if(!this._inflate){let i=`${n}_max_window_bits`,o=typeof this.params[i]!="number"?ve.Z_DEFAULT_WINDOWBITS:this.params[i];this._inflate=ve.createInflateRaw({...this._options.zlibInflateOptions,windowBits:o}),this._inflate[He]=this,this._inflate[H]=0,this._inflate[j]=[],this._inflate.on("error",Pn),this._inflate.on("data",ys)}this._inflate[le]=r,this._inflate.write(e),s&&this._inflate.write(On),this._inflate.flush(()=>{let i=this._inflate[ce];if(i){this._inflate.close(),this._inflate=null,r(i);return}let o=ps.concat(this._inflate[j],this._inflate[H]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[H]=0,this._inflate[j]=[],s&&this.params[`${n}_no_context_takeover`]&&this._inflate.reset()),r(null,o)})}_compress(e,s,r){let n=this._isServer?"server":"client";if(!this._deflate){let i=`${n}_max_window_bits`,o=typeof this.params[i]!="number"?ve.Z_DEFAULT_WINDOWBITS:this.params[i];this._deflate=ve.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:o}),this._deflate[H]=0,this._deflate[j]=[],this._deflate.on("data",Cn)}this._deflate[le]=r,this._deflate.write(e),this._deflate.flush(ve.Z_SYNC_FLUSH,()=>{if(!this._deflate)return;let i=ps.concat(this._deflate[j],this._deflate[H]);s&&(i=new In(i.buffer,i.byteOffset,i.length-4)),this._deflate[le]=null,this._deflate[H]=0,this._deflate[j]=[],s&&this.params[`${n}_no_context_takeover`]&&this._deflate.reset(),r(null,i)})}};gs.exports=St;function Cn(t){this[j].push(t),this[H]+=t.length}function ys(t){if(this[H]+=t.length,this[He]._maxPayload<1||this[H]<=this[He]._maxPayload){this[j].push(t);return}this[ce]=new RangeError("Max payload size exceeded"),this[ce].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[ce][_s]=1009,this.removeListener("data",ys),this.reset()}function Pn(t){if(this[He]._inflate=null,this[ce]){this[le](this[ce]);return}t[_s]=1007,this[le](t)}});var ue=v((oa,Ye)=>{"use strict";var{isUtf8:Ss}=S("buffer"),{hasBlob:Rn}=F(),kn=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0];function Ln(t){return t>=1e3&&t<=1014&&t!==1004&&t!==1005&&t!==1006||t>=3e3&&t<=4999}function xt(t){let e=t.length,s=0;for(;s<e;)if((t[s]&128)===0)s++;else if((t[s]&224)===192){if(s+1===e||(t[s+1]&192)!==128||(t[s]&254)===192)return!1;s+=2}else if((t[s]&240)===224){if(s+2>=e||(t[s+1]&192)!==128||(t[s+2]&192)!==128||t[s]===224&&(t[s+1]&224)===128||t[s]===237&&(t[s+1]&224)===160)return!1;s+=3}else if((t[s]&248)===240){if(s+3>=e||(t[s+1]&192)!==128||(t[s+2]&192)!==128||(t[s+3]&192)!==128||t[s]===240&&(t[s+1]&240)===128||t[s]===244&&t[s+1]>143||t[s]>244)return!1;s+=4}else return!1;return!0}function Nn(t){return Rn&&typeof t=="object"&&typeof t.arrayBuffer=="function"&&typeof t.type=="string"&&typeof t.stream=="function"&&(t[Symbol.toStringTag]==="Blob"||t[Symbol.toStringTag]==="File")}Ye.exports={isBlob:Nn,isValidStatusCode:Ln,isValidUTF8:xt,tokenChars:kn};if(Ss)Ye.exports.isValidUTF8=function(t){return t.length<24?xt(t):Ss(t)};else if(!process.env.WS_NO_UTF_8_VALIDATE)try{let t=S("utf-8-validate");Ye.exports.isValidUTF8=function(e){return e.length<32?xt(e):t(e)}}catch{}});var At=v((aa,vs)=>{"use strict";var{Writable:Mn}=S("stream"),xs=fe(),{BINARY_TYPES:Dn,EMPTY_BUFFER:Es,kStatusCode:Bn,kWebSocket:Un}=F(),{concat:Et,toArrayBuffer:Fn,unmask:Hn}=Ae(),{isValidStatusCode:Yn,isValidUTF8:bs}=ue(),$e=Buffer[Symbol.species],P=0,Ts=1,ws=2,As=3,bt=4,Tt=5,We=6,wt=class extends Mn{constructor(e={}){super(),this._allowSynchronousEvents=e.allowSynchronousEvents!==void 0?e.allowSynchronousEvents:!0,this._binaryType=e.binaryType||Dn[0],this._extensions=e.extensions||{},this._isServer=!!e.isServer,this._maxBufferedChunks=e.maxBufferedChunks|0,this._maxFragments=e.maxFragments|0,this._maxPayload=e.maxPayload|0,this._skipUTF8Validation=!!e.skipUTF8Validation,this[Un]=void 0,this._bufferedBytes=0,this._buffers=[],this._compressed=!1,this._payloadLength=0,this._mask=void 0,this._fragmented=0,this._masked=!1,this._fin=!1,this._opcode=0,this._totalPayloadLength=0,this._messageLength=0,this._numFragments=0,this._fragments=[],this._errored=!1,this._loop=!1,this._state=P}_write(e,s,r){if(this._opcode===8&&this._state==P)return r();if(this._maxBufferedChunks>0&&this._buffers.length>=this._maxBufferedChunks){r(this.createError(RangeError,"Too many buffered chunks",!1,1008,"WS_ERR_TOO_MANY_BUFFERED_PARTS"));return}this._bufferedBytes+=e.length,this._buffers.push(e),this.startLoop(r)}consume(e){if(this._bufferedBytes-=e,e===this._buffers[0].length)return this._buffers.shift();if(e<this._buffers[0].length){let r=this._buffers[0];return this._buffers[0]=new $e(r.buffer,r.byteOffset+e,r.length-e),new $e(r.buffer,r.byteOffset,e)}let s=Buffer.allocUnsafe(e);do{let r=this._buffers[0],n=s.length-e;e>=r.length?s.set(this._buffers.shift(),n):(s.set(new Uint8Array(r.buffer,r.byteOffset,e),n),this._buffers[0]=new $e(r.buffer,r.byteOffset+e,r.length-e)),e-=r.length}while(e>0);return s}startLoop(e){this._loop=!0;do switch(this._state){case P:this.getInfo(e);break;case Ts:this.getPayloadLength16(e);break;case ws:this.getPayloadLength64(e);break;case As:this.getMask();break;case bt:this.getData(e);break;case Tt:case We:this._loop=!1;return}while(this._loop);this._errored||e()}getInfo(e){if(this._bufferedBytes<2){this._loop=!1;return}let s=this.consume(2);if((s[0]&48)!==0){let n=this.createError(RangeError,"RSV2 and RSV3 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_2_3");e(n);return}let r=(s[0]&64)===64;if(r&&!this._extensions[xs.extensionName]){let n=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");e(n);return}if(this._fin=(s[0]&128)===128,this._opcode=s[0]&15,this._payloadLength=s[1]&127,this._opcode===0){if(r){let n=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");e(n);return}if(!this._fragmented){let n=this.createError(RangeError,"invalid opcode 0",!0,1002,"WS_ERR_INVALID_OPCODE");e(n);return}this._opcode=this._fragmented}else if(this._opcode===1||this._opcode===2){if(this._fragmented){let n=this.createError(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");e(n);return}this._compressed=r}else if(this._opcode>7&&this._opcode<11){if(!this._fin){let n=this.createError(RangeError,"FIN must be set",!0,1002,"WS_ERR_EXPECTED_FIN");e(n);return}if(r){let n=this.createError(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");e(n);return}if(this._payloadLength>125||this._opcode===8&&this._payloadLength===1){let n=this.createError(RangeError,`invalid payload length ${this._payloadLength}`,!0,1002,"WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH");e(n);return}}else{let n=this.createError(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");e(n);return}if(!this._fin&&!this._fragmented&&(this._fragmented=this._opcode),this._masked=(s[1]&128)===128,this._isServer){if(!this._masked){let n=this.createError(RangeError,"MASK must be set",!0,1002,"WS_ERR_EXPECTED_MASK");e(n);return}}else if(this._masked){let n=this.createError(RangeError,"MASK must be clear",!0,1002,"WS_ERR_UNEXPECTED_MASK");e(n);return}this._payloadLength===126?this._state=Ts:this._payloadLength===127?this._state=ws:this.haveLength(e)}getPayloadLength16(e){if(this._bufferedBytes<2){this._loop=!1;return}this._payloadLength=this.consume(2).readUInt16BE(0),this.haveLength(e)}getPayloadLength64(e){if(this._bufferedBytes<8){this._loop=!1;return}let s=this.consume(8),r=s.readUInt32BE(0);if(r>Math.pow(2,21)-1){let n=this.createError(RangeError,"Unsupported WebSocket frame: payload length > 2^53 - 1",!1,1009,"WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH");e(n);return}this._payloadLength=r*Math.pow(2,32)+s.readUInt32BE(4),this.haveLength(e)}haveLength(e){if(this._payloadLength&&this._opcode<8&&(this._totalPayloadLength+=this._payloadLength,this._totalPayloadLength>this._maxPayload&&this._maxPayload>0)){let s=this.createError(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");e(s);return}this._masked?this._state=As:this._state=bt}getMask(){if(this._bufferedBytes<4){this._loop=!1;return}this._mask=this.consume(4),this._state=bt}getData(e){let s=Es;if(this._payloadLength){if(this._bufferedBytes<this._payloadLength){this._loop=!1;return}s=this.consume(this._payloadLength),this._masked&&(this._mask[0]|this._mask[1]|this._mask[2]|this._mask[3])!==0&&Hn(s,this._mask)}if(this._opcode>7){this.controlMessage(s,e);return}if(this._maxFragments>0&&++this._numFragments>this._maxFragments){let r=this.createError(RangeError,"Too many message fragments",!1,1008,"WS_ERR_TOO_MANY_BUFFERED_PARTS");e(r);return}if(this._compressed){this._state=Tt,this.decompress(s,e);return}s.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(s)),this.dataMessage(e)}decompress(e,s){this._extensions[xs.extensionName].decompress(e,this._fin,(n,i)=>{if(n)return s(n);if(i.length){if(this._messageLength+=i.length,this._messageLength>this._maxPayload&&this._maxPayload>0){let o=this.createError(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");s(o);return}this._fragments.push(i)}this.dataMessage(s),this._state===P&&this.startLoop(s)})}dataMessage(e){if(!this._fin){this._state=P;return}let s=this._messageLength,r=this._fragments;if(this._totalPayloadLength=0,this._messageLength=0,this._fragmented=0,this._numFragments=0,this._fragments=[],this._opcode===2){let n;this._binaryType==="nodebuffer"?n=Et(r,s):this._binaryType==="arraybuffer"?n=Fn(Et(r,s)):this._binaryType==="blob"?n=new Blob(r):n=r,this._allowSynchronousEvents?(this.emit("message",n,!0),this._state=P):(this._state=We,setImmediate(()=>{this.emit("message",n,!0),this._state=P,this.startLoop(e)}))}else{let n=Et(r,s);if(!this._skipUTF8Validation&&!bs(n)){let i=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");e(i);return}this._state===Tt||this._allowSynchronousEvents?(this.emit("message",n,!1),this._state=P):(this._state=We,setImmediate(()=>{this.emit("message",n,!1),this._state=P,this.startLoop(e)}))}}controlMessage(e,s){if(this._opcode===8){if(e.length===0)this._loop=!1,this.emit("conclude",1005,Es),this.end();else{let r=e.readUInt16BE(0);if(!Yn(r)){let i=this.createError(RangeError,`invalid status code ${r}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");s(i);return}let n=new $e(e.buffer,e.byteOffset+2,e.length-2);if(!this._skipUTF8Validation&&!bs(n)){let i=this.createError(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");s(i);return}this._loop=!1,this.emit("conclude",r,n),this.end()}this._state=P;return}this._allowSynchronousEvents?(this.emit(this._opcode===9?"ping":"pong",e),this._state=P):(this._state=We,setImmediate(()=>{this.emit(this._opcode===9?"ping":"pong",e),this._state=P,this.startLoop(s)}))}createError(e,s,r,n,i){this._loop=!1,this._errored=!0;let o=new e(r?`Invalid WebSocket frame: ${s}`:s);return Error.captureStackTrace(o,this.createError),o.code=i,o[Bn]=n,o}};vs.exports=wt});var Ot=v((ca,Cs)=>{"use strict";var{Duplex:la}=S("stream"),{randomFillSync:$n}=S("crypto"),{types:{isUint8Array:Wn}}=S("util"),Is=fe(),{EMPTY_BUFFER:Gn,kWebSocket:jn,NOOP:Vn}=F(),{isBlob:de,isValidStatusCode:qn}=ue(),{mask:Os,toBuffer:K}=Ae(),R=Symbol("kByteLength"),zn=Buffer.alloc(4),Ge=8*1024,J,he=Ge,L=0,Xn=1,Kn=2,vt=class t{constructor(e,s,r){this._extensions=s||{},r&&(this._generateMask=r,this._maskBuffer=Buffer.alloc(4)),this._socket=e,this._firstFragment=!0,this._compress=!1,this._bufferedBytes=0,this._queue=[],this._state=L,this.onerror=Vn,this[jn]=void 0}static frame(e,s){let r,n=!1,i=2,o=!1;s.mask&&(r=s.maskBuffer||zn,s.generateMask?s.generateMask(r):(he===Ge&&(J===void 0&&(J=Buffer.alloc(Ge)),$n(J,0,Ge),he=0),r[0]=J[he++],r[1]=J[he++],r[2]=J[he++],r[3]=J[he++]),o=(r[0]|r[1]|r[2]|r[3])===0,i=6);let a;typeof e=="string"?(!s.mask||o)&&s[R]!==void 0?a=s[R]:(e=Buffer.from(e),a=e.length):(a=e.length,n=s.mask&&s.readOnly&&!o);let l=a;a>=65536?(i+=8,l=127):a>125&&(i+=2,l=126);let c=Buffer.allocUnsafe(n?a+i:i);return c[0]=s.fin?s.opcode|128:s.opcode,s.rsv1&&(c[0]|=64),c[1]=l,l===126?c.writeUInt16BE(a,2):l===127&&(c[2]=c[3]=0,c.writeUIntBE(a,4,6)),s.mask?(c[1]|=128,c[i-4]=r[0],c[i-3]=r[1],c[i-2]=r[2],c[i-1]=r[3],o?[c,e]:n?(Os(e,r,c,i,a),[c]):(Os(e,r,e,0,a),[c,e])):[c,e]}close(e,s,r,n){let i;if(e===void 0)i=Gn;else{if(typeof e!="number"||!qn(e))throw new TypeError("First argument must be a valid error code number");if(s===void 0||!s.length)i=Buffer.allocUnsafe(2),i.writeUInt16BE(e,0);else{let a=Buffer.byteLength(s);if(a>123)throw new RangeError("The message must not be greater than 123 bytes");if(i=Buffer.allocUnsafe(2+a),i.writeUInt16BE(e,0),typeof s=="string")i.write(s,2);else if(Wn(s))i.set(s,2);else throw new TypeError("Second argument must be a string or a Uint8Array")}}let o={[R]:i.length,fin:!0,generateMask:this._generateMask,mask:r,maskBuffer:this._maskBuffer,opcode:8,readOnly:!1,rsv1:!1};this._state!==L?this.enqueue([this.dispatch,i,!1,o,n]):this.sendFrame(t.frame(i,o),n)}ping(e,s,r){let n,i;if(typeof e=="string"?(n=Buffer.byteLength(e),i=!1):de(e)?(n=e.size,i=!1):(e=K(e),n=e.length,i=K.readOnly),n>125)throw new RangeError("The data size must not be greater than 125 bytes");let o={[R]:n,fin:!0,generateMask:this._generateMask,mask:s,maskBuffer:this._maskBuffer,opcode:9,readOnly:i,rsv1:!1};de(e)?this._state!==L?this.enqueue([this.getBlobData,e,!1,o,r]):this.getBlobData(e,!1,o,r):this._state!==L?this.enqueue([this.dispatch,e,!1,o,r]):this.sendFrame(t.frame(e,o),r)}pong(e,s,r){let n,i;if(typeof e=="string"?(n=Buffer.byteLength(e),i=!1):de(e)?(n=e.size,i=!1):(e=K(e),n=e.length,i=K.readOnly),n>125)throw new RangeError("The data size must not be greater than 125 bytes");let o={[R]:n,fin:!0,generateMask:this._generateMask,mask:s,maskBuffer:this._maskBuffer,opcode:10,readOnly:i,rsv1:!1};de(e)?this._state!==L?this.enqueue([this.getBlobData,e,!1,o,r]):this.getBlobData(e,!1,o,r):this._state!==L?this.enqueue([this.dispatch,e,!1,o,r]):this.sendFrame(t.frame(e,o),r)}send(e,s,r){let n=this._extensions[Is.extensionName],i=s.binary?2:1,o=s.compress,a,l;typeof e=="string"?(a=Buffer.byteLength(e),l=!1):de(e)?(a=e.size,l=!1):(e=K(e),a=e.length,l=K.readOnly),this._firstFragment?(this._firstFragment=!1,o&&n&&n.params[n._isServer?"server_no_context_takeover":"client_no_context_takeover"]&&(o=a>=n._threshold),this._compress=o):(o=!1,i=0),s.fin&&(this._firstFragment=!0);let c={[R]:a,fin:s.fin,generateMask:this._generateMask,mask:s.mask,maskBuffer:this._maskBuffer,opcode:i,readOnly:l,rsv1:o};de(e)?this._state!==L?this.enqueue([this.getBlobData,e,this._compress,c,r]):this.getBlobData(e,this._compress,c,r):this._state!==L?this.enqueue([this.dispatch,e,this._compress,c,r]):this.dispatch(e,this._compress,c,r)}getBlobData(e,s,r,n){this._bufferedBytes+=r[R],this._state=Kn,e.arrayBuffer().then(i=>{if(this._socket.destroyed){let a=new Error("The socket was closed while the blob was being read");process.nextTick(It,this,a,n);return}this._bufferedBytes-=r[R];let o=K(i);s?this.dispatch(o,s,r,n):(this._state=L,this.sendFrame(t.frame(o,r),n),this.dequeue())}).catch(i=>{process.nextTick(Jn,this,i,n)})}dispatch(e,s,r,n){if(!s){this.sendFrame(t.frame(e,r),n);return}let i=this._extensions[Is.extensionName];this._bufferedBytes+=r[R],this._state=Xn,i.compress(e,r.fin,(o,a)=>{if(this._socket.destroyed){let l=new Error("The socket was closed while data was being compressed");It(this,l,n);return}this._bufferedBytes-=r[R],this._state=L,r.readOnly=!1,this.sendFrame(t.frame(a,r),n),this.dequeue()})}dequeue(){for(;this._state===L&&this._queue.length;){let e=this._queue.shift();this._bufferedBytes-=e[3][R],Reflect.apply(e[0],this,e.slice(1))}}enqueue(e){this._bufferedBytes+=e[3][R],this._queue.push(e)}sendFrame(e,s){e.length===2?(this._socket.cork(),this._socket.write(e[0]),this._socket.write(e[1],s),this._socket.uncork()):this._socket.write(e[0],s)}};Cs.exports=vt;function It(t,e,s){typeof s=="function"&&s(e);for(let r=0;r<t._queue.length;r++){let n=t._queue[r],i=n[n.length-1];typeof i=="function"&&i(e)}}function Jn(t,e,s){It(t,e,s),t.onerror(e)}});var Us=v((fa,Bs)=>{"use strict";var{kForOnEventAttribute:Ie,kListener:Ct}=F(),Ps=Symbol("kCode"),Rs=Symbol("kData"),ks=Symbol("kError"),Ls=Symbol("kMessage"),Ns=Symbol("kReason"),me=Symbol("kTarget"),Ms=Symbol("kType"),Ds=Symbol("kWasClean"),Y=class{constructor(e){this[me]=null,this[Ms]=e}get target(){return this[me]}get type(){return this[Ms]}};Object.defineProperty(Y.prototype,"target",{enumerable:!0});Object.defineProperty(Y.prototype,"type",{enumerable:!0});var Z=class extends Y{constructor(e,s={}){super(e),this[Ps]=s.code===void 0?0:s.code,this[Ns]=s.reason===void 0?"":s.reason,this[Ds]=s.wasClean===void 0?!1:s.wasClean}get code(){return this[Ps]}get reason(){return this[Ns]}get wasClean(){return this[Ds]}};Object.defineProperty(Z.prototype,"code",{enumerable:!0});Object.defineProperty(Z.prototype,"reason",{enumerable:!0});Object.defineProperty(Z.prototype,"wasClean",{enumerable:!0});var pe=class extends Y{constructor(e,s={}){super(e),this[ks]=s.error===void 0?null:s.error,this[Ls]=s.message===void 0?"":s.message}get error(){return this[ks]}get message(){return this[Ls]}};Object.defineProperty(pe.prototype,"error",{enumerable:!0});Object.defineProperty(pe.prototype,"message",{enumerable:!0});var Oe=class extends Y{constructor(e,s={}){super(e),this[Rs]=s.data===void 0?null:s.data}get data(){return this[Rs]}};Object.defineProperty(Oe.prototype,"data",{enumerable:!0});var Zn={addEventListener(t,e,s={}){for(let n of this.listeners(t))if(!s[Ie]&&n[Ct]===e&&!n[Ie])return;let r;if(t==="message")r=function(i,o){let a=new Oe("message",{data:o?i:i.toString()});a[me]=this,je(e,this,a)};else if(t==="close")r=function(i,o){let a=new Z("close",{code:i,reason:o.toString(),wasClean:this._closeFrameReceived&&this._closeFrameSent});a[me]=this,je(e,this,a)};else if(t==="error")r=function(i){let o=new pe("error",{error:i,message:i.message});o[me]=this,je(e,this,o)};else if(t==="open")r=function(){let i=new Y("open");i[me]=this,je(e,this,i)};else return;r[Ie]=!!s[Ie],r[Ct]=e,s.once?this.once(t,r):this.on(t,r)},removeEventListener(t,e){for(let s of this.listeners(t))if(s[Ct]===e&&!s[Ie]){this.removeListener(t,s);break}}};Bs.exports={CloseEvent:Z,ErrorEvent:pe,Event:Y,EventTarget:Zn,MessageEvent:Oe};function je(t,e,s){typeof t=="object"&&t.handleEvent?t.handleEvent.call(t,s):t.call(e,s)}});var Ve=v((ua,Fs)=>{"use strict";var{tokenChars:Ce}=ue();function D(t,e,s){t[e]===void 0?t[e]=[s]:t[e].push(s)}function Qn(t){let e=Object.create(null),s=Object.create(null),r=!1,n=!1,i=!1,o,a,l=-1,c=-1,f=-1,d=0;for(;d<t.length;d++)if(c=t.charCodeAt(d),o===void 0)if(f===-1&&Ce[c]===1)l===-1&&(l=d);else if(d!==0&&(c===32||c===9))f===-1&&l!==-1&&(f=d);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${d}`);f===-1&&(f=d);let h=t.slice(l,f);c===44?(D(e,h,s),s=Object.create(null)):o=h,l=f=-1}else throw new SyntaxError(`Unexpected character at index ${d}`);else if(a===void 0)if(f===-1&&Ce[c]===1)l===-1&&(l=d);else if(c===32||c===9)f===-1&&l!==-1&&(f=d);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${d}`);f===-1&&(f=d),D(s,t.slice(l,f),!0),c===44&&(D(e,o,s),s=Object.create(null),o=void 0),l=f=-1}else if(c===61&&l!==-1&&f===-1)a=t.slice(l,d),l=f=-1;else throw new SyntaxError(`Unexpected character at index ${d}`);else if(n){if(Ce[c]!==1)throw new SyntaxError(`Unexpected character at index ${d}`);l===-1?l=d:r||(r=!0),n=!1}else if(i)if(Ce[c]===1)l===-1&&(l=d);else if(c===34&&l!==-1)i=!1,f=d;else if(c===92)n=!0;else throw new SyntaxError(`Unexpected character at index ${d}`);else if(c===34&&t.charCodeAt(d-1)===61)i=!0;else if(f===-1&&Ce[c]===1)l===-1&&(l=d);else if(l!==-1&&(c===32||c===9))f===-1&&(f=d);else if(c===59||c===44){if(l===-1)throw new SyntaxError(`Unexpected character at index ${d}`);f===-1&&(f=d);let h=t.slice(l,f);r&&(h=h.replace(/\\/g,""),r=!1),D(s,a,h),c===44&&(D(e,o,s),s=Object.create(null),o=void 0),a=void 0,l=f=-1}else throw new SyntaxError(`Unexpected character at index ${d}`);if(l===-1||i||c===32||c===9)throw new SyntaxError("Unexpected end of input");f===-1&&(f=d);let u=t.slice(l,f);return o===void 0?D(e,u,s):(a===void 0?D(s,u,!0):r?D(s,a,u.replace(/\\/g,"")):D(s,a,u),D(e,o,s)),e}function ei(t){return Object.keys(t).map(e=>{let s=t[e];return Array.isArray(s)||(s=[s]),s.map(r=>[e].concat(Object.keys(r).map(n=>{let i=r[n];return Array.isArray(i)||(i=[i]),i.map(o=>o===!0?n:`${n}=${o}`).join("; ")})).join("; ")).join(", ")}).join(", ")}Fs.exports={format:ei,parse:Qn}});var Ke=v((ma,Js)=>{"use strict";var ti=S("events"),si=S("https"),ri=S("http"),$s=S("net"),ni=S("tls"),{randomBytes:ii,createHash:oi}=S("crypto"),{Duplex:da,Readable:ha}=S("stream"),{URL:Pt}=S("url"),V=fe(),ai=At(),li=Ot(),{isBlob:ci}=ue(),{BINARY_TYPES:Hs,CLOSE_TIMEOUT:fi,EMPTY_BUFFER:qe,GUID:ui,kForOnEventAttribute:Rt,kListener:di,kStatusCode:hi,kWebSocket:E,NOOP:Ws}=F(),{EventTarget:{addEventListener:mi,removeEventListener:pi}}=Us(),{format:_i,parse:yi}=Ve(),{toBuffer:gi}=Ae(),Gs=Symbol("kAborted"),kt=[8,13],$=["CONNECTING","OPEN","CLOSING","CLOSED"],Si=/^[!#$%&'*+\-.0-9A-Z^_`|a-z~]+$/,g=class t extends ti{constructor(e,s,r){super(),this._binaryType=Hs[0],this._closeCode=1006,this._closeFrameReceived=!1,this._closeFrameSent=!1,this._closeMessage=qe,this._closeTimer=null,this._errorEmitted=!1,this._extensions={},this._paused=!1,this._protocol="",this._readyState=t.CONNECTING,this._receiver=null,this._sender=null,this._socket=null,e!==null?(this._bufferedAmount=0,this._isServer=!1,this._redirects=0,s===void 0?s=[]:Array.isArray(s)||(typeof s=="object"&&s!==null?(r=s,s=[]):s=[s]),js(this,e,s,r)):(this._autoPong=r.autoPong,this._closeTimeout=r.closeTimeout,this._isServer=!0)}get binaryType(){return this._binaryType}set binaryType(e){Hs.includes(e)&&(this._binaryType=e,this._receiver&&(this._receiver._binaryType=e))}get bufferedAmount(){return this._socket?this._socket._writableState.length+this._sender._bufferedBytes:this._bufferedAmount}get extensions(){return Object.keys(this._extensions).join()}get isPaused(){return this._paused}get onclose(){return null}get onerror(){return null}get onopen(){return null}get onmessage(){return null}get protocol(){return this._protocol}get readyState(){return this._readyState}get url(){return this._url}setSocket(e,s,r){let n=new ai({allowSynchronousEvents:r.allowSynchronousEvents,binaryType:this.binaryType,extensions:this._extensions,isServer:this._isServer,maxBufferedChunks:r.maxBufferedChunks,maxFragments:r.maxFragments,maxPayload:r.maxPayload,skipUTF8Validation:r.skipUTF8Validation}),i=new li(e,this._extensions,r.generateMask);this._receiver=n,this._sender=i,this._socket=e,n[E]=this,i[E]=this,e[E]=this,n.on("conclude",bi),n.on("drain",Ti),n.on("error",wi),n.on("message",Ai),n.on("ping",vi),n.on("pong",Ii),i.onerror=Oi,e.setTimeout&&e.setTimeout(0),e.setNoDelay&&e.setNoDelay(),s.length>0&&e.unshift(s),e.on("close",zs),e.on("data",Xe),e.on("end",Xs),e.on("error",Ks),this._readyState=t.OPEN,this.emit("open")}emitClose(){if(!this._socket){this._readyState=t.CLOSED,this.emit("close",this._closeCode,this._closeMessage);return}this._extensions[V.extensionName]&&this._extensions[V.extensionName].cleanup(),this._receiver.removeAllListeners(),this._readyState=t.CLOSED,this.emit("close",this._closeCode,this._closeMessage)}close(e,s){if(this.readyState!==t.CLOSED){if(this.readyState===t.CONNECTING){O(this,this._req,"WebSocket was closed before the connection was established");return}if(this.readyState===t.CLOSING){this._closeFrameSent&&(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end();return}this._readyState=t.CLOSING,this._sender.close(e,s,!this._isServer,r=>{r||(this._closeFrameSent=!0,(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end())}),qs(this)}}pause(){this.readyState===t.CONNECTING||this.readyState===t.CLOSED||(this._paused=!0,this._socket.pause())}ping(e,s,r){if(this.readyState===t.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof e=="function"?(r=e,e=s=void 0):typeof s=="function"&&(r=s,s=void 0),typeof e=="number"&&(e=e.toString()),this.readyState!==t.OPEN){Lt(this,e,r);return}s===void 0&&(s=!this._isServer),this._sender.ping(e||qe,s,r)}pong(e,s,r){if(this.readyState===t.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof e=="function"?(r=e,e=s=void 0):typeof s=="function"&&(r=s,s=void 0),typeof e=="number"&&(e=e.toString()),this.readyState!==t.OPEN){Lt(this,e,r);return}s===void 0&&(s=!this._isServer),this._sender.pong(e||qe,s,r)}resume(){this.readyState===t.CONNECTING||this.readyState===t.CLOSED||(this._paused=!1,this._receiver._writableState.needDrain||this._socket.resume())}send(e,s,r){if(this.readyState===t.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if(typeof s=="function"&&(r=s,s={}),typeof e=="number"&&(e=e.toString()),this.readyState!==t.OPEN){Lt(this,e,r);return}let n={binary:typeof e!="string",mask:!this._isServer,compress:!0,fin:!0,...s};this._extensions[V.extensionName]||(n.compress=!1),this._sender.send(e||qe,n,r)}terminate(){if(this.readyState!==t.CLOSED){if(this.readyState===t.CONNECTING){O(this,this._req,"WebSocket was closed before the connection was established");return}this._socket&&(this._readyState=t.CLOSING,this._socket.destroy())}}};Object.defineProperty(g,"CONNECTING",{enumerable:!0,value:$.indexOf("CONNECTING")});Object.defineProperty(g.prototype,"CONNECTING",{enumerable:!0,value:$.indexOf("CONNECTING")});Object.defineProperty(g,"OPEN",{enumerable:!0,value:$.indexOf("OPEN")});Object.defineProperty(g.prototype,"OPEN",{enumerable:!0,value:$.indexOf("OPEN")});Object.defineProperty(g,"CLOSING",{enumerable:!0,value:$.indexOf("CLOSING")});Object.defineProperty(g.prototype,"CLOSING",{enumerable:!0,value:$.indexOf("CLOSING")});Object.defineProperty(g,"CLOSED",{enumerable:!0,value:$.indexOf("CLOSED")});Object.defineProperty(g.prototype,"CLOSED",{enumerable:!0,value:$.indexOf("CLOSED")});["binaryType","bufferedAmount","extensions","isPaused","protocol","readyState","url"].forEach(t=>{Object.defineProperty(g.prototype,t,{enumerable:!0})});["open","error","close","message"].forEach(t=>{Object.defineProperty(g.prototype,`on${t}`,{enumerable:!0,get(){for(let e of this.listeners(t))if(e[Rt])return e[di];return null},set(e){for(let s of this.listeners(t))if(s[Rt]){this.removeListener(t,s);break}typeof e=="function"&&this.addEventListener(t,e,{[Rt]:!0})}})});g.prototype.addEventListener=mi;g.prototype.removeEventListener=pi;Js.exports=g;function js(t,e,s,r){let n={allowSynchronousEvents:!0,autoPong:!0,closeTimeout:fi,protocolVersion:kt[1],maxBufferedChunks:262144,maxFragments:16384,maxPayload:104857600,skipUTF8Validation:!1,perMessageDeflate:!0,followRedirects:!1,maxRedirects:10,...r,socketPath:void 0,hostname:void 0,protocol:void 0,timeout:void 0,method:"GET",host:void 0,path:void 0,port:void 0};if(t._autoPong=n.autoPong,t._closeTimeout=n.closeTimeout,!kt.includes(n.protocolVersion))throw new RangeError(`Unsupported protocol version: ${n.protocolVersion} (supported versions: ${kt.join(", ")})`);let i;if(e instanceof Pt)i=e;else try{i=new Pt(e)}catch{throw new SyntaxError(`Invalid URL: ${e}`)}i.protocol==="http:"?i.protocol="ws:":i.protocol==="https:"&&(i.protocol="wss:"),t._url=i.href;let o=i.protocol==="wss:",a=i.protocol==="ws+unix:",l;if(i.protocol!=="ws:"&&!o&&!a?l=`The URL's protocol must be one of "ws:", "wss:", "http:", "https:", or "ws+unix:"`:a&&!i.pathname?l="The URL's pathname is empty":i.hash&&(l="The URL contains a fragment identifier"),l){let _=new SyntaxError(l);if(t._redirects===0)throw _;ze(t,_);return}let c=o?443:80,f=ii(16).toString("base64"),d=o?si.request:ri.request,u=new Set,h;if(n.createConnection=n.createConnection||(o?Ei:xi),n.defaultPort=n.defaultPort||c,n.port=i.port||c,n.host=i.hostname.startsWith("[")?i.hostname.slice(1,-1):i.hostname,n.headers={...n.headers,"Sec-WebSocket-Version":n.protocolVersion,"Sec-WebSocket-Key":f,Connection:"Upgrade",Upgrade:"websocket"},n.path=i.pathname+i.search,n.timeout=n.handshakeTimeout,n.perMessageDeflate&&(h=new V({...n.perMessageDeflate,isServer:!1,maxPayload:n.maxPayload}),n.headers["Sec-WebSocket-Extensions"]=_i({[V.extensionName]:h.offer()})),s.length){for(let _ of s){if(typeof _!="string"||!Si.test(_)||u.has(_))throw new SyntaxError("An invalid or duplicated subprotocol was specified");u.add(_)}n.headers["Sec-WebSocket-Protocol"]=s.join(",")}if(n.origin&&(n.protocolVersion<13?n.headers["Sec-WebSocket-Origin"]=n.origin:n.headers.Origin=n.origin),(i.username||i.password)&&(n.auth=`${i.username}:${i.password}`),a){let _=n.path.split(":");n.socketPath=_[0],n.path=_[1]}let p;if(n.followRedirects){if(t._redirects===0){t._originalIpc=a,t._originalSecure=o,t._originalHostOrSocketPath=a?n.socketPath:i.host;let _=r&&r.headers;if(r={...r,headers:{}},_)for(let[b,ie]of Object.entries(_))r.headers[b.toLowerCase()]=ie}else if(t.listenerCount("redirect")===0){let _=a?t._originalIpc?n.socketPath===t._originalHostOrSocketPath:!1:t._originalIpc?!1:i.host===t._originalHostOrSocketPath;(!_||t._originalSecure&&!o)&&(delete n.headers.authorization,delete n.headers.cookie,_||delete n.headers.host,n.auth=void 0)}n.auth&&!r.headers.authorization&&(r.headers.authorization="Basic "+Buffer.from(n.auth).toString("base64")),p=t._req=d(n),t._redirects&&t.emit("redirect",t.url,p)}else p=t._req=d(n);n.timeout&&p.on("timeout",()=>{O(t,p,"Opening handshake has timed out")}),p.on("error",_=>{p===null||p[Gs]||(p=t._req=null,ze(t,_))}),p.on("response",_=>{let b=_.headers.location,ie=_.statusCode;if(b&&n.followRedirects&&ie>=300&&ie<400){if(++t._redirects>n.maxRedirects){O(t,p,"Maximum redirects exceeded");return}p.abort();let be;try{be=new Pt(b,e)}catch{let oe=new SyntaxError(`Invalid URL: ${b}`);ze(t,oe);return}js(t,be,s,r)}else t.emit("unexpected-response",p,_)||O(t,p,`Unexpected server response: ${_.statusCode}`)}),p.on("upgrade",(_,b,ie)=>{if(t.emit("upgrade",_),t.readyState!==g.CONNECTING)return;p=t._req=null;let be=_.headers.upgrade;if(be===void 0||be.toLowerCase()!=="websocket"){O(t,b,"Invalid Upgrade header");return}let ss=oi("sha1").update(f+ui).digest("base64");if(_.headers["sec-websocket-accept"]!==ss){O(t,b,"Invalid Sec-WebSocket-Accept header");return}let oe=_.headers["sec-websocket-protocol"],Te;if(oe!==void 0?u.size?u.has(oe)||(Te="Server sent an invalid subprotocol"):Te="Server sent a subprotocol but none was requested":u.size&&(Te="Server sent no subprotocol"),Te){O(t,b,Te);return}oe&&(t._protocol=oe);let rs=_.headers["sec-websocket-extensions"];if(rs!==void 0){if(!h){O(t,b,"Server sent a Sec-WebSocket-Extensions header but no extension was requested");return}let ft;try{ft=yi(rs)}catch{O(t,b,"Invalid Sec-WebSocket-Extensions header");return}let ns=Object.keys(ft);if(ns.length!==1||ns[0]!==V.extensionName){O(t,b,"Server indicated an extension that was not requested");return}try{h.accept(ft[V.extensionName])}catch{O(t,b,"Invalid Sec-WebSocket-Extensions header");return}t._extensions[V.extensionName]=h}t.setSocket(b,ie,{allowSynchronousEvents:n.allowSynchronousEvents,generateMask:n.generateMask,maxBufferedChunks:n.maxBufferedChunks,maxFragments:n.maxFragments,maxPayload:n.maxPayload,skipUTF8Validation:n.skipUTF8Validation})}),n.finishRequest?n.finishRequest(p,t):p.end()}function ze(t,e){t._readyState=g.CLOSING,t._errorEmitted=!0,t.emit("error",e),t.emitClose()}function xi(t){return t.path=t.socketPath,$s.connect(t)}function Ei(t){return t.path=void 0,!t.servername&&t.servername!==""&&(t.servername=$s.isIP(t.host)?"":t.host),ni.connect(t)}function O(t,e,s){t._readyState=g.CLOSING;let r=new Error(s);Error.captureStackTrace(r,O),e.setHeader?(e[Gs]=!0,e.abort(),e.socket&&!e.socket.destroyed&&e.socket.destroy(),process.nextTick(ze,t,r)):(e.destroy(r),e.once("error",t.emit.bind(t,"error")),e.once("close",t.emitClose.bind(t)))}function Lt(t,e,s){if(e){let r=ci(e)?e.size:gi(e).length;t._socket?t._sender._bufferedBytes+=r:t._bufferedAmount+=r}if(s){let r=new Error(`WebSocket is not open: readyState ${t.readyState} (${$[t.readyState]})`);process.nextTick(s,r)}}function bi(t,e){let s=this[E];s._closeFrameReceived=!0,s._closeMessage=e,s._closeCode=t,s._socket[E]!==void 0&&(s._socket.removeListener("data",Xe),process.nextTick(Vs,s._socket),t===1005?s.close():s.close(t,e))}function Ti(){let t=this[E];t.isPaused||t._socket.resume()}function wi(t){let e=this[E];e._socket[E]!==void 0&&(e._socket.removeListener("data",Xe),process.nextTick(Vs,e._socket),e.close(t[hi])),e._errorEmitted||(e._errorEmitted=!0,e.emit("error",t))}function Ys(){this[E].emitClose()}function Ai(t,e){this[E].emit("message",t,e)}function vi(t){let e=this[E];e._autoPong&&e.pong(t,!this._isServer,Ws),e.emit("ping",t)}function Ii(t){this[E].emit("pong",t)}function Vs(t){t.resume()}function Oi(t){let e=this[E];e.readyState!==g.CLOSED&&(e.readyState===g.OPEN&&(e._readyState=g.CLOSING,qs(e)),this._socket.end(),e._errorEmitted||(e._errorEmitted=!0,e.emit("error",t)))}function qs(t){t._closeTimer=setTimeout(t._socket.destroy.bind(t._socket),t._closeTimeout)}function zs(){let t=this[E];if(this.removeListener("close",zs),this.removeListener("data",Xe),this.removeListener("end",Xs),t._readyState=g.CLOSING,!this._readableState.endEmitted&&!t._closeFrameReceived&&!t._receiver._writableState.errorEmitted&&this._readableState.length!==0){let e=this.read(this._readableState.length);t._receiver.write(e)}t._receiver.end(),this[E]=void 0,clearTimeout(t._closeTimer),t._receiver._writableState.finished||t._receiver._writableState.errorEmitted?t.emitClose():(t._receiver.on("error",Ys),t._receiver.on("finish",Ys))}function Xe(t){this[E]._receiver.write(t)||this.pause()}function Xs(){let t=this[E];t._readyState=g.CLOSING,t._receiver.end(),this.end()}function Ks(){let t=this[E];this.removeListener("error",Ks),this.on("error",Ws),t&&(t._readyState=g.CLOSING,this.destroy())}});var tr=v((_a,er)=>{"use strict";var pa=Ke(),{Duplex:Ci}=S("stream");function Zs(t){t.emit("close")}function Pi(){!this.destroyed&&this._writableState.finished&&this.destroy()}function Qs(t){this.removeListener("error",Qs),this.destroy(),this.listenerCount("error")===0&&this.emit("error",t)}function Ri(t,e){let s=!0,r=new Ci({...e,autoDestroy:!1,emitClose:!1,objectMode:!1,writableObjectMode:!1});return t.on("message",function(i,o){let a=!o&&r._readableState.objectMode?i.toString():i;r.push(a)||t.pause()}),t.once("error",function(i){r.destroyed||(s=!1,r.destroy(i))}),t.once("close",function(){r.destroyed||r.push(null)}),r._destroy=function(n,i){if(t.readyState===t.CLOSED){i(n),process.nextTick(Zs,r);return}let o=!1;t.once("error",function(l){o=!0,i(l)}),t.once("close",function(){o||i(n),process.nextTick(Zs,r)}),s&&t.terminate()},r._final=function(n){if(t.readyState===t.CONNECTING){t.once("open",function(){r._final(n)});return}t._socket!==null&&(t._socket._writableState.finished?(n(),r._readableState.endEmitted&&r.destroy()):(t._socket.once("finish",function(){n()}),t.close()))},r._read=function(){t.isPaused&&t.resume()},r._write=function(n,i,o){if(t.readyState===t.CONNECTING){t.once("open",function(){r._write(n,i,o)});return}t.send(n,o)},r.on("end",Pi),r.on("error",Qs),r}er.exports=Ri});var Nt=v((ya,sr)=>{"use strict";var{tokenChars:ki}=ue();function Li(t){let e=new Set,s=-1,r=-1,n=0;for(n;n<t.length;n++){let o=t.charCodeAt(n);if(r===-1&&ki[o]===1)s===-1&&(s=n);else if(n!==0&&(o===32||o===9))r===-1&&s!==-1&&(r=n);else if(o===44){if(s===-1)throw new SyntaxError(`Unexpected character at index ${n}`);r===-1&&(r=n);let a=t.slice(s,r);if(e.has(a))throw new SyntaxError(`The "${a}" subprotocol is duplicated`);e.add(a),s=r=-1}else throw new SyntaxError(`Unexpected character at index ${n}`)}if(s===-1||r!==-1)throw new SyntaxError("Unexpected end of input");let i=t.slice(s,n);if(e.has(i))throw new SyntaxError(`The "${i}" subprotocol is duplicated`);return e.add(i),e}sr.exports={parse:Li}});var cr=v((Sa,lr)=>{"use strict";var Ni=S("events"),Je=S("http"),{Duplex:ga}=S("stream"),{createHash:Mi}=S("crypto"),rr=Ve(),Q=fe(),Di=Nt(),Bi=Ke(),{CLOSE_TIMEOUT:Ui,GUID:Fi,kWebSocket:Hi}=F(),Yi=/^[+/0-9A-Za-z]{22}==$/,nr=0,ir=1,ar=2,Mt=class extends Ni{constructor(e,s){if(super(),e={allowSynchronousEvents:!0,autoPong:!0,maxBufferedChunks:256*1024,maxFragments:16*1024,maxPayload:100*1024*1024,skipUTF8Validation:!1,perMessageDeflate:!1,handleProtocols:null,clientTracking:!0,closeTimeout:Ui,verifyClient:null,noServer:!1,backlog:null,server:null,host:null,path:null,port:null,WebSocket:Bi,...e},e.port==null&&!e.server&&!e.noServer||e.port!=null&&(e.server||e.noServer)||e.server&&e.noServer)throw new TypeError('One and only one of the "port", "server", or "noServer" options must be specified');if(e.port!=null?(this._server=Je.createServer((r,n)=>{let i=Je.STATUS_CODES[426];n.writeHead(426,{"Content-Length":i.length,"Content-Type":"text/plain"}),n.end(i)}),this._server.listen(e.port,e.host,e.backlog,s)):e.server&&(this._server=e.server),this._server){let r=this.emit.bind(this,"connection");this._removeListeners=$i(this._server,{listening:this.emit.bind(this,"listening"),error:this.emit.bind(this,"error"),upgrade:(n,i,o)=>{this.handleUpgrade(n,i,o,r)}})}e.perMessageDeflate===!0&&(e.perMessageDeflate={}),e.clientTracking&&(this.clients=new Set,this._shouldEmitClose=!1),this.options=e,this._state=nr}address(){if(this.options.noServer)throw new Error('The server is operating in "noServer" mode');return this._server?this._server.address():null}close(e){if(this._state===ar){e&&this.once("close",()=>{e(new Error("The server is not running"))}),process.nextTick(Pe,this);return}if(e&&this.once("close",e),this._state!==ir)if(this._state=ir,this.options.noServer||this.options.server)this._server&&(this._removeListeners(),this._removeListeners=this._server=null),this.clients?this.clients.size?this._shouldEmitClose=!0:process.nextTick(Pe,this):process.nextTick(Pe,this);else{let s=this._server;this._removeListeners(),this._removeListeners=this._server=null,s.close(()=>{Pe(this)})}}shouldHandle(e){if(this.options.path){let s=e.url.indexOf("?");if((s!==-1?e.url.slice(0,s):e.url)!==this.options.path)return!1}return!0}handleUpgrade(e,s,r,n){s.on("error",or);let i=e.headers["sec-websocket-key"],o=e.headers.upgrade,a=+e.headers["sec-websocket-version"];if(e.method!=="GET"){ee(this,e,s,405,"Invalid HTTP method");return}if(o===void 0||o.toLowerCase()!=="websocket"){ee(this,e,s,400,"Invalid Upgrade header");return}if(i===void 0||!Yi.test(i)){ee(this,e,s,400,"Missing or invalid Sec-WebSocket-Key header");return}if(a!==13&&a!==8){ee(this,e,s,400,"Missing or invalid Sec-WebSocket-Version header",{"Sec-WebSocket-Version":"13, 8"});return}if(!this.shouldHandle(e)){Re(s,400);return}let l=e.headers["sec-websocket-protocol"],c=new Set;if(l!==void 0)try{c=Di.parse(l)}catch{ee(this,e,s,400,"Invalid Sec-WebSocket-Protocol header");return}let f=e.headers["sec-websocket-extensions"],d={};if(this.options.perMessageDeflate&&f!==void 0){let u=new Q({...this.options.perMessageDeflate,isServer:!0,maxPayload:this.options.maxPayload});try{let h=rr.parse(f);h[Q.extensionName]&&(u.accept(h[Q.extensionName]),d[Q.extensionName]=u)}catch{ee(this,e,s,400,"Invalid or unacceptable Sec-WebSocket-Extensions header");return}}if(this.options.verifyClient){let u={origin:e.headers[`${a===8?"sec-websocket-origin":"origin"}`],secure:!!(e.socket.authorized||e.socket.encrypted),req:e};if(this.options.verifyClient.length===2){this.options.verifyClient(u,(h,p,_,b)=>{if(!h)return Re(s,p||401,_,b);this.completeUpgrade(d,i,c,e,s,r,n)});return}if(!this.options.verifyClient(u))return Re(s,401)}this.completeUpgrade(d,i,c,e,s,r,n)}completeUpgrade(e,s,r,n,i,o,a){if(!i.readable||!i.writable)return i.destroy();if(i[Hi])throw new Error("server.handleUpgrade() was called more than once with the same socket, possibly due to a misconfiguration");if(this._state>nr)return Re(i,503);let c=["HTTP/1.1 101 Switching Protocols","Upgrade: websocket","Connection: Upgrade",`Sec-WebSocket-Accept: ${Mi("sha1").update(s+Fi).digest("base64")}`],f=new this.options.WebSocket(null,void 0,this.options);if(r.size){let d=this.options.handleProtocols?this.options.handleProtocols(r,n):r.values().next().value;d&&(c.push(`Sec-WebSocket-Protocol: ${d}`),f._protocol=d)}if(e[Q.extensionName]){let d=e[Q.extensionName].params,u=rr.format({[Q.extensionName]:[d]});c.push(`Sec-WebSocket-Extensions: ${u}`),f._extensions=e}this.emit("headers",c,n),i.write(c.concat(`\r
5
+ `).join(`\r
6
+ `)),i.removeListener("error",or),f.setSocket(i,o,{allowSynchronousEvents:this.options.allowSynchronousEvents,maxBufferedChunks:this.options.maxBufferedChunks,maxFragments:this.options.maxFragments,maxPayload:this.options.maxPayload,skipUTF8Validation:this.options.skipUTF8Validation}),this.clients&&(this.clients.add(f),f.on("close",()=>{this.clients.delete(f),this._shouldEmitClose&&!this.clients.size&&process.nextTick(Pe,this)})),a(f,n)}};lr.exports=Mt;function $i(t,e){for(let s of Object.keys(e))t.on(s,e[s]);return function(){for(let r of Object.keys(e))t.removeListener(r,e[r])}}function Pe(t){t._state=ar,t.emit("close")}function or(){this.destroy()}function Re(t,e,s,r){s=s||Je.STATUS_CODES[e],r={Connection:"close","Content-Type":"text/html","Content-Length":Buffer.byteLength(s),...r},t.once("finish",t.destroy),t.end(`HTTP/1.1 ${e} ${Je.STATUS_CODES[e]}\r
7
+ `+Object.keys(r).map(n=>`${n}: ${r[n]}`).join(`\r
8
+ `)+`\r
9
+ \r
10
+ `+s)}function ee(t,e,s,r,n,i){if(t.listenerCount("wsClientError")){let o=new Error(n);Error.captureStackTrace(o,ee),t.emit("wsClientError",o,s,e)}else Re(s,r,n,i)}});import qo from"node:fs";import zo from"node:path";import hn from"node:fs";import mn from"node:os";import we from"node:path";var ae=we.resolve(import.meta.dirname,"..");function ht(){if(process.env.RELAY_STATE_DIR)return process.env.RELAY_STATE_DIR;let t=process.env.XDG_STATE_HOME||we.join(mn.homedir(),".local","state");return we.join(t,"herdr-remote-relay")}var x={relay:{mode:"remote",host:"127.0.0.1",port:8787,publicUrl:"http://127.0.0.1:8787",maxPayloadBytes:5*1024*1024,maxClientsPerHost:16,maxHosts:1024,maxPendingHandshakes:1024,maxBufferedBytesPerClient:4*1024*1024,allowedOrigins:[],trustProxy:!1,hostReconnectGraceMs:30*1e3},auth:{pairingTtlMs:600*1e3,deviceTtlMs:720*60*60*1e3,maxDevices:32,password:null,adminToken:null,stateFile:null},cleanup:{intervalMs:60*1e3,heartbeatIntervalMs:30*1e3,staleAfterMs:90*1e3}};function pn(t){return JSON.parse(JSON.stringify(t))}function _n(t){try{return JSON.parse(hn.readFileSync(t,"utf8"))}catch(e){let{code:s,message:r}=e;if(s==="ENOENT")return null;throw new Error(`cannot read relay config ${t}: ${r}`)}}function mt(t,e){if(t==null||t==="")return e;if(t===!0||t===!1)return t;let s=String(t).trim().toLowerCase();return["true","1","yes","on"].includes(s)?!0:["false","0","no","off"].includes(s)?!1:e}function I(t,e,s,r){let n=Number(t);return!Number.isInteger(n)||n<s||n>r?e:n}function os(t){return Array.isArray(t)?t.map(e=>String(e).trim()).filter(Boolean):typeof t!="string"?null:t.split(",").map(e=>e.trim()).filter(Boolean)}function dt(t,e){if(!e||typeof e!="object")return;let s=e;for(let r of Object.keys(t))s[r]!==void 0&&(t[r]=s[r])}function yn(t=[]){let e={},s=[],r=!1,n=!1;for(let i=0;i<t.length;i+=1){let o=t[i];if(o==="--help"||o==="-h"){r=!0;continue}if(o==="--version"||o==="-v"){n=!0;continue}if(!o.startsWith("--")){s.push(`unexpected argument: ${o}`);continue}let a=o.indexOf("="),l=a===-1?o.slice(2):o.slice(2,a),c=a===-1?void 0:o.slice(a+1);if(c===void 0){let f=t[i+1];f===void 0||f.startsWith("--")?c="true":(c=f,i+=1)}e[l]=c}return{options:e,help:r,version:n,errors:s}}function gn(t,e){if(!e||typeof e!="object")return;let s=e;dt(t.relay,s.relay),dt(t.auth,s.auth),dt(t.cleanup,s.cleanup)}function Sn(t,e){if((e.RELAY_DEPLOYMENT_MODE==="local"||e.RELAY_DEPLOYMENT_MODE==="remote")&&(t.relay.mode=e.RELAY_DEPLOYMENT_MODE),e.RELAY_BIND&&(t.relay.host=e.RELAY_BIND),e.RELAY_PORT&&(t.relay.port=e.RELAY_PORT),e.RELAY_PUBLIC_URL&&(t.relay.publicUrl=e.RELAY_PUBLIC_URL),e.RELAY_MAX_PAYLOAD_BYTES&&(t.relay.maxPayloadBytes=e.RELAY_MAX_PAYLOAD_BYTES),e.RELAY_MAX_CLIENTS_PER_HOST&&(t.relay.maxClientsPerHost=e.RELAY_MAX_CLIENTS_PER_HOST),e.RELAY_MAX_HOSTS&&(t.relay.maxHosts=e.RELAY_MAX_HOSTS),e.RELAY_MAX_PENDING_HANDSHAKES&&(t.relay.maxPendingHandshakes=e.RELAY_MAX_PENDING_HANDSHAKES),e.RELAY_MAX_BUFFERED_BYTES_PER_CLIENT&&(t.relay.maxBufferedBytesPerClient=e.RELAY_MAX_BUFFERED_BYTES_PER_CLIENT),e.RELAY_HOST_RECONNECT_GRACE_MS&&(t.relay.hostReconnectGraceMs=e.RELAY_HOST_RECONNECT_GRACE_MS),e.RELAY_ALLOWED_ORIGINS!==void 0){let s=os(e.RELAY_ALLOWED_ORIGINS);s&&(t.relay.allowedOrigins=s)}e.RELAY_TRUST_PROXY!==void 0&&(t.relay.trustProxy=mt(e.RELAY_TRUST_PROXY,t.relay.trustProxy)),e.RELAY_PASSWORD&&(t.auth.password=e.RELAY_PASSWORD),e.RELAY_ADMIN_TOKEN&&(t.auth.adminToken=e.RELAY_ADMIN_TOKEN),e.RELAY_AUTH_STATE_FILE&&(t.auth.stateFile=e.RELAY_AUTH_STATE_FILE),e.RELAY_PAIRING_TTL_MS&&(t.auth.pairingTtlMs=e.RELAY_PAIRING_TTL_MS),e.RELAY_DEVICE_TTL_MS&&(t.auth.deviceTtlMs=e.RELAY_DEVICE_TTL_MS),e.RELAY_MAX_DEVICES&&(t.auth.maxDevices=e.RELAY_MAX_DEVICES)}function xn(t,e){if((e["deployment-mode"]==="local"||e["deployment-mode"]==="remote")&&(t.relay.mode=e["deployment-mode"]),e.bind&&(t.relay.host=e.bind),e.port&&(t.relay.port=e.port),e["public-url"]&&(t.relay.publicUrl=e["public-url"]),e["allowed-origins"]!==void 0){let s=os(e["allowed-origins"]);s&&(t.relay.allowedOrigins=s)}e["trust-proxy"]!==void 0&&(t.relay.trustProxy=mt(e["trust-proxy"],t.relay.trustProxy)),e.password&&(t.auth.password=e.password),e["admin-token"]&&(t.auth.adminToken=e["admin-token"]),e["state-file"]&&(t.auth.stateFile=e["state-file"]),e["max-clients"]&&(t.relay.maxClientsPerHost=e["max-clients"]),e["max-hosts"]&&(t.relay.maxHosts=e["max-hosts"]),e["max-pending-handshakes"]&&(t.relay.maxPendingHandshakes=e["max-pending-handshakes"]),e["max-buffered-bytes"]&&(t.relay.maxBufferedBytesPerClient=e["max-buffered-bytes"]),e["host-reconnect-grace-ms"]&&(t.relay.hostReconnectGraceMs=e["host-reconnect-grace-ms"])}function En(t){return t.relay.mode!=="local"&&t.relay.mode!=="remote"&&(t.relay.mode=x.relay.mode),t.relay.port=I(t.relay.port,x.relay.port,0,65535),t.relay.maxPayloadBytes=I(t.relay.maxPayloadBytes,x.relay.maxPayloadBytes,4096,16*1024*1024),t.relay.maxClientsPerHost=I(t.relay.maxClientsPerHost,x.relay.maxClientsPerHost,1,256),t.relay.maxHosts=I(t.relay.maxHosts,x.relay.maxHosts,1,1e5),t.relay.maxPendingHandshakes=I(t.relay.maxPendingHandshakes,x.relay.maxPendingHandshakes,16,1e5),t.relay.maxBufferedBytesPerClient=I(t.relay.maxBufferedBytesPerClient,x.relay.maxBufferedBytesPerClient,64*1024,256*1024*1024),t.relay.hostReconnectGraceMs=I(t.relay.hostReconnectGraceMs,x.relay.hostReconnectGraceMs,1e3,1440*60*1e3),t.auth.pairingTtlMs=I(t.auth.pairingTtlMs,x.auth.pairingTtlMs,30*1e3,1440*60*1e3),t.auth.deviceTtlMs=I(t.auth.deviceTtlMs,x.auth.deviceTtlMs,60*1e3,365*24*60*60*1e3),t.auth.maxDevices=I(t.auth.maxDevices,x.auth.maxDevices,1,1e4),t.cleanup.intervalMs=I(t.cleanup.intervalMs,x.cleanup.intervalMs,1e3,1440*60*1e3),t.cleanup.heartbeatIntervalMs=I(t.cleanup.heartbeatIntervalMs,x.cleanup.heartbeatIntervalMs,1e3,600*1e3),t.cleanup.staleAfterMs=I(t.cleanup.staleAfterMs,x.cleanup.staleAfterMs,t.cleanup.heartbeatIntervalMs*2,1440*60*1e3),(typeof t.relay.host!="string"||t.relay.host.length===0)&&(t.relay.host=x.relay.host),(typeof t.relay.publicUrl!="string"||t.relay.publicUrl.length===0)&&(t.relay.publicUrl=`http://${t.relay.host}:${t.relay.port}`),t.relay.publicUrl=String(t.relay.publicUrl).replace(/\/+$/,""),Array.isArray(t.relay.allowedOrigins)||(t.relay.allowedOrigins=[]),t.relay.trustProxy=mt(t.relay.trustProxy,!1),t.auth.stateFile||(t.auth.stateFile=we.join(ht(),"relay-auth.json")),t}function bn(t){let e=[],s=["127.0.0.1","localhost","::1"].includes(t.relay.host),r;try{r=new URL(t.relay.publicUrl)}catch{e.push(`publicUrl is not a valid URL: ${t.relay.publicUrl}`)}return t.auth.password||e.push("no password set (RELAY_PASSWORD): this is a public relay, anyone may connect a workstation to it"),t.relay.mode==="remote"&&!t.auth.adminToken&&e.push("no admin token set (RELAY_ADMIN_TOKEN): the relay operator dashboard is unavailable"),!s&&r&&r.protocol==="http:"&&!["127.0.0.1","localhost"].includes(r.hostname)&&e.push(`publicUrl uses plain http on a non-loopback address (${t.relay.publicUrl}); terminate TLS in front of the relay`),e}function Be({argv:t=[],env:e=process.env}={}){let{options:s,help:r,version:n,errors:i}=yn(t),o=pn(x),a=s.config||e.HERDR_RELAY_CONFIG||null;if(a){let c=_n(we.resolve(a));if(c===null)throw new Error(`relay config not found: ${a}`);gn(o,c)}Sn(o,e),xn(o,s);let l=En(o);return{config:l,help:r,version:n,errors:i,configFile:a,warnings:bn(l)}}import jo from"node:http";import ts from"node:path";import{URL as Vo}from"node:url";var Wi=G(tr(),1),Gi=G(Ve(),1),ji=G(fe(),1),Vi=G(At(),1),qi=G(Ot(),1),zi=G(Nt(),1),T=G(Ke(),1),Dt=G(cr(),1);import Ft from"node:crypto";import Ki from"node:path";import fr from"node:crypto";import te from"node:fs";import Xi from"node:path";function ke(t){te.mkdirSync(t,{recursive:!0,mode:448});try{te.chmodSync(t,448)}catch{}}function ur(t,e){ke(Xi.dirname(t));let s=`${t}.${process.pid}.${fr.randomBytes(4).toString("hex")}.tmp`;te.writeFileSync(s,`${JSON.stringify(e,null,2)}
11
+ `,{mode:384});try{te.chmodSync(s,384)}catch{}te.renameSync(s,t);try{te.chmodSync(t,384)}catch{}}function dr(t,e){try{return JSON.parse(te.readFileSync(t,"utf8"))}catch(s){let{code:r,message:n}=s;return r!=="ENOENT"&&process.stderr.write(`herdr-remote: invalid state at ${t}: ${n}
12
+ `),e}}function Bt(t=32){return fr.randomBytes(t).toString("base64url")}function B(t){return Ft.createHash("sha256").update(String(t),"utf8").digest("hex")}function _e(t,e){if(typeof t!="string"||typeof e!="string")return!1;let s=Buffer.from(t,"hex"),r=Buffer.from(e,"hex");return s.length===r.length&&s.length>0&&Ft.timingSafeEqual(s,r)}function U(t=Date.now()){return new Date(t).toISOString()}function Ut(t){return typeof t=="string"&&/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(t)}var hr="ABCDEFGHJKMNPQRSTUVWXYZ23456789",Ji=6;function Zi(){let t="";for(let e=0;e<Ji;e+=1)t+=hr[Ft.randomInt(hr.length)];return t}var Ze=class{stateFile;pairingTtlMs;deviceTtlMs;maxDevices;password;pairings=new Map;lastDeviceSaveAt=new Map;state;constructor({stateFile:e,pairingTtlMs:s=600*1e3,deviceTtlMs:r=720*60*60*1e3,maxDevices:n=32,password:i=null}={}){if(!e)throw new TypeError("stateFile is required");this.stateFile=e,this.pairingTtlMs=s,this.deviceTtlMs=r,this.maxDevices=n,this.password=i||null,this.state=dr(e,{version:1,hosts:{},devices:{}}),this.state.version=1,this.state.hosts=this.state.hosts&&typeof this.state.hosts=="object"?this.state.hosts:{},this.state.devices=this.state.devices&&typeof this.state.devices=="object"?this.state.devices:{},ke(Ki.dirname(e))}save(){ur(this.stateFile,this.state)}checkPassword(e){return this.password?typeof e=="string"&&_e(B(e),B(this.password)):!0}registerHost(e,s,r=null,n=Date.now()){if(!Ut(e)||typeof s!="string"||s.length<16||s.length>4096)return{ok:!1,code:"invalid_host_credentials",message:"hostId and token are required"};if(!this.checkPassword(r))return{ok:!1,code:"relay_password_required",message:"the relay password is missing or wrong"};let i=this.state.hosts[e];return i?_e(i.tokenHash,B(s))?(i.lastSeenAt=U(n),this.save(),{ok:!0,hostId:e,firstSeen:!1}):{ok:!1,code:"host_auth_failed",message:"host token is invalid"}:(this.state.hosts[e]={tokenHash:B(s),createdAt:U(n),lastSeenAt:U(n)},this.save(),{ok:!0,hostId:e,firstSeen:!0})}authenticateHost(e,s){if(!Ut(e)||typeof s!="string"||s.length<16||s.length>4096)return!1;let r=this.state.hosts[e];return!!r&&_e(r.tokenHash,B(s))}hostCount(){return Object.keys(this.state.hosts).length}startPairing(e,s,r=Date.now()){if(!Ut(e)||!this.state.hosts[e])throw Object.assign(new Error("host is not connected or enrolled"),{code:"host_not_found"});this.cleanup(r);let n;do n=Zi();while([...this.pairings.values()].some(o=>_e(o.codeHash,B(n))));let i=r+this.pairingTtlMs;return this.pairings.set(n,{codeHash:B(n),hostId:e,expiresAt:i,publicUrl:s}),{code:n,hostId:e,publicUrl:s,expiresAt:i,expiresAtIso:U(i)}}completePairing(e,s=Date.now()){if(typeof e!="string"||e.length<4||e.length>32)return null;this.cleanup(s);let r=e.trim().toUpperCase(),n=this.pairings.get(r);if(!n||n.expiresAt<=s||!_e(n.codeHash,B(r)))return null;this.pairings.delete(r);let i=Object.values(this.state.devices);i.length>=this.maxDevices&&(i.sort((c,f)=>(c.lastSeenAt||"").localeCompare(f.lastSeenAt||"")),delete this.state.devices[i[0].deviceId]);let o=`device-${Bt(9)}`,a=Bt(32),l=s+this.deviceTtlMs;return this.state.devices[o]={deviceId:o,hostId:n.hostId,tokenHash:B(a),createdAt:U(s),lastSeenAt:U(s),expiresAt:l},this.save(),{deviceId:o,hostId:n.hostId,token:a,expiresAt:l,expiresAtIso:U(l),publicUrl:n.publicUrl}}noteDeviceSeen(e,{userAgent:s,ip:r}={},n=Date.now()){let i=this.state.devices[e];if(!i)return null;let o=!1;return s&&i.userAgent!==s&&(i.userAgent=String(s).slice(0,256),o=!0),r&&i.lastIp!==r&&(i.lastIp=String(r).slice(0,64),o=!0),i.lastSeenAt=U(n),o&&this.save(),{...i}}listDevices(e=Date.now()){return Object.values(this.state.devices).filter(s=>s&&s.expiresAt>e).map(s=>({deviceId:s.deviceId,hostId:s.hostId,createdAt:s.createdAt,lastSeenAt:s.lastSeenAt,expiresAt:s.expiresAt,expiresAtIso:U(s.expiresAt),userAgent:s.userAgent||null,lastIp:s.lastIp||null})).sort((s,r)=>String(r.lastSeenAt||"").localeCompare(String(s.lastSeenAt||"")))}revokeDevice(e){if(typeof e!="string"||!e)return null;let s=this.state.devices[e];return s?(delete this.state.devices[e],this.lastDeviceSaveAt.delete(e),this.save(),{deviceId:s.deviceId,hostId:s.hostId,userAgent:s.userAgent||null,lastIp:s.lastIp||null}):null}authenticateDevice(e,s=Date.now()){if(typeof e!="string"||e.length<16)return null;let r=B(e);for(let n of Object.values(this.state.devices)){if(n.expiresAt<=s||!_e(n.tokenHash,r))continue;n.lastSeenAt=U(s),n.expiresAt=s+this.deviceTtlMs;let i=this.lastDeviceSaveAt.get(n.deviceId)||0;return s-i>=60*1e3&&(this.lastDeviceSaveAt.set(n.deviceId,s),this.save()),{...n}}return null}cleanup(e=Date.now()){let s=0;for(let[n,i]of Object.entries(this.state.devices))(!i||i.expiresAt<=e)&&(delete this.state.devices[n],this.lastDeviceSaveAt.delete(n),s+=1);let r=0;for(let[n,i]of this.pairings.entries())i.expiresAt<=e&&(this.pairings.delete(n),r+=1);return s&&this.save(),{removedDevices:s,removedPairings:r}}deviceCount(){return Object.keys(this.state.devices).length}};import Qe from"node:os";import{monitorEventLoopDelay as Qi}from"node:perf_hooks";function et(t,e=0){return Number.isFinite(t)?t:e}function q(t,e,s){return!s||s<=0?0:Math.max(0,(t-e)*1e3/s)}function st(t=[]){let e=new Set;for(let s of t)s&&e.add(typeof s.deviceId=="string"&&s.deviceId?`device:${s.deviceId}`:`client:${s.id}`);return e.size}var tt=class{version;protocolVersion;startedAt;bytesIn;bytesOut;framesIn;framesOut;cleanup;lastSample;hostTraffic;cpuSampleAt;cpuSample;eventLoop;constructor({version:e="0.1.0",protocolVersion:s=1}={}){this.version=e,this.protocolVersion=s,this.startedAt=Date.now(),this.bytesIn=0,this.bytesOut=0,this.framesIn=0,this.framesOut=0,this.cleanup={staleClientsPurged:0,closedPtysCleaned:0,deadConnectionsClosed:0,idleHostsTerminated:0,slowClientsDropped:0,lastCleanupAt:null},this.lastSample={at:this.startedAt,bytesIn:0,bytesOut:0,framesIn:0,framesOut:0},this.hostTraffic=new Map,this.cpuSampleAt=process.hrtime.bigint(),this.cpuSample=process.cpuUsage(),this.eventLoop=Qi({resolution:20}),this.eventLoop.enable()}hostCounter(e){if(typeof e!="string"||e.length===0)return null;let s=this.hostTraffic.get(e);return s||(s={bytesIn:0,bytesOut:0,framesIn:0,framesOut:0,sampleAt:Date.now(),sampleBytesIn:0,sampleBytesOut:0,sampleFramesIn:0,sampleFramesOut:0},this.hostTraffic.set(e,s)),s}recordIn(e,s=null){let r=Math.max(0,Number(e)||0);this.bytesIn+=r,this.framesIn+=1;let n=this.hostCounter(s);n&&(n.bytesIn+=r,n.framesIn+=1)}recordOut(e,s=null){let r=Math.max(0,Number(e)||0);this.bytesOut+=r,this.framesOut+=1;let n=this.hostCounter(s);n&&(n.bytesOut+=r,n.framesOut+=1)}forgetHost(e){typeof e=="string"&&this.hostTraffic.delete(e)}hostThroughput(e,s=Date.now()){let r=this.hostCounter(e);if(!r)return{bytesIn:0,bytesOut:0,bytesInPerSec:0,bytesOutPerSec:0,framesIn:0,framesOut:0,framesInPerSec:0,framesOutPerSec:0};let n=s-r.sampleAt,i={bytesIn:r.bytesIn,bytesOut:r.bytesOut,bytesInPerSec:q(r.bytesIn,r.sampleBytesIn,n),bytesOutPerSec:q(r.bytesOut,r.sampleBytesOut,n),framesIn:r.framesIn,framesOut:r.framesOut,framesInPerSec:q(r.framesIn,r.sampleFramesIn,n),framesOutPerSec:q(r.framesOut,r.sampleFramesOut,n)};return r.sampleAt=s,r.sampleBytesIn=r.bytesIn,r.sampleBytesOut=r.bytesOut,r.sampleFramesIn=r.framesIn,r.sampleFramesOut=r.framesOut,i}recordCleanup(e,s=1){Object.hasOwn(this.cleanup,e)&&typeof this.cleanup[e]=="number"&&(this.cleanup[e]+=s),this.cleanup.lastCleanupAt=new Date().toISOString()}cpuPercent(){let e=process.hrtime.bigint(),s=Number(e-this.cpuSampleAt),r=process.cpuUsage(this.cpuSample);if(this.cpuSampleAt=e,this.cpuSample=process.cpuUsage(),s<=0)return 0;let n=(r.user+r.system)*1e5/s;return Math.max(0,Math.round(n*100)/100)}eventLoopDelay(){let e=s=>Math.round(et(s/1e6)*100)/100;return{p50Ms:e(this.eventLoop.percentile(50)),p90Ms:e(this.eventLoop.percentile(90)),p99Ms:e(this.eventLoop.percentile(99)),maxMs:e(this.eventLoop.max),meanMs:e(this.eventLoop.mean)}}snapshot({clients:e=[],hosts:s=[],ptys:r=[],sample:n=!0,scopeHostId:i=null,activeUserCount:o=null}={}){let a=Date.now(),l=a-this.lastSample.at,c={bytesIn:this.bytesIn,bytesOut:this.bytesOut,bytesInPerSec:q(this.bytesIn,this.lastSample.bytesIn,l),bytesOutPerSec:q(this.bytesOut,this.lastSample.bytesOut,l),framesIn:this.framesIn,framesOut:this.framesOut,framesInPerSec:q(this.framesIn,this.lastSample.framesIn,l),framesOutPerSec:q(this.framesOut,this.lastSample.framesOut,l)};n&&(this.lastSample={at:a,bytesIn:this.bytesIn,bytesOut:this.bytesOut,framesIn:this.framesIn,framesOut:this.framesOut});let f=process.memoryUsage(),d=Qe.loadavg(),u=e[0],h=i?this.hostThroughput(i,a):c;return{version:this.version,protocolVersion:this.protocolVersion,uptimeSeconds:Math.floor((a-this.startedAt)/1e3),startTime:new Date(this.startedAt).toISOString(),serverTime:new Date(a).toISOString(),activeControllerId:null,activeHostId:u?.hostId||s[0]?.id||null,clients:e,hosts:s,ptys:r,clientCount:e.length,hostCount:s.length,ptyCount:r.length,activeUserCount:Number.isFinite(o)?o:st(e),throughput:h,cpu:{load1m:et(d[0]),load5m:et(d[1]),load15m:et(d[2]),cpuPercent:this.cpuPercent(),cores:Qe.cpus().length},memory:{rssBytes:f.rss,heapUsedBytes:f.heapUsed,heapTotalBytes:f.heapTotal,externalBytes:f.external,systemTotalBytes:Qe.totalmem(),systemFreeBytes:Qe.freemem()},eventLoopDelay:this.eventLoopDelay(),cleanup:{...this.cleanup}}}close(){this.eventLoop.disable(),this.hostTraffic.clear()}};function Le(t){return Buffer.isBuffer(t)?t:(typeof t=="string"||t instanceof ArrayBuffer,Buffer.from(t))}function mr(t,e,s=Buffer.alloc(0)){if(typeof t!="string"||!t||typeof e!="string"||!e)throw new TypeError("type and streamId must be non-empty strings");let r=Le(s),n=Buffer.from(JSON.stringify({type:t,streamId:e}),"utf8");if(n.length>8192)throw new RangeError("stream frame header is too large");let i=Buffer.allocUnsafe(4);return i.writeUInt32BE(n.length,0),Buffer.concat([i,n,r])}function eo(t){let e=Le(t);if(e.length<4)throw new Error("stream frame is truncated");let s=e.readUInt32BE(0);if(s===0||s>8192||e.length<4+s)throw new Error("stream frame header is invalid");let r;try{r=JSON.parse(e.subarray(4,4+s).toString("utf8"))}catch(n){throw new Error(`stream frame header is not JSON: ${n.message}`)}if(!r||typeof r.type!="string"||typeof r.streamId!="string")throw new Error("stream frame header is missing routing fields");return{version:1,type:r.type,streamId:r.streamId,payload:e.subarray(4+s)}}function pr(t,e,s=Buffer.alloc(0)){let r=t;if(t==="output"?r=0:t==="input"&&(r=1),r!==0&&r!==1)throw new TypeError("type must be FRAME_TYPE_OUTPUT (0) or FRAME_TYPE_INPUT (1)");if(!Number.isInteger(e)||e<0||e>65535)throw new RangeError("streamIndex must be an unsigned 16-bit integer (0-65535)");let n=Le(s),i=Buffer.allocUnsafe(4+n.length);return i.writeUInt8(255,0),i.writeUInt8(r,1),i.writeUInt16BE(e,2),n.copy(i,4),i}function to(t){let e=Le(t);if(e.length<4)throw new Error("v2 stream frame is truncated");if(e.readUInt8(0)!==255)throw new Error("invalid v2 stream frame magic byte");let r=e.readUInt8(1);if(r!==0&&r!==1)throw new Error(`unknown v2 stream frame type: ${r}`);let n=e.readUInt16BE(2);return{version:2,type:r===0?"output":"input",typeCode:r,streamIndex:n,payload:e.subarray(4)}}function _r(t){let e=Le(t);if(e.length<4)throw new Error("stream frame is truncated");return e[0]===255?to(e):eo(e)}var yr=["black","red","green","yellow","blue","magenta","cyan","white","brightBlack","brightRed","brightGreen","brightYellow","brightBlue","brightMagenta","brightCyan","brightWhite"],so=["regular","bold","italic","boldItalic"],ro=["truetype","opentype"],no=["cjk","all"];var io=/^#[0-9a-f]{6}$/i,oo=/["'\\;,{}<>@\u0000-\u001f\u007f]/,gr=/^[0-9a-f]{64}$/;function Ne(t){return!!t&&typeof t=="object"&&!Array.isArray(t)}function Ht(t,e){return t.includes(e)}function Sr(t){return typeof t=="string"&&io.test(t)?t.toLowerCase():void 0}function Er(t){if(!Ne(t))return null;let e={};for(let r of["background","foreground","cursor"]){let n=Sr(t[r]);n&&(e[r]=n)}let s=t.ansi;if(Ne(s)){let r={};for(let n of yr){let i=Sr(s[n]);i&&(r[n]=i)}Object.keys(r).length===yr.length&&(e.ansi=r)}return Object.keys(e).length>0?e:null}function xr(t){let e=typeof t=="string"?t.trim():"";return!e||e.length>128||oo.test(e)?null:e}function rt(t){if(!Ne(t))return null;let e=xr(t.family);if(!e)return null;let s=Number(t.sizePx),r=Number.isFinite(s)&&s>=4&&s<=96?Math.round(s*10)/10:null,n=typeof t.source=="string"&&/^[a-z0-9-]{1,32}$/.test(t.source)?t.source:null,i=[],o=new Set;for(let l of Array.isArray(t.faces)?t.faces.slice(0,16):[]){if(!Ne(l))continue;let{style:c,format:f,bytes:d,sha256:u}=l;!Ht(so,c)||o.has(c)||Ht(ro,f)&&(typeof d!="number"||!Number.isInteger(d)||d<=0||d>16777216||typeof u!="string"||!gr.test(u)||(o.add(c),i.push({style:c,format:f,bytes:d,sha256:u})))}let a=[];for(let l of Array.isArray(t.subsets)?t.subsets.slice(0,16):[]){if(!Ne(l))continue;let c=xr(l.family),{scope:f,sha256:d}=l;!c||l.style!=="regular"||Ht(no,f)&&(typeof d!="string"||!gr.test(d)||a.some(u=>u.scope===f)||a.push({family:c,style:"regular",scope:f,sha256:d}))}return{family:e,...r!==null&&{sizePx:r},...n!==null&&{source:n},faces:i,subsets:a}}var ao=/^[\x21-\x7e]+$/;function br(t){if(!Array.isArray(t))return;let e=new Set;for(let s of t){if(e.size>=8)break;if(typeof s!="string")continue;let r=s.replace(/\s+/g,"").toLowerCase();r.length<=32&&ao.test(r)&&e.add(r)}return e.size>0?[...e]:void 0}var Yt="/ws/host",Tr="/ws/client",ye={hostHandoff:"host_handoff",idleHeartbeat:"idle_heartbeat",binaryFrameV2:"binary_frame_v2",adminTabs:"admin_tabs"};var wr={"image/png":".png","image/jpeg":".jpg","image/webp":".webp","image/gif":".gif"},Da=Object.keys(wr);function Ar(t){return typeof t=="string"&&Object.hasOwn(wr,t)}function vr(t,e){switch(t){case"image/png":return e.length>=8&&e[0]===137&&e[1]===80&&e[2]===78&&e[3]===71;case"image/jpeg":return e.length>=3&&e[0]===255&&e[1]===216&&e[2]===255;case"image/webp":return e.length>=12&&e[0]===82&&e[1]===73&&e[2]===70&&e[3]===70&&e[8]===87&&e[9]===69&&e[10]===66&&e[11]===80;case"image/gif":return e.length>=6&&e[0]===71&&e[1]===73&&e[2]===70&&e[3]===56;default:return!1}}var lo="dibin666/herdr-remote",Ir=`https://github.com/${lo}/releases`,Ua=`${Ir}/latest/download/latest.json`,Fa=`${Ir}/latest/download/herdr-remote.tgz`;import co from"node:crypto";var fo=500;function ge(t){return`${t}-${co.randomBytes(9).toString("base64url")}`}function z(t,e){return typeof t=="string"&&t.length>0?t.slice(0,e):null}function Me(t,e){let s=Number(t);return Number.isInteger(s)?Math.min(fo,Math.max(2,s)):e}function y(t){return!!t&&t?.readyState===T.default.OPEN}function m(t,e){y(t)&&t.send(JSON.stringify(e))}function k(t,e=1e3,s=""){if(!(!t||t.readyState===T.default.CLOSED||t.readyState===T.default.CLOSING))try{t.close(e,s.slice(0,120))}catch{}}function se(t){if(!(!t||t.readyState===T.default.CLOSED))try{typeof t.terminate=="function"?t.terminate():typeof t.destroy=="function"&&t.destroy()}catch{}}function Se(t){if(typeof t!="string")return null;try{return JSON.parse(t)}catch{return null}}function w(t,e,s="invalid_handshake"){m(t,{type:"error",code:s,message:e}),k(t,1008,e)}var Cr=4,Pr=3e4,Rr=Math.ceil(262144/3)*4,$t=/^[0-9a-f]{64}$/,uo=32*1024,ho=64,mo=16*1024*1024,kr=/^[A-Za-z0-9_-]{1,32}$/;function Wt(t){Array.isArray(t.fontChunkRequests)&&t.fontChunkRequests.length&&t.fontChunkRequests.shift()}function nt(t){Array.isArray(t.fontSubsetRequests)&&t.fontSubsetRequests.length&&t.fontSubsetRequests.shift()}function Lr(t,e,s){if(!e.session||!y(t.ws))return;let r=t.terminalFont?.faces?.find(a=>a.sha256===s.sha256),n=r?r.bytes:e.fontSubsets?.get(s.sha256)||0,i=Math.ceil(n/262144);if(!n||!Number.isInteger(s.index)||s.index<0||s.index>=i){m(e.ws,{type:"error",code:"host_font_unavailable",message:"The workstation did not offer this font file"});return}let o=Date.now();e.fontChunkRequests=(e.fontChunkRequests||[]).filter(a=>o-a<Pr),!(e.fontChunkRequests.length>=Cr)&&(e.fontChunkRequests.push(o),m(t.ws,{type:"host_font_chunk_request",clientId:e.session.streamId,streamId:e.session.streamId,sha256:s.sha256,index:s.index}))}function Nr(t,e,s){if(!e.session||!y(t.ws))return;if(!(t.terminalFont?.subsets?.some(o=>o.sha256===s.sha256)&&typeof s.text=="string"&&s.text.length>0&&s.text.length<=uo&&typeof s.requestId=="string"&&kr.test(s.requestId))){m(e.ws,{type:"error",code:"host_font_unavailable",message:"The workstation did not offer this font"});return}let i=Date.now();e.fontSubsetRequests=(e.fontSubsetRequests||[]).filter(o=>i-o<Pr),!(e.fontSubsetRequests.length>=Cr)&&(e.fontSubsetRequests.push(i),m(t.ws,{type:"host_font_subset_request",clientId:e.session.streamId,streamId:e.session.streamId,requestId:s.requestId,sha256:s.sha256,text:s.text}))}function Mr(t,e){if(nt(t),!!(typeof e.sha256=="string"&&$t.test(e.sha256)&&typeof e.subsetSha=="string"&&$t.test(e.subsetSha)&&typeof e.requestId=="string"&&kr.test(e.requestId)&&Number.isInteger(e.bytes)&&e.bytes>0&&e.bytes<=mo&&(e.dataBase64===void 0||typeof e.dataBase64=="string"&&e.dataBase64.length<=Rr))){if(e.dataBase64===void 0)for(t.fontSubsets||=new Map,t.fontSubsets.delete(e.subsetSha),t.fontSubsets.set(e.subsetSha,e.bytes);t.fontSubsets.size>ho;){let r=t.fontSubsets.keys().next().value;r!==void 0&&t.fontSubsets.delete(r)}m(t.ws,{type:"host_font_subset_ready",requestId:e.requestId,sha256:e.sha256,subsetSha:e.subsetSha,bytes:e.bytes,...e.dataBase64!==void 0?{dataBase64:e.dataBase64}:{}})}}function Dr(t,e){Wt(t),typeof e.sha256=="string"&&$t.test(e.sha256)&&Number.isInteger(e.index)&&e.index>=0&&Number.isInteger(e.total)&&e.total>e.index&&typeof e.dataBase64=="string"&&e.dataBase64.length<=Rr&&m(t.ws,{type:"host_font_chunk",sha256:e.sha256,index:e.index,total:e.total,dataBase64:e.dataBase64})}import Gt from"node:crypto";function po(t){let e=t.headers.authorization;if(typeof e!="string")return null;let s=/^Bearer\s+(.+)$/i.exec(e.trim());return s?s[1]:null}function _o(t,e){if(typeof t!="string"||t.length===0||typeof e!="string"||e.length===0)return!1;let s=Gt.createHash("sha256").update(t,"utf8").digest(),r=Gt.createHash("sha256").update(e,"utf8").digest();return Gt.timingSafeEqual(s,r)}function jt(t,e,s){if(!e||(t.config.relay.allowedOrigins||[]).includes(e))return!0;try{return new URL(e).host===s.headers.host}catch{return!1}}function Br(t,e){let s=e.headers.origin;return s?jt(t,s,e):!0}function Vt(t,e){let s=e.headers["x-herdr-host-id"],r=e.headers["x-herdr-host-token"];return typeof s!="string"||typeof r!="string"?null:t.auth.authenticateHost(s,r)?s:null}function yo(t,e){let s=po(e);return s?t.auth.authenticateDevice(s):null}function Ur(t,e){let s=e.headers["x-herdr-host-id"],r=e.headers["x-herdr-host-token"],n=s!==void 0||r!==void 0,i=e.headers.authorization!==void 0,o=Vt(t,e),a=yo(t,e);return n&&!o||i&&!a||o&&a&&o!==a.hostId?null:o?{kind:"host",hostId:o}:a?{kind:"device",hostId:a.hostId,deviceId:a.deviceId}:null}function Fr(t,e){return _o(e.headers["x-relay-admin-token"],t.adminToken)}function Hr(t,e){if(!t.trustProxy)return null;let s=e.headers["x-forwarded-for"];return typeof s!="string"||s.length===0?null:s.split(",")[0].trim()||null}function go(t,e){return Hr(t,e)||e.socket.remoteAddress||"unknown"}function Yr(t,e){return Hr(t,e)||e.socket.remoteAddress||null}function qt(t,e,s,r=20){let n=go(t,s),i=Date.now(),o=e.get(n);if(!o||i-o.startedAt>=6e4){if(e.size>=4096){let a=e.keys().next().value;a!==void 0&&e.delete(a)}return e.set(n,{startedAt:i,count:1}),!0}return o.count>=r?!1:(o.count+=1,!0)}function it(t,e){return qt(t,t.pairAttempts,e,20)}function $r(t,e){return qt(t,t.clientHandshakeAttempts,e,60)}function Wr(t,e){return qt(t,t.hostHandshakeAttempts,e,60)}function xe(t){!t||!y(t.ws)||m(t.ws,{type:"client_count",clientCount:t.clients.size})}function Gr(t,e,s,r){if(!s||!y(s.ws))return!1;let n=t.config.relay.maxBufferedBytesPerClient;if((Number(s.ws.bufferedAmount)||0)+r.length>n)return t.metrics.recordCleanup("slowClientsDropped"),C(t,s,{notify:!0,reason:"slow_client",closeCode:1013}),!1;let o=!1,a=()=>{if(y(s.ws))try{s.ws.send(r),s.bytesSent+=r.length,t.metrics.recordOut(r.length,e.id),o=!0}catch{C(t,s,{notify:!1,reason:"client_send_failed",closeCode:1011})}};return t.devLatencyMs>0?zt(t,s.ws,a):a(),t.devLatencyMs>0?!0:o}function zt(t,e,s){let r=t.sendQueues.get(e);if(!r){let i={items:[],timer:null};r=i,t.sendQueues.set(e,i),e.once("close",()=>{i.timer&&(clearTimeout(i.timer),t.activeDelayTimers.delete(i.timer),i.timer=null),i.items=[]})}let n=Date.now()+t.devDelayMs;if(r.items.push({sendAt:n,task:s}),!r.timer){let i=r,o=setTimeout(()=>jr(t,e,i),t.devDelayMs);r.timer=o,t.activeDelayTimers.add(o)}}function jr(t,e,s){s.timer&&(t.activeDelayTimers.delete(s.timer),s.timer=null);let r=Date.now();for(;s.items.length>0&&s.items[0].sendAt<=r;){let n=s.items.shift();try{n?.task()}catch{}}if(s.items.length>0&&!s.timer){let n=Math.max(0,s.items[0].sendAt-Date.now()),i=setTimeout(()=>jr(t,e,s),n);s.timer=i,t.activeDelayTimers.add(i)}}function X(t,e,s){for(let r of[...e.clients]){let n=t.clients.get(r);if(!n)continue;let i=s(n);i&&m(n.ws,i)}}var Xt=20,Kt=6;function So(t){if(!t)return null;let e=65536;for(let s=0;s<e;s++){let r=t.nextStreamIndex;if(t.nextStreamIndex=t.nextStreamIndex+1&65535,!t.streamIndices.has(r))return r}return null}function Ee(t,e,s){let r=s.session;r&&(r.streamIndex!==null&&r.streamIndex!==void 0&&e&&e.streamIndices.delete(r.streamIndex),t.streams.delete(r.streamId),s.session=null)}function ot(t,e,s,{restarted:r=!1}={}){if(!s||!y(e.ws))return;let n=ge("session"),i=null;e.binaryFrameV2&&(i=So(e),i!==null&&e.streamIndices.set(i,s.id)),s.session={streamId:n,streamIndex:i,cols:s.cols,rows:s.rows,ready:!1},t.streams.set(n,s.id),m(e.ws,{type:"session_start",clientId:n,streamId:n,cols:Math.max(Xt,s.cols),rows:Math.max(Kt,s.rows),role:"controller",...i!==null?{streamIndex:i}:{}}),r&&m(s.ws,{type:"session_restarted",streamId:n,cols:s.cols,rows:s.rows,hostname:e.hostname,platform:e.platform,...e.shellProfile?{shellProfile:e.shellProfile}:{},adminTerminalSupported:e.adminTabsAvailable,terminalPalette:e.terminalPalette||null,terminalFont:e.terminalFont||null,hostVersion:e.version,...e.herdrPrefixKeys?{herdrPrefixKeys:e.herdrPrefixKeys}:{}})}function Jt(t,e){for(let s of e.clients){let r=t.clients.get(s);r&&(r.role="controller",r.controllerId=null,m(r.ws,{type:"control_state",role:"controller",controllerId:null,clientCount:e.clients.size}))}}function Vr(t,e){if(!e)return 0;let s=0;for(let r of[...t.clients.values()])!r||r.deviceId!==e||(C(t,r,{notify:!1,reason:"device_revoked"}),k(r.ws,1e3,"this device has been revoked by the relay operator"),s+=1);return s}function C(t,e,{notify:s=!0,reason:r="client_disconnected",closeCode:n=1e3,terminate:i=!1}={}){if(!e||!t.clients.has(e.id))return;t.clients.delete(e.id);let o=t.hosts.get(e.hostId);if(e.session){let l=e.session.streamId;Ee(t,o,e),o&&y(o.ws)&&m(o.ws,{type:"session_stop",clientId:l,streamId:l})}o&&(o.clients.delete(e.id),xe(o),Jt(t,o),o.clients.size===0&&(o.controllerId=null,o.reconnecting&&N(t,o,{notify:!1,reason:"no_clients"})));let a=e.ws;e.ws=null,s&&m(a,{type:"error",code:r,message:r==="host_offline"?"Herdr host is offline":"connection closed"}),i||a?.readyState===T.default.CLOSING?se(a):k(a,n,r),t.metrics.recordCleanup("closedPtysCleaned")}function N(t,e,{notify:s=!0,reason:r="host_disconnected",terminate:n=!1}={}){if(!e||t.hosts.get(e.id)!==e)return;e.reconnectTimer&&clearTimeout(e.reconnectTimer),e.reconnectTimer=null,t.hosts.delete(e.id);for(let o of[...e.clients]){let a=t.clients.get(o);if(!a)continue;t.clients.delete(a.id),Ee(t,e,a);let l=a.ws;a.ws=null,s&&m(l,{type:"error",code:r,message:"Herdr host disconnected"}),n||l?.readyState===T.default.CLOSING?se(l):k(l,1012,r)}e.clients.clear(),t.metrics.forgetHost(e.id);let i=e.ws;e.ws=null,n||i?.readyState===T.default.CLOSING?se(i):k(i,1e3,r)}function at(t,e){if(!e?.handoffCapable||e.clients.size===0)return!1;for(let s of e.clients)if(!t.clients.get(s)?.handoffCapable)return!1;return!0}function lt(t,e,s="host_disconnected"){if(!e||t.hosts.get(e.id)!==e||e.reconnecting)return;if(!at(t,e)){N(t,e,{notify:!0,reason:s});return}e.ws=null,e.reconnecting=!0,e.reconnectStartedAt=Date.now(),e.lastSeenAt=Date.now(),e.load={},e.ptys=[];for(let n of e.clients){let i=t.clients.get(n);i&&Ee(t,e,i)}X(t,e,()=>({type:"host_reconnecting",code:s}));let r=setTimeout(()=>{e.reconnectTimer=null,t.hosts.get(e.id)===e&&e.reconnecting&&N(t,e,{notify:!0,reason:"host_reconnect_timeout"})},t.config.relay.hostReconnectGraceMs);e.reconnectTimer=r,r.unref?.()}var bo=3e3,To=2e3,wo=1e3;function Ao(t,e){if(typeof e!="string"||e.length===0)return!1;let s;try{s=Buffer.from(e.slice(0,32),"base64")}catch{return!1}return s.length===0?!1:vr(t,s)}function qr(t,e,s){let r={ws:e,req:s,authenticated:!1},n=setTimeout(()=>{r.authenticated||k(e,1008,"client hello timeout")},1e4);e.isAlive=!0,e.on("pong",()=>{e.isAlive=!0,r.client&&(r.client.lastSeenAt=Date.now(),r.client.lastPingAt=new Date().toISOString())}),e.on("ping",()=>{e.isAlive=!0,r.client&&(r.client.lastSeenAt=Date.now(),r.client.lastPingAt=new Date().toISOString())}),e.on("message",(i,o)=>{if(!r.authenticated){if(o)return w(e,"client hello must be JSON");let a=Se(i.toString());if(a?.type!=="hello"||a.protocol!==1)return w(e,"invalid client hello");if(!$r(t,s))return w(e,"too many connection attempts","rate_limited");let l=null,c=null;if(a.pairCode){if(!it(t,s))return w(e,"too many pairing attempts","rate_limited");c=t.auth.completePairing(a.pairCode)}if(c?l=c:a.token&&(l=t.auth.authenticateDevice(a.token)),!l)return w(e,"valid device token or pairing code required","auth_required");let f=t.hosts.get(l.hostId);if(!f||f.reconnecting||!y(f.ws))return w(e,"paired Herdr host is offline",f?.reconnecting?"host_reconnecting":"host_offline");if(f.clients.size>=t.config.relay.maxClientsPerHost)return w(e,"host client limit reached","too_many_clients");clearTimeout(n),t.finishHandshake(e);let u={id:ge("client"),ws:e,hostId:f.id,deviceId:l.deviceId,browserClientId:typeof a.clientId=="string"?a.clientId.slice(0,128):null,handoffCapable:Array.isArray(a.capabilities)&&a.capabilities.includes(ye.hostHandoff),session:null,role:"controller",controllerId:null,connectedAt:new Date().toISOString(),connectedAtMs:Date.now(),lastSeenAt:Date.now(),lastPingAt:null,bytesReceived:0,bytesSent:0,ip:Yr(t,s),userAgent:String(s.headers["user-agent"]||"").slice(0,256),cols:Me(a.cols,80),rows:Me(a.rows,24)};u.controllerId=null,t.auth.noteDeviceSeen(l.deviceId,{userAgent:u.userAgent,ip:u.ip}),r.authenticated=!0,r.client=u,t.clients.set(u.id,u),f.clients.add(u.id),xe(f),e.isAlive=!0,c&&m(e,{type:"paired",token:c.token,deviceId:c.deviceId,hostId:c.hostId,expiresAt:c.expiresAtIso}),m(e,{type:"ready",role:u.role,controllerId:null,hostId:f.id,hostname:f.hostname,platform:f.platform,...f.shellProfile?{shellProfile:f.shellProfile}:{},adminTerminalSupported:f.adminTabsAvailable,clientId:u.id,terminalPalette:f.terminalPalette||null,terminalFont:f.terminalFont||null,clientCount:f.clients.size,hostVersion:f.version,...f.herdrPrefixKeys?{herdrPrefixKeys:f.herdrPrefixKeys}:{}}),f.agentStatus&&m(e,f.agentStatus),f.updateStatus&&m(e,f.updateStatus),ot(t,f,u),Jt(t,f);return}r.client&&vo(t,r.client,i,o)}),e.on("close",()=>{clearTimeout(n),r.client&&C(t,r.client,{notify:!0})}),e.on("error",()=>{})}function vo(t,e,s,r){e.lastSeenAt=Date.now();let n=t.hosts.get(e.hostId);if(!n){C(t,e,{notify:!0,reason:"host_offline"});return}if(r){let o=s;if(o.length>t.config.relay.maxPayloadBytes||!e.session)return;let a=n.binaryFrameV2&&typeof e.session.streamIndex=="number"?pr(1,e.session.streamIndex,o):mr("input",e.session.streamId,o);if(y(n.ws)){let l=()=>{if(y(n.ws))try{n.ws.send(a),e.bytesReceived+=o.length,t.metrics.recordIn(o.length,n.id)}catch{lt(t,n,"host_send_failed")}};t.devLatencyMs>0?zt(t,n.ws,l):l()}return}let i=Se(s.toString());if(!i){m(e.ws,{type:"error",code:"invalid_json",message:"message must be JSON"});return}if(i.type==="ping")e.lastPingAt=new Date().toISOString(),m(e.ws,{type:"pong"});else if(i.type==="resize")e.cols=Me(i.cols,e.cols),e.rows=Me(i.rows,e.rows),e.session&&(e.session.cols=e.cols,e.session.rows=e.rows,y(n.ws)&&m(n.ws,{type:"resize",clientId:e.session.streamId,streamId:e.session.streamId,cols:Math.max(Xt,e.cols),rows:Math.max(Kt,e.rows)}));else if(i.type==="herdr_start"){if(!e.session){m(e.ws,{type:"error",code:"no_session",message:"terminal session is not ready"});return}let o=Date.now();if(e.herdrStartAt&&o-e.herdrStartAt<bo)return;e.herdrStartAt=o,y(n.ws)&&m(n.ws,{type:"herdr_start",clientId:e.session.streamId,streamId:e.session.streamId})}else if(i.type==="admin_tab_open"){if(!e.session||!y(n.ws))return;if(!n.adminTabsAvailable){m(e.ws,{type:"error",code:"admin_terminal_unsupported",message:"Update the host connector to open administrator terminals."});return}let o=Date.now();if(e.adminTabAt&&o-e.adminTabAt<wo)return;e.adminTabAt=o,m(n.ws,{type:"admin_tab_open",clientId:e.session.streamId,streamId:e.session.streamId})}else if(i.type==="host_font_chunk_request")Lr(n,e,i);else if(i.type==="host_font_subset_request")Nr(n,e,i);else if(i.type==="host_font_refresh"){if(!e.session||!y(n.ws))return;let o=Date.now();if(e.fontRefreshAt&&o-e.fontRefreshAt<To)return;e.fontRefreshAt=o,m(n.ws,{type:"host_font_refresh",clientId:e.session.streamId,streamId:e.session.streamId})}else if(i.type==="claim_control")e.role="controller",m(e.ws,{type:"control_granted"});else if(i.type==="release_control")m(e.ws,{type:"control_state",role:"controller",controllerId:null});else if(i.type==="paste_file"){if(e.role==="viewer"){m(e.ws,{type:"error",code:"viewer_mode",message:"Viewer mode cannot paste to terminal"});return}if(!Ar(i.mime)){m(e.ws,{type:"error",code:"paste_file_unsupported",message:"Unsupported paste image format"});return}if(typeof i.dataBase64!="string"){m(e.ws,{type:"error",code:"paste_file_unsupported",message:"Invalid paste image payload"});return}let o=Buffer.byteLength(i.dataBase64,"base64");if(o===0){m(e.ws,{type:"error",code:"paste_file_empty",message:"Pasted image payload is empty"});return}if(o>3145728){m(e.ws,{type:"error",code:"paste_file_too_large",message:"Pasted image exceeds 3 MB limit"});return}if(!Ao(i.mime,i.dataBase64)){m(e.ws,{type:"error",code:"paste_file_unsupported",message:"Pasted image content does not match declared MIME type"});return}if(!y(n?.ws)){m(e.ws,{type:"error",code:"host_offline",message:"Host is offline or disconnected"});return}if(!e.session){m(e.ws,{type:"error",code:"no_session",message:"Terminal session is not ready"});return}m(n.ws,{type:"paste_file",clientId:e.session.streamId,streamId:e.session.streamId,mime:i.mime,dataBase64:i.dataBase64})}}import Co from"node:os";var Io=/^\d{1,6}\.\d{1,6}\.\d{1,6}(?:-[0-9A-Za-z.-]{1,32})?$/;function De(t){return typeof t=="string"&&Io.test(t)?t:null}var Oo=16;function zr(t,e,s){let r={};if(s.counts&&typeof s.counts=="object")for(let[o,a]of Object.entries(s.counts))typeof a=="number"&&Number.isFinite(a)&&(r[o.slice(0,32)]=Math.max(0,Math.min(9999,Math.trunc(a))));let n=(Array.isArray(s.agents)?s.agents:[]).slice(0,Oo).map(o=>({paneId:z(o?.paneId,64),workspaceId:z(o?.workspaceId,64),agent:z(o?.agent,32),title:z(o?.title,64),status:z(o?.status,16),focused:o?.focused===!0})),i={type:"agent_status",focusedPaneId:z(s.focusedPaneId,64),focusedAgent:z(s.focusedAgent,32),counts:r,total:typeof s.total=="number"&&Number.isFinite(s.total)?Math.max(0,Math.trunc(s.total)):n.length,agents:n};e.agentStatus=i,X(t,e,()=>i)}function Xr(t,e,s){let r=De(s.current),n=De(s.latest);if(!r||!n)return;let i={type:"update_status",current:r,installed:De(s.installed)||r,latest:n,updateAvailable:s.updateAvailable===!0,restartPending:s.restartPending===!0,updateCommand:s.updateCommand===!0};e.updateStatus=i,X(t,e,()=>i)}function Kr(t,e,s){e.terminalFont=rt(s.terminalFont);let r={type:"terminal_font",terminalFont:e.terminalFont||null};X(t,e,()=>r)}function Po(t,e,s,r=new Set){let n=Array.isArray(t.capabilities)?t.capabilities:[],i=br(t.herdrPrefixKeys);return{id:t.hostId,ws:e,hostname:typeof t.hostname=="string"?t.hostname.slice(0,128):Co.hostname(),platform:typeof t.platform=="string"?t.platform.slice(0,32):process.platform,...t.shellProfile==="git-bash"?{shellProfile:t.shellProfile}:{},adminTabsAvailable:n.includes(ye.adminTabs),arch:typeof t.arch=="string"?t.arch.slice(0,32):process.arch,connectedAt:new Date(s.connectedAt).toISOString(),connectedAtMs:s.connectedAt,terminalPalette:Er(t.terminalPalette),terminalFont:rt(t.terminalFont),...i?{herdrPrefixKeys:i}:{},agentStatus:null,updateStatus:null,version:De(t.version),lastSeenAt:Date.now(),clients:r,controllerId:null,load:{},ptys:[],reconnecting:!1,reconnectTimer:null,connectionGeneration:ge("host-connection"),handoffCapable:n.includes(ye.hostHandoff),binaryFrameV2:n.includes(ye.binaryFrameV2),streamIndices:new Map,nextStreamIndex:0,shutdownRequested:!1}}function Jr(t,e,s){let r={ws:e,remoteAddress:s.socket.remoteAddress,connectedAt:Date.now(),authenticated:!1},n=setTimeout(()=>{r.authenticated||k(e,1008,"host hello timeout")},1e4);e.isAlive=!0,e.on("pong",()=>{e.isAlive=!0,r.host&&(r.host.lastSeenAt=Date.now())}),e.on("ping",()=>{e.isAlive=!0,r.host&&(r.host.lastSeenAt=Date.now())}),e.on("message",(i,o)=>{if(!r.authenticated){if(o)return w(e,"host hello must be JSON");let a=Se(i.toString());if(a?.type!=="host_hello"||a.protocol!==1)return w(e,"invalid host hello");if(!Wr(t,s))return w(e,"too many connection attempts","rate_limited");let l=t.hosts.get(a.hostId);if(!l&&t.hosts.size>=t.config.relay.maxHosts)return w(e,"relay host limit reached","too_many_hosts");let c=t.auth.registerHost(a.hostId,a.token,a.password??null);if(!c.ok)return w(e,c.message,c.code);clearTimeout(n),r.authenticated=!0,t.finishHandshake(e);let f=at(t,l),d=f&&l?l.clients:new Set;if(l)if(l.reconnectTimer&&clearTimeout(l.reconnectTimer),l.reconnectTimer=null,f){for(let h of l.clients){let p=t.clients.get(h);p?.session&&(y(l.ws)&&m(l.ws,{type:"session_stop",clientId:p.session.streamId,streamId:p.session.streamId}),Ee(t,l,p))}l.load={},l.ptys=[],X(t,l,()=>({type:"host_reconnecting",code:"host_replaced"}))}else N(t,l,{notify:!0,reason:"host_replaced"});let u=Po(a,e,r,d);if(r.host=u,t.hosts.set(u.id,u),l&&f&&k(l.ws,1e3,"host_replaced"),m(e,{type:"host_ready",protocol:1,hostId:u.id,clientCount:u.clients.size}),xe(u),f)for(let h of u.clients){let p=t.clients.get(h);p&&ot(t,u,p,{restarted:!0})}return}r.host&&Ro(t,r.host,i,o)}),e.on("close",(i,o)=>{clearTimeout(n);let a=r.host;if(!a||t.hosts.get(a.id)!==a||a.ws!==e)return;if((o?o.toString():"")==="host_shutdown"&&(a.shutdownRequested=!0),a.shutdownRequested||!at(t,a)){N(t,a,{notify:!0,reason:a.shutdownRequested?"host_shutdown":"host_disconnected"});return}lt(t,a,"host_disconnected")}),e.on("error",()=>{})}function Ro(t,e,s,r){if(e.lastSeenAt=Date.now(),r){let c;try{c=_r(s)}catch(u){t.metrics.recordCleanup("deadConnectionsClosed"),k(e.ws,1003,u.message);return}if(c.type!=="output")return;let f=c.version===2?e.streamIndices.get(c.streamIndex):t.streams.get(c.streamId);if(!f)return;let d=t.clients.get(f);if(!d||!y(d.ws))return;Gr(t,e,d,c.payload);return}let n=Se(s.toString());if(!n)return;if(n.type==="heartbeat"){e.load=n.load&&typeof n.load=="object"?n.load:{},e.ptys=Array.isArray(n.ptys)?n.ptys.slice(0,256):[];return}if(n.type==="host_shutdown"){e.shutdownRequested=!0,t.hosts.get(e.id)===e&&N(t,e,{notify:!0,reason:"host_shutdown"});return}if(n.type==="agent_status"){zr(t,e,n);return}if(n.type==="update_status"){Xr(t,e,n);return}if(n.type==="terminal_font"){Kr(t,e,n);return}let i=n,o=typeof i.clientId=="string"?i.clientId:i.streamId,a=o?t.streams.get(o):null,l=a?t.clients.get(a):null;if(l)if(n.type==="session_ready")l.session&&(l.session.ready=!0),m(l.ws,{type:"session_ready",clientId:l.id});else if(n.type==="session_exit"){let c=Number.isInteger(n.code)?n.code:null;m(l.ws,{type:"exit",code:c}),Ee(t,e,l),C(t,l,{notify:!1})}else n.type==="host_font_chunk"?Dr(l,n):n.type==="host_font_subset_ready"?Mr(l,n):n.type==="paste_file_ready"?m(l.ws,{type:"paste_file_ready",path:n.path}):n.type==="error"&&(n.code==="host_font_unavailable"&&(Wt(l),nt(l)),n.code==="host_font_subset_failed"&&nt(l),m(l.ws,{type:"error",code:n.code||"host_error",message:String(n.message||"Host connector error")}))}import Zr from"node:fs";import re from"node:path";import{URL as No}from"node:url";function Zt(t,{sample:e=!0,includeDevices:s=!1,scopeHostId:r=null}={}){let n=r?[...t.clients.values()].filter(h=>h.hostId===r):[...t.clients.values()],i=r?[...t.hosts.values()].filter(h=>h.id===r):[...t.hosts.values()],o=n.map(h=>({id:h.id,role:h.role,...r?{}:{hostId:h.hostId},...s?{deviceId:h.deviceId}:{},userAgent:h.userAgent,connectedAt:h.connectedAt,lastPingAt:h.lastPingAt,bytesReceived:h.bytesReceived,bytesSent:h.bytesSent,ip:h.ip})),a=t.auth.listDevices(),l=new Map;for(let h of a)l.set(h.hostId,(l.get(h.hostId)||0)+1);let c=i.map(h=>({id:h.id,hostname:h.hostname,platform:h.platform,arch:h.arch,status:h.reconnecting?"reconnecting":h.clients.size?"busy":"online",connectedAt:h.connectedAt,activePtyCount:h.ptys.length,connectedDeviceCount:st([...h.clients].map(p=>t.clients.get(p)).filter(p=>!!p)),pairedDeviceCount:l.get(h.id)||0,version:h.version??h.updateStatus?.current??null,latestVersion:h.updateStatus?.updateAvailable?h.updateStatus.latest:null,load:h.load})),f=i.flatMap(h=>h.ptys.map(p=>({...p,...r?{}:{hostId:h.id},activeClients:h.clients.size}))),d=s?a:void 0,u=t.address();return{...t.metrics.snapshot({clients:o,hosts:c,ptys:f,sample:e,scopeHostId:r,activeUserCount:st(n)}),...d?{devices:d}:{},relayMode:t.relayMode,isRemoteRelay:t.relayMode==="remote",remoteAdminUrl:t.relayMode==="remote"?`${String(t.config.relay.publicUrl||"").replace(/\/+$/,"")}/admin`:void 0,relay:{mode:t.relayMode,publicUrl:t.config.relay.publicUrl,bind:t.config.relay.host,port:(u&&"port"in u?u.port:void 0)||t.config.relay.port,maxClientsPerHost:t.config.relay.maxClientsPerHost,maxHosts:t.config.relay.maxHosts,maxPendingHandshakes:t.config.relay.maxPendingHandshakes,maxBufferedBytesPerClient:t.config.relay.maxBufferedBytesPerClient,hostReconnectGraceMs:t.config.relay.hostReconnectGraceMs,adminConfigured:!!t.adminToken,adminStatusPath:"/api/admin/status",dashboardPath:"/admin"}}}import ko from"node:fs";import Lo from"node:path";var W=JSON.parse(ko.readFileSync(Lo.join(ae,"package.json"),"utf8")).version;var Qr=re.join(ae,"web","dist"),Mo=10080*60,Do={".html":"text/html; charset=utf-8",".js":"text/javascript; charset=utf-8",".css":"text/css; charset=utf-8",".json":"application/json",".svg":"image/svg+xml",".png":"image/png",".ico":"image/x-icon",".woff2":"font/woff2"};function Qt(t,e="application/json"){t.setHeader("Content-Type",e),t.setHeader("Cache-Control","no-store"),t.setHeader("X-Content-Type-Options","nosniff"),t.setHeader("X-Frame-Options","DENY"),t.setHeader("Referrer-Policy","no-referrer"),t.setHeader("Content-Security-Policy","default-src 'self'; base-uri 'none'; object-src 'none'; frame-ancestors 'none'; connect-src 'self' ws: wss:; img-src 'self' data:; style-src 'self' 'unsafe-inline'; script-src 'self'")}function ne(t,e,s){Qt(t),t.writeHead(e),t.end(JSON.stringify(s))}function A(t,e,s,r){ne(t,e,{ok:!1,code:s,message:r})}function en(t,e,s){return t.adminToken?Fr(t,e)?!0:(A(s,401,"admin_auth_required","a valid relay admin token is required"),!1):(A(s,503,"admin_not_configured","configure RELAY_ADMIN_TOKEN to access the relay dashboard"),!1)}var Bo=(t,e,s)=>{ne(s,200,{ok:!0,version:W,protocol:1,uptimeSeconds:Math.floor((Date.now()-t.startedAt)/1e3)})},Uo=(t,e,s)=>{let r=String(t.config.relay.publicUrl||"").replace(/\/+$/,"");ne(s,200,{ok:!0,version:W,protocol:1,relayMode:t.relayMode,isRemoteRelay:t.relayMode==="remote",adminConfigured:!!t.adminToken,adminPath:"/admin",adminStatusPath:"/api/admin/status",publicUrl:r,remoteAdminUrl:t.relayMode==="remote"?`${r}/admin`:null})},Fo=(t,e,s)=>{let r=Ur(t,e);if(!r){A(s,401,"auth_required","an authorized device or host token is required");return}ne(s,200,Zt(t,{scopeHostId:r.hostId}))},Ho=(t,e,s)=>{en(t,e,s)&&ne(s,200,Zt(t,{includeDevices:!0}))},Yo=(t,e,s,r)=>{if(!en(t,e,s))return;let n=decodeURIComponent(r.pathname.slice(19)),i=t.auth.revokeDevice(n);if(!i){A(s,404,"device_not_found","no such paired device");return}let o=Vr(t,n);ne(s,200,{ok:!0,deviceId:i.deviceId,disconnected:o})},$o=(t,e,s)=>{if(!it(t,e)){A(s,429,"rate_limited","too many pairing attempts");return}let r=Vt(t,e);if(!r){A(s,401,"host_auth_required","a valid host id and token are required");return}let n=t.hosts.get(r);if(!n||n.reconnecting||!y(n.ws)){A(s,409,"host_offline","no Herdr host is connected");return}try{let i=t.auth.startPairing(r,String(t.config.relay.publicUrl).replace(/\/$/,"")),o={ok:!0,...i,pairUrl:`${i.publicUrl}/?pairCode=${encodeURIComponent(i.code)}`};ne(s,200,o)}catch(i){let{code:o,message:a}=i;A(s,409,o||"pairing_failed",a)}},Wo=[{method:"GET",path:"/healthz",handle:Bo},{method:"GET",path:"/api/info",handle:Uo},{method:"GET",path:"/api/status",handle:Fo},{method:"GET",path:"/api/admin/status",handle:Ho},{method:"DELETE",path:"/api/admin/devices/",prefix:!0,handle:Yo},{method:"POST",path:"/api/pair/start",handle:$o}];function tn(t,e,s){let r;try{r=new No(e.url||"",`http://${e.headers.host||"localhost"}`)}catch{A(s,400,"bad_request","invalid URL");return}if(!Br(t,e)){A(s,403,"origin_denied","origin is not allowed");return}if(e.method==="OPTIONS"){s.writeHead(204,{"Access-Control-Allow-Origin":e.headers.origin||"*","Access-Control-Allow-Headers":"Authorization, Content-Type, X-Herdr-Host-Id, X-Herdr-Host-Token, X-Relay-Admin-Token","Access-Control-Allow-Methods":"GET, POST, DELETE, OPTIONS",Vary:"Origin"}),s.end();return}e.headers.origin&&(s.setHeader("Access-Control-Allow-Origin",e.headers.origin),s.setHeader("Vary","Origin"));let{pathname:n}=r,i=Wo.find(o=>o.method===e.method&&(o.prefix?n.startsWith(o.path):n===o.path));if(i){i.handle(t,e,s,r);return}if(n.startsWith("/api/")){A(s,404,"not_found","API route not found");return}Go(t.publicDir,n,s)}function Go(t,e,s){let r=e==="/"||e==="/admin"?"index.html":e.replace(/^\/+/,""),n=re.resolve(t,r);if(!n.startsWith(`${t}${re.sep}`)&&n!==re.join(t,"index.html")){A(s,403,"forbidden","path is not allowed");return}Zr.readFile(n,(i,o)=>{if(i&&!re.extname(r)){Zr.readFile(re.join(t,"index.html"),(l,c)=>{if(l){A(s,503,"web_not_built","frontend has not been built");return}Qt(s,"text/html; charset=utf-8"),s.end(c)});return}if(i){A(s,404,"not_found","file not found");return}let a=re.extname(n).toLowerCase();Qt(s,Do[a]||"application/octet-stream"),r.startsWith("fonts/")&&s.setHeader("Cache-Control",`public, max-age=${Mo}`),s.end(o)})}function sn(t){let e=[...[...t.hosts.values()].map(s=>s.ws),...[...t.clients.values()].map(s=>s.ws)].filter(s=>!!(s&&s.readyState!==T.default.CLOSED));for(let s of e){if(s.readyState===T.default.CLOSING){t.metrics.recordCleanup("deadConnectionsClosed"),se(s);continue}if(s.readyState===T.default.OPEN){if(!s.isAlive){t.metrics.recordCleanup("deadConnectionsClosed"),se(s);continue}s.isAlive=!1;try{s.ping()}catch{}}}}function es(t,e){for(let[s,r]of t.entries())e-r.startedAt>=6e4&&t.delete(s)}function rn(t){let e=Date.now(),s=t.config.cleanup.staleAfterMs;es(t.pairAttempts,e),es(t.clientHandshakeAttempts,e),es(t.hostHandshakeAttempts,e);for(let n of[...t.clients.values()])e-n.lastSeenAt>s&&(t.metrics.recordCleanup("staleClientsPurged"),C(t,n,{notify:!1,reason:"stale_client",terminate:!0}));for(let n of[...t.hosts.values()])n.reconnecting||e-n.lastSeenAt>s&&(t.metrics.recordCleanup("deadConnectionsClosed"),N(t,n,{notify:!0,reason:"stale_host",terminate:!0}));let r=t.auth.cleanup(e);(r.removedDevices||r.removedPairings)&&t.metrics.recordCleanup("staleClientsPurged",r.removedDevices),t.metrics.cleanup.lastCleanupAt=new Date(e).toISOString()}var ct=class{config;relayMode;hosts=new Map;clients=new Map;streams=new Map;pairAttempts=new Map;clientHandshakeAttempts=new Map;hostHandshakeAttempts=new Map;pendingHandshakes=new Set;startedAt=Date.now();metrics;stateFile;password;adminToken;trustProxy;devLatencyMs;devDelayMs;publicDir;sendQueues=new WeakMap;activeDelayTimers=new Set;auth;server;wss;heartbeatTimer=null;cleanupTimer=null;constructor(e=Be().config,s={}){this.config={relay:{...x.relay,...e.relay},auth:{...x.auth,...e.auth},cleanup:{...x.cleanup,...e.cleanup}};let{relay:r,auth:n}=this.config;this.relayMode=r.mode==="local"?"local":"remote",this.metrics=s.metrics||new tt({version:W,protocolVersion:1}),this.stateFile=s.stateFile||n.stateFile||ts.join(ht(),"relay-auth.json"),this.password=s.password??n.password??null,this.adminToken=s.adminToken??n.adminToken??null,this.trustProxy=!!(s.trustProxy??r.trustProxy),this.publicDir=ts.resolve(s.publicDir??Qr);let i=s.devLatencyMs??r.devLatencyMs??process.env.RELAY_DEV_LATENCY_MS;this.devLatencyMs=i?Math.max(0,Number.parseInt(String(i),10)||0):0,this.devDelayMs=this.devLatencyMs>0?Math.round(this.devLatencyMs/2):0,this.auth=s.auth||new Ze({stateFile:this.stateFile,pairingTtlMs:n.pairingTtlMs,deviceTtlMs:n.deviceTtlMs,maxDevices:n.maxDevices,password:this.password}),this.server=jo.createServer((o,a)=>tn(this,o,a)),this.wss=new Dt.default({noServer:!0,clientTracking:!1,maxPayload:r.maxPayloadBytes,perMessageDeflate:{threshold:1024,zlibDeflateOptions:{level:3},serverNoContextTakeover:!1,clientNoContextTakeover:!1,concurrencyLimit:10}}),this.server.on("upgrade",(o,a,l)=>this.handleUpgrade(o,a,l))}listen(e=this.config.relay.port,s=this.config.relay.host){return ke(ts.dirname(this.stateFile)),new Promise((r,n)=>{let i=a=>{this.server.off("listening",o),n(a)},o=()=>{this.server.off("error",i),this.heartbeatTimer=setInterval(()=>sn(this),this.config.cleanup.heartbeatIntervalMs),this.cleanupTimer=setInterval(()=>rn(this),this.config.cleanup.intervalMs),r(this.address())};this.server.once("error",i),this.server.once("listening",o),this.server.listen(e,s)})}address(){let e=this.server.address();return e?typeof e=="string"?{path:e}:{host:e.address,port:e.port}:null}async close(){this.heartbeatTimer&&clearInterval(this.heartbeatTimer),this.cleanupTimer&&clearInterval(this.cleanupTimer),this.heartbeatTimer=null,this.cleanupTimer=null;for(let e of this.activeDelayTimers)clearTimeout(e);this.activeDelayTimers.clear();for(let e of[...this.clients.values()])C(this,e,{notify:!1});for(let e of[...this.hosts.values()])N(this,e,{notify:!1});this.streams.clear(),this.pairAttempts.clear(),this.clientHandshakeAttempts.clear(),this.hostHandshakeAttempts.clear(),this.pendingHandshakes.clear(),this.metrics.close(),await new Promise(e=>{if(!this.server.listening)return e();this.server.close(()=>e())})}handleUpgrade(e,s,r){let n=(()=>{try{return new Vo(e.url||"","http://localhost").pathname}catch{return""}})();if(![Yt,Tr].includes(n)||!jt(this,e.headers.origin,e)){s.write(`HTTP/1.1 403 Forbidden\r
13
+ Connection: close\r
14
+ \r
15
+ `),s.destroy();return}if(this.pendingHandshakes.size>=this.config.relay.maxPendingHandshakes){s.write(`HTTP/1.1 503 Service Unavailable\r
16
+ Connection: close\r
17
+ \r
18
+ `),s.destroy();return}try{let i=s;i.setNoDelay(!0),i.setKeepAlive?.(!0,this.config.cleanup.heartbeatIntervalMs)}catch{}this.wss.handleUpgrade(e,s,r,i=>{this.pendingHandshakes.add(i),i.once("close",()=>this.finishHandshake(i)),n===Yt?Jr(this,i,e):qr(this,i,e)})}finishHandshake(e){this.pendingHandshakes.delete(e)}};var nn=`herdr-remote-relay ${W} \u2014 standalone relay for Herdr Remote
15
19
 
16
20
  Usage: herdr-remote-relay [options]
17
21
 
@@ -35,82 +39,19 @@ Options:
35
39
  -v, --version Show version
36
40
 
37
41
  Guide: docs/self-hosted-relay.md
38
- `;
39
-
40
- function webUiBuilt() {
41
- return fs.existsSync(path.join(PACKAGE_ROOT, 'web', 'dist', 'index.html'));
42
- }
43
-
44
- async function main(argv = process.argv.slice(2)) {
45
- let loaded;
46
- try {
47
- loaded = loadRelayConfig({ argv });
48
- } catch (error) {
49
- process.stderr.write(`herdr-remote-relay: ${error.message}\n`);
50
- process.exitCode = 1;
51
- return;
52
- }
53
- const { config, help, version, errors, warnings, configFile } = loaded;
54
-
55
- if (help) {
56
- process.stdout.write(USAGE);
57
- return;
58
- }
59
- if (version) {
60
- process.stdout.write(`${VERSION}\n`);
61
- return;
62
- }
63
- if (errors.length > 0) {
64
- for (const message of errors) process.stderr.write(`herdr-remote-relay: ${message}\n`);
65
- process.stderr.write(USAGE);
66
- process.exitCode = 2;
67
- return;
68
- }
69
-
70
- const server = new RelayServer(config);
71
- let address;
72
- try {
73
- address = await server.listen(config.relay.port, config.relay.host);
74
- } catch (error) {
75
- const hint =
76
- error.code === 'EADDRINUSE'
77
- ? ` (port ${config.relay.port} is already in use)`
78
- : error.code === 'EACCES'
79
- ? ` (no permission to bind port ${config.relay.port})`
80
- : '';
81
- process.stderr.write(`herdr-remote-relay: failed to listen${hint}: ${error.message}\n`);
82
- process.exitCode = 1;
83
- return;
84
- }
85
-
86
- process.stdout.write(`herdr-remote-relay ${VERSION} (protocol ${PROTOCOL_VERSION})\n`);
87
- process.stdout.write(` listening http://${config.relay.host}:${address.port}\n`);
88
- process.stdout.write(` public url ${config.relay.publicUrl}\n`);
89
- process.stdout.write(
90
- ` password ${config.auth.password ? 'set' : 'not set (public relay)'}\n`,
91
- );
92
- process.stdout.write(
93
- ` admin ${config.auth.adminToken ? 'set (/admin)' : 'not configured'}\n`,
94
- );
95
- process.stdout.write(` state file ${server.stateFile}\n`);
96
- if (configFile) process.stdout.write(` config file ${configFile}\n`);
97
- if (!webUiBuilt()) {
98
- process.stdout.write(' web ui NOT BUILT (run "npm run build" in this package)\n');
99
- }
100
- for (const warning of warnings) process.stdout.write(` warning: ${warning}\n`);
101
-
102
- let closing = false;
103
- const stop = (signal) => {
104
- if (closing) return;
105
- closing = true;
106
- process.stdout.write(`herdr-remote-relay: ${signal} received, shutting down\n`);
107
- server.close().finally(() => process.exit(0));
108
- };
109
- process.on('SIGINT', () => stop('SIGINT'));
110
- process.on('SIGTERM', () => stop('SIGTERM'));
111
- }
112
-
113
- main().catch((error) => {
114
- process.stderr.write(`herdr-remote-relay: ${error.stack || error.message}\n`);
115
- process.exitCode = 1;
116
- });
42
+ `;function Xo(){return qo.existsSync(zo.join(ae,"web","dist","index.html"))}async function Ko(t=process.argv.slice(2)){let e;try{e=Be({argv:t})}catch(u){process.stderr.write(`herdr-remote-relay: ${u.message}
43
+ `),process.exitCode=1;return}let{config:s,help:r,version:n,errors:i,warnings:o,configFile:a}=e;if(r){process.stdout.write(nn);return}if(n){process.stdout.write(`${W}
44
+ `);return}if(i.length>0){for(let u of i)process.stderr.write(`herdr-remote-relay: ${u}
45
+ `);process.stderr.write(nn),process.exitCode=2;return}let l=new ct(s),c;try{c=await l.listen(s.relay.port,s.relay.host)}catch(u){let h=u.code==="EADDRINUSE"?` (port ${s.relay.port} is already in use)`:u.code==="EACCES"?` (no permission to bind port ${s.relay.port})`:"";process.stderr.write(`herdr-remote-relay: failed to listen${h}: ${u.message}
46
+ `),process.exitCode=1;return}process.stdout.write(`herdr-remote-relay ${W} (protocol ${1})
47
+ `),process.stdout.write(` listening http://${s.relay.host}:${c.port}
48
+ `),process.stdout.write(` public url ${s.relay.publicUrl}
49
+ `),process.stdout.write(` password ${s.auth.password?"set":"not set (public relay)"}
50
+ `),process.stdout.write(` admin ${s.auth.adminToken?"set (/admin)":"not configured"}
51
+ `),process.stdout.write(` state file ${l.stateFile}
52
+ `),a&&process.stdout.write(` config file ${a}
53
+ `),Xo()||process.stdout.write(` web ui NOT BUILT (run "npm run build" in this package)
54
+ `);for(let u of o)process.stdout.write(` warning: ${u}
55
+ `);let f=!1,d=u=>{f||(f=!0,process.stdout.write(`herdr-remote-relay: ${u} received, shutting down
56
+ `),l.close().finally(()=>process.exit(0)))};process.on("SIGINT",()=>d("SIGINT")),process.on("SIGTERM",()=>d("SIGTERM"))}Ko().catch(t=>{process.stderr.write(`herdr-remote-relay: ${t.stack||t.message}
57
+ `),process.exitCode=1});
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "herdr-remote-relay",
3
- "version": "0.3.29",
3
+ "version": "0.3.31",
4
4
  "description": "Standalone relay server and web terminal for Herdr Remote",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -26,51 +26,8 @@
26
26
  "bin": {
27
27
  "herdr-remote-relay": "./bin/herdr-remote-relay.js"
28
28
  },
29
- "main": "./dist/relay-server.js",
30
- "types": "./dist/relay-server.d.ts",
31
- "exports": {
32
- ".": {
33
- "types": "./dist/relay-server.d.ts",
34
- "default": "./dist/relay-server.js"
35
- },
36
- "./protocol": {
37
- "types": "./dist/protocol/index.d.ts",
38
- "default": "./dist/protocol/index.js"
39
- },
40
- "./config": {
41
- "types": "./dist/relay-config.d.ts",
42
- "default": "./dist/relay-config.js"
43
- },
44
- "./state": {
45
- "types": "./dist/state.d.ts",
46
- "default": "./dist/state.js"
47
- },
48
- "./package.json": "./package.json"
49
- },
50
29
  "files": [
51
30
  "bin",
52
- "dist",
53
- "web/dist",
54
- "deploy",
55
- "config.example.json",
56
- "README.zh-CN.md"
57
- ],
58
- "scripts": {
59
- "build": "npm run build:server && npm --prefix web run build",
60
- "build:server": "node -e \"fs.rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.json",
61
- "prepack": "npm run build",
62
- "start": "node bin/herdr-remote-relay.js",
63
- "pretest": "npm run build:server",
64
- "test": "vitest run",
65
- "typecheck": "tsc -p tsconfig.json --noEmit"
66
- },
67
- "dependencies": {
68
- "ws": "^8.18.0"
69
- },
70
- "devDependencies": {
71
- "@types/node": "^22.20.4",
72
- "@types/ws": "^8.18.1",
73
- "typescript": "~5.7.3",
74
- "vitest": "^3.2.7"
75
- }
31
+ "web/dist"
32
+ ]
76
33
  }