@johnhenry/wsh 0.0.0

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.
@@ -0,0 +1,928 @@
1
+ /**
2
+ * wsh protocol control message types and constructors.
3
+ * AUTO-GENERATED from wsh-v1.yaml — do not edit.
4
+ * Run: node web/packages/wsh/spec/codegen.mjs
5
+ */
6
+
7
+ // ── Message type constants ────────────────────────────────────────────
8
+
9
+ export const MSG = Object.freeze({
10
+ // Handshake
11
+ HELLO: 0x01,
12
+ SERVER_HELLO: 0x02,
13
+ CHALLENGE: 0x03,
14
+ AUTH_METHODS: 0x04,
15
+ AUTH: 0x05,
16
+ AUTH_OK: 0x06,
17
+ AUTH_FAIL: 0x07,
18
+
19
+ // Channel
20
+ OPEN: 0x10,
21
+ OPEN_OK: 0x11,
22
+ OPEN_FAIL: 0x12,
23
+ RESIZE: 0x13,
24
+ SIGNAL: 0x14,
25
+ EXIT: 0x15,
26
+ CLOSE: 0x16,
27
+ SESSION_DATA: 0x17,
28
+
29
+ // Transport
30
+ ERROR: 0x20,
31
+ PING: 0x21,
32
+ PONG: 0x22,
33
+
34
+ // Session
35
+ ATTACH: 0x30,
36
+ RESUME: 0x31,
37
+ RENAME: 0x32,
38
+ IDLE_WARNING: 0x33,
39
+ SHUTDOWN: 0x34,
40
+ SNAPSHOT: 0x35,
41
+ PRESENCE: 0x36,
42
+ CONTROL_CHANGED: 0x37,
43
+ METRICS: 0x38,
44
+ CLIPBOARD: 0x39,
45
+ RECORDING_EXPORT: 0x3a,
46
+ COMMAND_JOURNAL: 0x3b,
47
+ METRICS_REQUEST: 0x3c,
48
+ SUSPEND_SESSION: 0x3d,
49
+ RESTART_PTY: 0x3e,
50
+ SESSION_LIST_REQUEST: 0x3f,
51
+
52
+ // Mcp
53
+ MCP_DISCOVER: 0x40,
54
+ MCP_TOOLS: 0x41,
55
+ MCP_CALL: 0x42,
56
+ MCP_RESULT: 0x43,
57
+
58
+ // Reverse
59
+ REVERSE_REGISTER: 0x50,
60
+ REVERSE_LIST: 0x51,
61
+ REVERSE_PEERS: 0x52,
62
+ REVERSE_CONNECT: 0x53,
63
+ REVERSE_ACCEPT: 0x54,
64
+ REVERSE_REJECT: 0x55,
65
+
66
+ // Session
67
+ SESSION_LIST: 0x5f,
68
+ DETACH: 0x60,
69
+
70
+ // Framing
71
+ WS_DATA: 0x60,
72
+
73
+ // Session
74
+ DETACH_OK: 0x61,
75
+ DETACH_FAIL: 0x62,
76
+
77
+ // Gateway
78
+ OPEN_TCP: 0x70,
79
+ OPEN_UDP: 0x71,
80
+ RESOLVE_DNS: 0x72,
81
+ GATEWAY_OK: 0x73,
82
+ GATEWAY_FAIL: 0x74,
83
+ GATEWAY_CLOSE: 0x75,
84
+ INBOUND_OPEN: 0x76,
85
+ INBOUND_ACCEPT: 0x77,
86
+ INBOUND_REJECT: 0x78,
87
+ DNS_RESULT: 0x79,
88
+ LISTEN_REQUEST: 0x7a,
89
+ LISTEN_OK: 0x7b,
90
+ LISTEN_FAIL: 0x7c,
91
+ LISTEN_CLOSE: 0x7d,
92
+ GATEWAY_DATA: 0x7e,
93
+
94
+ // Guest
95
+ GUEST_INVITE: 0x80,
96
+ GUEST_JOIN: 0x81,
97
+ GUEST_REVOKE: 0x82,
98
+
99
+ // Sharing
100
+ SHARE_SESSION: 0x83,
101
+ SHARE_REVOKE: 0x84,
102
+
103
+ // Compression
104
+ COMPRESS_BEGIN: 0x85,
105
+ COMPRESS_ACK: 0x86,
106
+
107
+ // Ratecontrol
108
+ RATE_CONTROL: 0x87,
109
+ RATE_WARNING: 0x88,
110
+
111
+ // Linking
112
+ SESSION_LINK: 0x89,
113
+ SESSION_UNLINK: 0x8a,
114
+
115
+ // Copilot
116
+ COPILOT_ATTACH: 0x8b,
117
+ COPILOT_SUGGEST: 0x8c,
118
+ COPILOT_DETACH: 0x8d,
119
+
120
+ // E2e
121
+ KEY_EXCHANGE: 0x8e,
122
+ ENCRYPTED_FRAME: 0x8f,
123
+
124
+ // Echo
125
+ ECHO_ACK: 0x90,
126
+ ECHO_STATE: 0x91,
127
+
128
+ // Termsync
129
+ TERM_SYNC: 0x92,
130
+ TERM_DIFF: 0x93,
131
+
132
+ // Scaling
133
+ NODE_ANNOUNCE: 0x94,
134
+ NODE_REDIRECT: 0x95,
135
+
136
+ // Principals
137
+ SESSION_GRANT: 0x96,
138
+ SESSION_REVOKE: 0x97,
139
+
140
+ // Filechannel
141
+ FILE_OP: 0x98,
142
+ FILE_RESULT: 0x99,
143
+ FILE_CHUNK: 0x9a,
144
+
145
+ // Policy
146
+ POLICY_EVAL: 0x9b,
147
+ POLICY_RESULT: 0x9c,
148
+ POLICY_UPDATE: 0x9d,
149
+
150
+ // Terminal
151
+ TERMINAL_CONFIG: 0x9e,
152
+ });
153
+
154
+ // Reverse lookup: number → name
155
+ export const MSG_NAMES = Object.freeze(
156
+ Object.fromEntries(Object.entries(MSG).map(([k, v]) => [v, k]))
157
+ );
158
+
159
+ // ── Channel kinds ─────────────────────────────────────────────────────
160
+
161
+ export const CHANNEL_KIND = Object.freeze({
162
+ PTY: 'pty',
163
+ EXEC: 'exec',
164
+ META: 'meta',
165
+ FILE: 'file',
166
+ TCP: 'tcp',
167
+ UDP: 'udp',
168
+ JOB: 'job',
169
+ });
170
+
171
+ // ── Auth methods ──────────────────────────────────────────────────────
172
+
173
+ export const AUTH_METHOD = Object.freeze({
174
+ PUBKEY: 'pubkey',
175
+ PASSWORD: 'password',
176
+ });
177
+
178
+ // ── Protocol version ──────────────────────────────────────────────────
179
+
180
+ export const PROTOCOL_VERSION = 'wsh-v1';
181
+
182
+ // ── Message constructors ──────────────────────────────────────────────
183
+
184
+ export function hello({ username, features = [], authMethod = AUTH_METHOD.PUBKEY } = {}) {
185
+ return {
186
+ type: MSG.HELLO,
187
+ version: PROTOCOL_VERSION,
188
+ username,
189
+ features,
190
+ auth_method: authMethod,
191
+ };
192
+ }
193
+
194
+ export function serverHello({ sessionId, features = [], fingerprints = [] } = {}) {
195
+ return {
196
+ type: MSG.SERVER_HELLO,
197
+ session_id: sessionId,
198
+ features,
199
+ fingerprints,
200
+ };
201
+ }
202
+
203
+ export function challenge({ nonce } = {}) {
204
+ return {
205
+ type: MSG.CHALLENGE,
206
+ nonce,
207
+ };
208
+ }
209
+
210
+ export function authMethods({ methods = [AUTH_METHOD.PUBKEY] } = {}) {
211
+ return {
212
+ type: MSG.AUTH_METHODS,
213
+ methods,
214
+ };
215
+ }
216
+
217
+ export function auth({ method, signature, publicKey, password } = {}) {
218
+ const msg = { type: MSG.AUTH, method };
219
+ if (method === AUTH_METHOD.PUBKEY) {
220
+ msg.signature = signature;
221
+ msg.public_key = publicKey;
222
+ } else if (method === AUTH_METHOD.PASSWORD) {
223
+ msg.password = password;
224
+ }
225
+ return msg;
226
+ }
227
+
228
+ export function authOk({ sessionId, token, ttl } = {}) {
229
+ return {
230
+ type: MSG.AUTH_OK,
231
+ session_id: sessionId,
232
+ token,
233
+ ttl,
234
+ };
235
+ }
236
+
237
+ export function authFail({ reason } = {}) {
238
+ return {
239
+ type: MSG.AUTH_FAIL,
240
+ reason,
241
+ };
242
+ }
243
+
244
+ export function open({ kind, command, cols, rows, env } = {}) {
245
+ const msg = { type: MSG.OPEN, kind };
246
+ if (command !== undefined) msg.command = command;
247
+ if (cols !== undefined) msg.cols = cols;
248
+ if (rows !== undefined) msg.rows = rows;
249
+ if (env !== undefined) msg.env = env;
250
+ return msg;
251
+ }
252
+
253
+ export function openOk({ channelId, streamIds = [], dataMode = "stream", capabilities = [] } = {}) {
254
+ return {
255
+ type: MSG.OPEN_OK,
256
+ channel_id: channelId,
257
+ stream_ids: streamIds,
258
+ data_mode: dataMode,
259
+ capabilities,
260
+ };
261
+ }
262
+
263
+ export function openFail({ reason } = {}) {
264
+ return {
265
+ type: MSG.OPEN_FAIL,
266
+ reason,
267
+ };
268
+ }
269
+
270
+ export function resize({ channelId, cols, rows } = {}) {
271
+ return {
272
+ type: MSG.RESIZE,
273
+ channel_id: channelId,
274
+ cols,
275
+ rows,
276
+ };
277
+ }
278
+
279
+ export function signal({ channelId, signal } = {}) {
280
+ return {
281
+ type: MSG.SIGNAL,
282
+ channel_id: channelId,
283
+ signal,
284
+ };
285
+ }
286
+
287
+ export function exit({ channelId, code } = {}) {
288
+ return {
289
+ type: MSG.EXIT,
290
+ channel_id: channelId,
291
+ code,
292
+ };
293
+ }
294
+
295
+ export function close({ channelId } = {}) {
296
+ return {
297
+ type: MSG.CLOSE,
298
+ channel_id: channelId,
299
+ };
300
+ }
301
+
302
+ export function sessionData({ channelId, data } = {}) {
303
+ return {
304
+ type: MSG.SESSION_DATA,
305
+ channel_id: channelId,
306
+ data,
307
+ };
308
+ }
309
+
310
+ export function error({ code, message } = {}) {
311
+ return {
312
+ type: MSG.ERROR,
313
+ code,
314
+ message,
315
+ };
316
+ }
317
+
318
+ export function ping({ id } = {}) {
319
+ return {
320
+ type: MSG.PING,
321
+ id,
322
+ };
323
+ }
324
+
325
+ export function pong({ id } = {}) {
326
+ return {
327
+ type: MSG.PONG,
328
+ id,
329
+ };
330
+ }
331
+
332
+ export function attach({ sessionId, token, mode = "control", deviceLabel } = {}) {
333
+ const msg = { type: MSG.ATTACH, session_id: sessionId, token, mode };
334
+ if (deviceLabel !== undefined) msg.device_label = deviceLabel;
335
+ return msg;
336
+ }
337
+
338
+ export function resume({ sessionId, token, lastSeq } = {}) {
339
+ return {
340
+ type: MSG.RESUME,
341
+ session_id: sessionId,
342
+ token,
343
+ last_seq: lastSeq,
344
+ };
345
+ }
346
+
347
+ export function rename({ sessionId, name } = {}) {
348
+ return {
349
+ type: MSG.RENAME,
350
+ session_id: sessionId,
351
+ name,
352
+ };
353
+ }
354
+
355
+ export function idleWarning({ expiresIn } = {}) {
356
+ return {
357
+ type: MSG.IDLE_WARNING,
358
+ expires_in: expiresIn,
359
+ };
360
+ }
361
+
362
+ export function shutdown({ reason, retryAfter } = {}) {
363
+ const msg = { type: MSG.SHUTDOWN, reason };
364
+ if (retryAfter !== undefined) msg.retry_after = retryAfter;
365
+ return msg;
366
+ }
367
+
368
+ export function snapshot({ label } = {}) {
369
+ return {
370
+ type: MSG.SNAPSHOT,
371
+ label,
372
+ };
373
+ }
374
+
375
+ export function presence({ attachments } = {}) {
376
+ return {
377
+ type: MSG.PRESENCE,
378
+ attachments,
379
+ };
380
+ }
381
+
382
+ export function controlChanged({ newController } = {}) {
383
+ return {
384
+ type: MSG.CONTROL_CHANGED,
385
+ new_controller: newController,
386
+ };
387
+ }
388
+
389
+ export function metrics({ cpu, memory, sessions, rtt } = {}) {
390
+ const msg = { type: MSG.METRICS, };
391
+ if (cpu !== undefined) msg.cpu = cpu;
392
+ if (memory !== undefined) msg.memory = memory;
393
+ if (sessions !== undefined) msg.sessions = sessions;
394
+ if (rtt !== undefined) msg.rtt = rtt;
395
+ return msg;
396
+ }
397
+
398
+ export function clipboard({ direction, data } = {}) {
399
+ return {
400
+ type: MSG.CLIPBOARD,
401
+ direction,
402
+ data,
403
+ };
404
+ }
405
+
406
+ export function recordingExport({ sessionId, format = "jsonl", data } = {}) {
407
+ const msg = { type: MSG.RECORDING_EXPORT, session_id: sessionId, format };
408
+ if (data !== undefined) msg.data = data;
409
+ return msg;
410
+ }
411
+
412
+ export function commandJournal({ sessionId, command, exitCode, durationMs, cwd, timestamp } = {}) {
413
+ const msg = { type: MSG.COMMAND_JOURNAL, session_id: sessionId, command, timestamp };
414
+ if (exitCode !== undefined) msg.exit_code = exitCode;
415
+ if (durationMs !== undefined) msg.duration_ms = durationMs;
416
+ if (cwd !== undefined) msg.cwd = cwd;
417
+ return msg;
418
+ }
419
+
420
+ export function metricsRequest() {
421
+ return { type: MSG.METRICS_REQUEST };
422
+ }
423
+
424
+ export function suspendSession({ sessionId, action } = {}) {
425
+ return {
426
+ type: MSG.SUSPEND_SESSION,
427
+ session_id: sessionId,
428
+ action,
429
+ };
430
+ }
431
+
432
+ export function restartPty({ sessionId, command } = {}) {
433
+ const msg = { type: MSG.RESTART_PTY, session_id: sessionId };
434
+ if (command !== undefined) msg.command = command;
435
+ return msg;
436
+ }
437
+
438
+ export function sessionListRequest() {
439
+ return { type: MSG.SESSION_LIST_REQUEST };
440
+ }
441
+
442
+ export function mcpDiscover() {
443
+ return { type: MSG.MCP_DISCOVER };
444
+ }
445
+
446
+ export function mcpTools({ tools } = {}) {
447
+ return {
448
+ type: MSG.MCP_TOOLS,
449
+ tools,
450
+ };
451
+ }
452
+
453
+ export function mcpCall({ tool, arguments: args } = {}) {
454
+ return { type: MSG.MCP_CALL, tool, arguments: args };
455
+ }
456
+
457
+ export function mcpResult({ result } = {}) {
458
+ return {
459
+ type: MSG.MCP_RESULT,
460
+ result,
461
+ };
462
+ }
463
+
464
+ export function reverseRegister({ username, capabilities = [], peerType = "host", shellBackend = "pty", supportsAttach = false, supportsReplay = false, supportsEcho = false, supportsTermSync = false, publicKey } = {}) {
465
+ return {
466
+ type: MSG.REVERSE_REGISTER,
467
+ username,
468
+ capabilities,
469
+ peer_type: peerType,
470
+ shell_backend: shellBackend,
471
+ supports_attach: supportsAttach,
472
+ supports_replay: supportsReplay,
473
+ supports_echo: supportsEcho,
474
+ supports_term_sync: supportsTermSync,
475
+ public_key: publicKey,
476
+ };
477
+ }
478
+
479
+ export function reverseList() {
480
+ return { type: MSG.REVERSE_LIST };
481
+ }
482
+
483
+ export function reversePeers({ peers } = {}) {
484
+ return {
485
+ type: MSG.REVERSE_PEERS,
486
+ peers,
487
+ };
488
+ }
489
+
490
+ export function reverseConnect({ targetFingerprint, username } = {}) {
491
+ return {
492
+ type: MSG.REVERSE_CONNECT,
493
+ target_fingerprint: targetFingerprint,
494
+ username,
495
+ };
496
+ }
497
+
498
+ export function reverseAccept({ targetFingerprint, username, capabilities = [], peerType = "host", shellBackend = "pty", supportsAttach = false, supportsReplay = false, supportsEcho = false, supportsTermSync = false } = {}) {
499
+ return {
500
+ type: MSG.REVERSE_ACCEPT,
501
+ target_fingerprint: targetFingerprint,
502
+ username,
503
+ capabilities,
504
+ peer_type: peerType,
505
+ shell_backend: shellBackend,
506
+ supports_attach: supportsAttach,
507
+ supports_replay: supportsReplay,
508
+ supports_echo: supportsEcho,
509
+ supports_term_sync: supportsTermSync,
510
+ };
511
+ }
512
+
513
+ export function reverseReject({ targetFingerprint, username, reason } = {}) {
514
+ return {
515
+ type: MSG.REVERSE_REJECT,
516
+ target_fingerprint: targetFingerprint,
517
+ username,
518
+ reason,
519
+ };
520
+ }
521
+
522
+ export function sessionList({ sessions } = {}) {
523
+ return {
524
+ type: MSG.SESSION_LIST,
525
+ sessions,
526
+ };
527
+ }
528
+
529
+ export function detach({ sessionId } = {}) {
530
+ return {
531
+ type: MSG.DETACH,
532
+ session_id: sessionId,
533
+ };
534
+ }
535
+
536
+ export function detachOk({ sessionId } = {}) {
537
+ return {
538
+ type: MSG.DETACH_OK,
539
+ session_id: sessionId,
540
+ };
541
+ }
542
+
543
+ export function detachFail({ reason } = {}) {
544
+ return {
545
+ type: MSG.DETACH_FAIL,
546
+ reason,
547
+ };
548
+ }
549
+
550
+ export function openTcp({ gatewayId, host, port } = {}) {
551
+ return {
552
+ type: MSG.OPEN_TCP,
553
+ gateway_id: gatewayId,
554
+ host,
555
+ port,
556
+ };
557
+ }
558
+
559
+ export function openUdp({ gatewayId, host, port } = {}) {
560
+ return {
561
+ type: MSG.OPEN_UDP,
562
+ gateway_id: gatewayId,
563
+ host,
564
+ port,
565
+ };
566
+ }
567
+
568
+ export function resolveDns({ gatewayId, name, recordType = "A" } = {}) {
569
+ return {
570
+ type: MSG.RESOLVE_DNS,
571
+ gateway_id: gatewayId,
572
+ name,
573
+ record_type: recordType,
574
+ };
575
+ }
576
+
577
+ export function gatewayOk({ gatewayId, resolvedAddr } = {}) {
578
+ const msg = { type: MSG.GATEWAY_OK, gateway_id: gatewayId };
579
+ if (resolvedAddr !== undefined) msg.resolved_addr = resolvedAddr;
580
+ return msg;
581
+ }
582
+
583
+ export function gatewayFail({ gatewayId, code, message } = {}) {
584
+ return {
585
+ type: MSG.GATEWAY_FAIL,
586
+ gateway_id: gatewayId,
587
+ code,
588
+ message,
589
+ };
590
+ }
591
+
592
+ export function gatewayClose({ gatewayId, reason } = {}) {
593
+ const msg = { type: MSG.GATEWAY_CLOSE, gateway_id: gatewayId };
594
+ if (reason !== undefined) msg.reason = reason;
595
+ return msg;
596
+ }
597
+
598
+ export function inboundOpen({ listenerId, channelId, peerAddr, peerPort } = {}) {
599
+ return {
600
+ type: MSG.INBOUND_OPEN,
601
+ listener_id: listenerId,
602
+ channel_id: channelId,
603
+ peer_addr: peerAddr,
604
+ peer_port: peerPort,
605
+ };
606
+ }
607
+
608
+ export function inboundAccept({ channelId, gatewayId } = {}) {
609
+ const msg = { type: MSG.INBOUND_ACCEPT, channel_id: channelId };
610
+ if (gatewayId !== undefined) msg.gateway_id = gatewayId;
611
+ return msg;
612
+ }
613
+
614
+ export function inboundReject({ channelId, reason } = {}) {
615
+ const msg = { type: MSG.INBOUND_REJECT, channel_id: channelId };
616
+ if (reason !== undefined) msg.reason = reason;
617
+ return msg;
618
+ }
619
+
620
+ export function dnsResult({ gatewayId, addresses, ttl } = {}) {
621
+ const msg = { type: MSG.DNS_RESULT, gateway_id: gatewayId, addresses };
622
+ if (ttl !== undefined) msg.ttl = ttl;
623
+ return msg;
624
+ }
625
+
626
+ export function listenRequest({ listenerId, port, bindAddr = "0.0.0.0" } = {}) {
627
+ return {
628
+ type: MSG.LISTEN_REQUEST,
629
+ listener_id: listenerId,
630
+ port,
631
+ bind_addr: bindAddr,
632
+ };
633
+ }
634
+
635
+ export function listenOk({ listenerId, actualPort } = {}) {
636
+ return {
637
+ type: MSG.LISTEN_OK,
638
+ listener_id: listenerId,
639
+ actual_port: actualPort,
640
+ };
641
+ }
642
+
643
+ export function listenFail({ listenerId, reason } = {}) {
644
+ return {
645
+ type: MSG.LISTEN_FAIL,
646
+ listener_id: listenerId,
647
+ reason,
648
+ };
649
+ }
650
+
651
+ export function listenClose({ listenerId } = {}) {
652
+ return {
653
+ type: MSG.LISTEN_CLOSE,
654
+ listener_id: listenerId,
655
+ };
656
+ }
657
+
658
+ export function gatewayData({ gatewayId, data } = {}) {
659
+ return {
660
+ type: MSG.GATEWAY_DATA,
661
+ gateway_id: gatewayId,
662
+ data,
663
+ };
664
+ }
665
+
666
+ export function guestInvite({ sessionId, ttl, permissions = ["read"] } = {}) {
667
+ return {
668
+ type: MSG.GUEST_INVITE,
669
+ session_id: sessionId,
670
+ ttl,
671
+ permissions,
672
+ };
673
+ }
674
+
675
+ export function guestJoin({ token, deviceLabel } = {}) {
676
+ const msg = { type: MSG.GUEST_JOIN, token };
677
+ if (deviceLabel !== undefined) msg.device_label = deviceLabel;
678
+ return msg;
679
+ }
680
+
681
+ export function guestRevoke({ token, reason } = {}) {
682
+ const msg = { type: MSG.GUEST_REVOKE, token };
683
+ if (reason !== undefined) msg.reason = reason;
684
+ return msg;
685
+ }
686
+
687
+ export function shareSession({ sessionId, mode = "read", ttl } = {}) {
688
+ return {
689
+ type: MSG.SHARE_SESSION,
690
+ session_id: sessionId,
691
+ mode,
692
+ ttl,
693
+ };
694
+ }
695
+
696
+ export function shareRevoke({ shareId, reason } = {}) {
697
+ const msg = { type: MSG.SHARE_REVOKE, share_id: shareId };
698
+ if (reason !== undefined) msg.reason = reason;
699
+ return msg;
700
+ }
701
+
702
+ export function compressBegin({ algorithm, level = 3 } = {}) {
703
+ return {
704
+ type: MSG.COMPRESS_BEGIN,
705
+ algorithm,
706
+ level,
707
+ };
708
+ }
709
+
710
+ export function compressAck({ algorithm, accepted } = {}) {
711
+ return {
712
+ type: MSG.COMPRESS_ACK,
713
+ algorithm,
714
+ accepted,
715
+ };
716
+ }
717
+
718
+ export function rateControl({ sessionId, maxBytesPerSec, policy = "pause" } = {}) {
719
+ return {
720
+ type: MSG.RATE_CONTROL,
721
+ session_id: sessionId,
722
+ max_bytes_per_sec: maxBytesPerSec,
723
+ policy,
724
+ };
725
+ }
726
+
727
+ export function rateWarning({ sessionId, queuedBytes, action } = {}) {
728
+ return {
729
+ type: MSG.RATE_WARNING,
730
+ session_id: sessionId,
731
+ queued_bytes: queuedBytes,
732
+ action,
733
+ };
734
+ }
735
+
736
+ export function sessionLink({ sourceSession, targetHost, targetPort, targetUser } = {}) {
737
+ const msg = { type: MSG.SESSION_LINK, source_session: sourceSession, target_host: targetHost, target_port: targetPort };
738
+ if (targetUser !== undefined) msg.target_user = targetUser;
739
+ return msg;
740
+ }
741
+
742
+ export function sessionUnlink({ linkId, reason } = {}) {
743
+ const msg = { type: MSG.SESSION_UNLINK, link_id: linkId };
744
+ if (reason !== undefined) msg.reason = reason;
745
+ return msg;
746
+ }
747
+
748
+ export function copilotAttach({ sessionId, model, contextWindow } = {}) {
749
+ const msg = { type: MSG.COPILOT_ATTACH, session_id: sessionId, model };
750
+ if (contextWindow !== undefined) msg.context_window = contextWindow;
751
+ return msg;
752
+ }
753
+
754
+ export function copilotSuggest({ sessionId, suggestion, confidence } = {}) {
755
+ const msg = { type: MSG.COPILOT_SUGGEST, session_id: sessionId, suggestion };
756
+ if (confidence !== undefined) msg.confidence = confidence;
757
+ return msg;
758
+ }
759
+
760
+ export function copilotDetach({ sessionId, reason } = {}) {
761
+ const msg = { type: MSG.COPILOT_DETACH, session_id: sessionId };
762
+ if (reason !== undefined) msg.reason = reason;
763
+ return msg;
764
+ }
765
+
766
+ export function keyExchange({ algorithm, publicKey, sessionId } = {}) {
767
+ return {
768
+ type: MSG.KEY_EXCHANGE,
769
+ algorithm,
770
+ public_key: publicKey,
771
+ session_id: sessionId,
772
+ };
773
+ }
774
+
775
+ export function encryptedFrame({ nonce, ciphertext, sessionId } = {}) {
776
+ return {
777
+ type: MSG.ENCRYPTED_FRAME,
778
+ nonce,
779
+ ciphertext,
780
+ session_id: sessionId,
781
+ };
782
+ }
783
+
784
+ export function echoAck({ channelId, echoSeq } = {}) {
785
+ return {
786
+ type: MSG.ECHO_ACK,
787
+ channel_id: channelId,
788
+ echo_seq: echoSeq,
789
+ };
790
+ }
791
+
792
+ export function echoState({ channelId, echoSeq, cursorX, cursorY, pending } = {}) {
793
+ return {
794
+ type: MSG.ECHO_STATE,
795
+ channel_id: channelId,
796
+ echo_seq: echoSeq,
797
+ cursor_x: cursorX,
798
+ cursor_y: cursorY,
799
+ pending,
800
+ };
801
+ }
802
+
803
+ export function termSync({ channelId, frameSeq, stateHash } = {}) {
804
+ return {
805
+ type: MSG.TERM_SYNC,
806
+ channel_id: channelId,
807
+ frame_seq: frameSeq,
808
+ state_hash: stateHash,
809
+ };
810
+ }
811
+
812
+ export function termDiff({ channelId, frameSeq, baseSeq, patch } = {}) {
813
+ return {
814
+ type: MSG.TERM_DIFF,
815
+ channel_id: channelId,
816
+ frame_seq: frameSeq,
817
+ base_seq: baseSeq,
818
+ patch,
819
+ };
820
+ }
821
+
822
+ export function nodeAnnounce({ nodeId, endpoint, load, capacity } = {}) {
823
+ return {
824
+ type: MSG.NODE_ANNOUNCE,
825
+ node_id: nodeId,
826
+ endpoint,
827
+ load,
828
+ capacity,
829
+ };
830
+ }
831
+
832
+ export function nodeRedirect({ targetNode, targetEndpoint, sessionId, reason } = {}) {
833
+ const msg = { type: MSG.NODE_REDIRECT, target_node: targetNode, target_endpoint: targetEndpoint, session_id: sessionId };
834
+ if (reason !== undefined) msg.reason = reason;
835
+ return msg;
836
+ }
837
+
838
+ export function sessionGrant({ sessionId, principal, permissions = ["read"] } = {}) {
839
+ return {
840
+ type: MSG.SESSION_GRANT,
841
+ session_id: sessionId,
842
+ principal,
843
+ permissions,
844
+ };
845
+ }
846
+
847
+ export function sessionRevoke({ sessionId, principal, reason } = {}) {
848
+ const msg = { type: MSG.SESSION_REVOKE, session_id: sessionId, principal };
849
+ if (reason !== undefined) msg.reason = reason;
850
+ return msg;
851
+ }
852
+
853
+ export function fileOp({ channelId, op, path, offset, length } = {}) {
854
+ const msg = { type: MSG.FILE_OP, channel_id: channelId, op, path };
855
+ if (offset !== undefined) msg.offset = offset;
856
+ if (length !== undefined) msg.length = length;
857
+ return msg;
858
+ }
859
+
860
+ export function fileResult({ channelId, success, metadata = {}, errorMessage } = {}) {
861
+ const msg = { type: MSG.FILE_RESULT, channel_id: channelId, success, metadata };
862
+ if (errorMessage !== undefined) msg.error_message = errorMessage;
863
+ return msg;
864
+ }
865
+
866
+ export function fileChunk({ channelId, offset, data, isFinal } = {}) {
867
+ return {
868
+ type: MSG.FILE_CHUNK,
869
+ channel_id: channelId,
870
+ offset,
871
+ data,
872
+ is_final: isFinal,
873
+ };
874
+ }
875
+
876
+ export function policyEval({ requestId, action, principal, context = {} } = {}) {
877
+ return {
878
+ type: MSG.POLICY_EVAL,
879
+ request_id: requestId,
880
+ action,
881
+ principal,
882
+ context,
883
+ };
884
+ }
885
+
886
+ export function policyResult({ requestId, allowed, reason } = {}) {
887
+ const msg = { type: MSG.POLICY_RESULT, request_id: requestId, allowed };
888
+ if (reason !== undefined) msg.reason = reason;
889
+ return msg;
890
+ }
891
+
892
+ export function policyUpdate({ policyId, rules, version } = {}) {
893
+ return {
894
+ type: MSG.POLICY_UPDATE,
895
+ policy_id: policyId,
896
+ rules,
897
+ version,
898
+ };
899
+ }
900
+
901
+ export function terminalConfig({ channelId, frontend, options = {} } = {}) {
902
+ return {
903
+ type: MSG.TERMINAL_CONFIG,
904
+ channel_id: channelId,
905
+ frontend,
906
+ options,
907
+ };
908
+ }
909
+
910
+ // ── Utility ───────────────────────────────────────────────────────────
911
+
912
+ /**
913
+ * Get the human-readable name for a message type number.
914
+ * @param {number} typeNum
915
+ * @returns {string}
916
+ */
917
+ export function msgName(typeNum) {
918
+ return MSG_NAMES[typeNum] || `UNKNOWN(0x${typeNum.toString(16)})`;
919
+ }
920
+
921
+ /**
922
+ * Validate that a message has a recognized type field.
923
+ * @param {object} msg
924
+ * @returns {boolean}
925
+ */
926
+ export function isValidMessage(msg) {
927
+ return msg != null && typeof msg === 'object' && typeof msg.type === 'number' && msg.type in MSG_NAMES;
928
+ }