@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.
package/src/index.d.ts ADDED
@@ -0,0 +1,1454 @@
1
+ /**
2
+ * wsh -- Web Shell client library
3
+ *
4
+ * Browser-native remote command execution over WebTransport/WebSocket
5
+ * with Ed25519 authentication.
6
+ */
7
+
8
+ // ============================================================================
9
+ // cbor.mjs -- CBOR codec + length-prefixed framing
10
+ // ============================================================================
11
+
12
+ /**
13
+ * Encode a JS value into CBOR bytes.
14
+ * Supports: maps, arrays, strings, integers, bytes (Uint8Array),
15
+ * booleans, null, and floats.
16
+ */
17
+ export function cborEncode(value: unknown): Uint8Array;
18
+
19
+ /**
20
+ * Decode CBOR bytes into a JS value.
21
+ */
22
+ export function cborDecode(data: Uint8Array): unknown;
23
+
24
+ /**
25
+ * Frame a CBOR-encoded message with a 4-byte big-endian length prefix.
26
+ * @returns [4-byte length][CBOR payload]
27
+ */
28
+ export function frameEncode(value: unknown): Uint8Array;
29
+
30
+ /**
31
+ * Streaming frame decoder. Feed it chunks and it yields complete messages.
32
+ */
33
+ export class FrameDecoder {
34
+ /**
35
+ * Feed bytes and return decoded messages.
36
+ */
37
+ feed(chunk: Uint8Array): unknown[];
38
+
39
+ /** Reset internal buffer. */
40
+ reset(): void;
41
+
42
+ /** Bytes remaining in buffer. */
43
+ readonly pending: number;
44
+ }
45
+
46
+ // ============================================================================
47
+ // messages.gen.mjs / messages.mjs -- Protocol messages
48
+ // ============================================================================
49
+
50
+ /** Wire message type constants (frozen object of hex opcodes). */
51
+ export const MSG: {
52
+ // Handshake
53
+ readonly HELLO: 0x01;
54
+ readonly SERVER_HELLO: 0x02;
55
+ readonly CHALLENGE: 0x03;
56
+ readonly AUTH_METHODS: 0x04;
57
+ readonly AUTH: 0x05;
58
+ readonly AUTH_OK: 0x06;
59
+ readonly AUTH_FAIL: 0x07;
60
+
61
+ // Channel
62
+ readonly OPEN: 0x10;
63
+ readonly OPEN_OK: 0x11;
64
+ readonly OPEN_FAIL: 0x12;
65
+ readonly RESIZE: 0x13;
66
+ readonly SIGNAL: 0x14;
67
+ readonly EXIT: 0x15;
68
+ readonly CLOSE: 0x16;
69
+ readonly SESSION_DATA: 0x17;
70
+
71
+ // Transport
72
+ readonly ERROR: 0x20;
73
+ readonly PING: 0x21;
74
+ readonly PONG: 0x22;
75
+
76
+ // Session
77
+ readonly ATTACH: 0x30;
78
+ readonly RESUME: 0x31;
79
+ readonly RENAME: 0x32;
80
+ readonly IDLE_WARNING: 0x33;
81
+ readonly SHUTDOWN: 0x34;
82
+ readonly SNAPSHOT: 0x35;
83
+ readonly PRESENCE: 0x36;
84
+ readonly CONTROL_CHANGED: 0x37;
85
+ readonly METRICS: 0x38;
86
+ readonly CLIPBOARD: 0x39;
87
+ readonly RECORDING_EXPORT: 0x3a;
88
+ readonly COMMAND_JOURNAL: 0x3b;
89
+ readonly METRICS_REQUEST: 0x3c;
90
+ readonly SUSPEND_SESSION: 0x3d;
91
+ readonly RESTART_PTY: 0x3e;
92
+
93
+ // Mcp
94
+ readonly MCP_DISCOVER: 0x40;
95
+ readonly MCP_TOOLS: 0x41;
96
+ readonly MCP_CALL: 0x42;
97
+ readonly MCP_RESULT: 0x43;
98
+
99
+ // Reverse
100
+ readonly REVERSE_REGISTER: 0x50;
101
+ readonly REVERSE_LIST: 0x51;
102
+ readonly REVERSE_PEERS: 0x52;
103
+ readonly REVERSE_CONNECT: 0x53;
104
+ readonly REVERSE_ACCEPT: 0x54;
105
+ readonly REVERSE_REJECT: 0x55;
106
+
107
+ // Framing
108
+ readonly WS_DATA: 0x60;
109
+
110
+ // Gateway
111
+ readonly OPEN_TCP: 0x70;
112
+ readonly OPEN_UDP: 0x71;
113
+ readonly RESOLVE_DNS: 0x72;
114
+ readonly GATEWAY_OK: 0x73;
115
+ readonly GATEWAY_FAIL: 0x74;
116
+ readonly GATEWAY_CLOSE: 0x75;
117
+ readonly INBOUND_OPEN: 0x76;
118
+ readonly INBOUND_ACCEPT: 0x77;
119
+ readonly INBOUND_REJECT: 0x78;
120
+ readonly DNS_RESULT: 0x79;
121
+ readonly LISTEN_REQUEST: 0x7a;
122
+ readonly LISTEN_OK: 0x7b;
123
+ readonly LISTEN_FAIL: 0x7c;
124
+ readonly LISTEN_CLOSE: 0x7d;
125
+ readonly GATEWAY_DATA: 0x7e;
126
+
127
+ // Guest
128
+ readonly GUEST_INVITE: 0x80;
129
+ readonly GUEST_JOIN: 0x81;
130
+ readonly GUEST_REVOKE: 0x82;
131
+
132
+ // Sharing
133
+ readonly SHARE_SESSION: 0x83;
134
+ readonly SHARE_REVOKE: 0x84;
135
+
136
+ // Compression
137
+ readonly COMPRESS_BEGIN: 0x85;
138
+ readonly COMPRESS_ACK: 0x86;
139
+
140
+ // Ratecontrol
141
+ readonly RATE_CONTROL: 0x87;
142
+ readonly RATE_WARNING: 0x88;
143
+
144
+ // Linking
145
+ readonly SESSION_LINK: 0x89;
146
+ readonly SESSION_UNLINK: 0x8a;
147
+
148
+ // Copilot
149
+ readonly COPILOT_ATTACH: 0x8b;
150
+ readonly COPILOT_SUGGEST: 0x8c;
151
+ readonly COPILOT_DETACH: 0x8d;
152
+
153
+ // E2e
154
+ readonly KEY_EXCHANGE: 0x8e;
155
+ readonly ENCRYPTED_FRAME: 0x8f;
156
+
157
+ // Echo
158
+ readonly ECHO_ACK: 0x90;
159
+ readonly ECHO_STATE: 0x91;
160
+
161
+ // Termsync
162
+ readonly TERM_SYNC: 0x92;
163
+ readonly TERM_DIFF: 0x93;
164
+
165
+ // Scaling
166
+ readonly NODE_ANNOUNCE: 0x94;
167
+ readonly NODE_REDIRECT: 0x95;
168
+
169
+ // Principals
170
+ readonly SESSION_GRANT: 0x96;
171
+ readonly SESSION_REVOKE: 0x97;
172
+
173
+ // Filechannel
174
+ readonly FILE_OP: 0x98;
175
+ readonly FILE_RESULT: 0x99;
176
+ readonly FILE_CHUNK: 0x9a;
177
+
178
+ // Policy
179
+ readonly POLICY_EVAL: 0x9b;
180
+ readonly POLICY_RESULT: 0x9c;
181
+ readonly POLICY_UPDATE: 0x9d;
182
+
183
+ // Terminal
184
+ readonly TERMINAL_CONFIG: 0x9e;
185
+ };
186
+
187
+ /** Reverse lookup: message type number to name string. */
188
+ export const MSG_NAMES: Readonly<Record<number, string>>;
189
+
190
+ /** Channel kind constants. */
191
+ export const CHANNEL_KIND: {
192
+ readonly PTY: 'pty';
193
+ readonly EXEC: 'exec';
194
+ readonly META: 'meta';
195
+ readonly FILE: 'file';
196
+ readonly TCP: 'tcp';
197
+ readonly UDP: 'udp';
198
+ readonly JOB: 'job';
199
+ };
200
+
201
+ /** Authentication method constants. */
202
+ export const AUTH_METHOD: {
203
+ readonly PUBKEY: 'pubkey';
204
+ readonly PASSWORD: 'password';
205
+ };
206
+
207
+ /** Protocol version string. */
208
+ export const PROTOCOL_VERSION: 'wsh-v1';
209
+
210
+ /** A wsh protocol control message (all messages have a numeric `type`). */
211
+ export interface WshMessage {
212
+ type: number;
213
+ [key: string]: unknown;
214
+ }
215
+
216
+ // -- Message constructors (handshake) --
217
+
218
+ export function hello(opts?: {
219
+ username?: string;
220
+ features?: string[];
221
+ authMethod?: string;
222
+ }): WshMessage;
223
+
224
+ export function serverHello(opts?: {
225
+ sessionId?: string;
226
+ features?: string[];
227
+ fingerprints?: string[];
228
+ }): WshMessage;
229
+
230
+ export function challenge(opts?: {
231
+ nonce?: Uint8Array;
232
+ }): WshMessage;
233
+
234
+ export function authMethods(opts?: {
235
+ methods?: string[];
236
+ }): WshMessage;
237
+
238
+ export function auth(opts?: {
239
+ method?: string;
240
+ signature?: Uint8Array;
241
+ publicKey?: Uint8Array;
242
+ password?: string;
243
+ }): WshMessage;
244
+
245
+ export function authOk(opts?: {
246
+ sessionId?: string;
247
+ token?: string;
248
+ ttl?: number;
249
+ }): WshMessage;
250
+
251
+ export function authFail(opts?: {
252
+ reason?: string;
253
+ }): WshMessage;
254
+
255
+ // -- Message constructors (channel) --
256
+
257
+ export function open(opts?: {
258
+ kind?: string;
259
+ command?: string;
260
+ cols?: number;
261
+ rows?: number;
262
+ env?: Record<string, string>;
263
+ }): WshMessage;
264
+
265
+ export function openOk(opts?: {
266
+ channelId?: number;
267
+ streamIds?: number[];
268
+ dataMode?: string;
269
+ capabilities?: string[];
270
+ }): WshMessage;
271
+
272
+ export function openFail(opts?: {
273
+ reason?: string;
274
+ }): WshMessage;
275
+
276
+ export function resize(opts?: {
277
+ channelId?: number;
278
+ cols?: number;
279
+ rows?: number;
280
+ }): WshMessage;
281
+
282
+ export function signal(opts?: {
283
+ channelId?: number;
284
+ signal?: string;
285
+ }): WshMessage;
286
+
287
+ export function exit(opts?: {
288
+ channelId?: number;
289
+ code?: number;
290
+ }): WshMessage;
291
+
292
+ export function close(opts?: {
293
+ channelId?: number;
294
+ }): WshMessage;
295
+
296
+ export function sessionData(opts?: {
297
+ channelId?: number;
298
+ data?: Uint8Array;
299
+ }): WshMessage;
300
+
301
+ // -- Message constructors (transport) --
302
+
303
+ export function error(opts?: {
304
+ code?: number | string;
305
+ message?: string;
306
+ }): WshMessage;
307
+
308
+ export function ping(opts?: {
309
+ id?: number;
310
+ }): WshMessage;
311
+
312
+ export function pong(opts?: {
313
+ id?: number;
314
+ }): WshMessage;
315
+
316
+ // -- Message constructors (session) --
317
+
318
+ export function attach(opts?: {
319
+ sessionId?: string;
320
+ token?: string;
321
+ mode?: string;
322
+ deviceLabel?: string;
323
+ }): WshMessage;
324
+
325
+ export function resume(opts?: {
326
+ sessionId?: string;
327
+ token?: string;
328
+ lastSeq?: number;
329
+ }): WshMessage;
330
+
331
+ export function rename(opts?: {
332
+ sessionId?: string;
333
+ name?: string;
334
+ }): WshMessage;
335
+
336
+ export function idleWarning(opts?: {
337
+ expiresIn?: number;
338
+ }): WshMessage;
339
+
340
+ export function shutdown(opts?: {
341
+ reason?: string;
342
+ retryAfter?: number;
343
+ }): WshMessage;
344
+
345
+ export function snapshot(opts?: {
346
+ label?: string;
347
+ }): WshMessage;
348
+
349
+ export function presence(opts?: {
350
+ attachments?: unknown;
351
+ }): WshMessage;
352
+
353
+ export function controlChanged(opts?: {
354
+ newController?: string;
355
+ }): WshMessage;
356
+
357
+ export function metrics(opts?: {
358
+ cpu?: number;
359
+ memory?: number;
360
+ sessions?: number;
361
+ rtt?: number;
362
+ }): WshMessage;
363
+
364
+ // -- Message constructors (MCP) --
365
+
366
+ export function mcpDiscover(): WshMessage;
367
+
368
+ export function mcpTools(opts?: {
369
+ tools?: unknown[];
370
+ }): WshMessage;
371
+
372
+ export function mcpCall(opts?: {
373
+ tool?: string;
374
+ arguments?: Record<string, unknown>;
375
+ }): WshMessage;
376
+
377
+ export function mcpResult(opts?: {
378
+ result?: unknown;
379
+ }): WshMessage;
380
+
381
+ // -- Message constructors (reverse) --
382
+
383
+ export function reverseRegister(opts?: {
384
+ username?: string;
385
+ capabilities?: string[];
386
+ peerType?: string;
387
+ shellBackend?: string;
388
+ supportsAttach?: boolean;
389
+ supportsReplay?: boolean;
390
+ supportsEcho?: boolean;
391
+ supportsTermSync?: boolean;
392
+ publicKey?: Uint8Array | null;
393
+ }): WshMessage;
394
+
395
+ export function reverseList(): WshMessage;
396
+
397
+ export function reversePeers(opts?: {
398
+ peers?: unknown[];
399
+ }): WshMessage;
400
+
401
+ export function reverseConnect(opts?: {
402
+ targetFingerprint?: string;
403
+ username?: string;
404
+ }): WshMessage;
405
+
406
+ export function reverseAccept(opts?: {
407
+ targetFingerprint?: string;
408
+ username?: string;
409
+ capabilities?: string[];
410
+ peerType?: string;
411
+ shellBackend?: string;
412
+ supportsAttach?: boolean;
413
+ supportsReplay?: boolean;
414
+ supportsEcho?: boolean;
415
+ supportsTermSync?: boolean;
416
+ }): WshMessage;
417
+
418
+ export function reverseReject(opts?: {
419
+ targetFingerprint?: string;
420
+ username?: string;
421
+ reason?: string;
422
+ }): WshMessage;
423
+
424
+ // -- Message constructors (gateway) --
425
+
426
+ export function openTcp(opts?: {
427
+ gatewayId?: number;
428
+ host?: string;
429
+ port?: number;
430
+ }): WshMessage;
431
+
432
+ export function openUdp(opts?: {
433
+ gatewayId?: number;
434
+ host?: string;
435
+ port?: number;
436
+ }): WshMessage;
437
+
438
+ export function resolveDns(opts?: {
439
+ gatewayId?: number;
440
+ name?: string;
441
+ recordType?: string;
442
+ }): WshMessage;
443
+
444
+ export function gatewayOk(opts?: {
445
+ gatewayId?: number;
446
+ resolvedAddr?: string;
447
+ }): WshMessage;
448
+
449
+ export function gatewayFail(opts?: {
450
+ gatewayId?: number;
451
+ code?: number | string;
452
+ message?: string;
453
+ }): WshMessage;
454
+
455
+ export function gatewayClose(opts?: {
456
+ gatewayId?: number;
457
+ reason?: string;
458
+ }): WshMessage;
459
+
460
+ export function inboundOpen(opts?: {
461
+ listenerId?: number;
462
+ channelId?: number;
463
+ peerAddr?: string;
464
+ peerPort?: number;
465
+ }): WshMessage;
466
+
467
+ export function inboundAccept(opts?: {
468
+ channelId?: number;
469
+ gatewayId?: number;
470
+ }): WshMessage;
471
+
472
+ export function inboundReject(opts?: {
473
+ channelId?: number;
474
+ reason?: string;
475
+ }): WshMessage;
476
+
477
+ export function dnsResult(opts?: {
478
+ gatewayId?: number;
479
+ addresses?: string[];
480
+ ttl?: number;
481
+ }): WshMessage;
482
+
483
+ export function listenRequest(opts?: {
484
+ listenerId?: number;
485
+ port?: number;
486
+ bindAddr?: string;
487
+ }): WshMessage;
488
+
489
+ export function listenOk(opts?: {
490
+ listenerId?: number;
491
+ actualPort?: number;
492
+ }): WshMessage;
493
+
494
+ export function listenFail(opts?: {
495
+ listenerId?: number;
496
+ reason?: string;
497
+ }): WshMessage;
498
+
499
+ export function listenClose(opts?: {
500
+ listenerId?: number;
501
+ }): WshMessage;
502
+
503
+ export function gatewayData(opts?: {
504
+ gatewayId?: number;
505
+ data?: Uint8Array;
506
+ }): WshMessage;
507
+
508
+ // -- Utility --
509
+
510
+ /**
511
+ * Get the human-readable name for a message type number.
512
+ */
513
+ export function msgName(typeNum: number): string;
514
+
515
+ /**
516
+ * Validate that a message has a recognized type field.
517
+ */
518
+ export function isValidMessage(msg: unknown): boolean;
519
+
520
+ // ============================================================================
521
+ // auth.mjs -- Ed25519 crypto
522
+ // ============================================================================
523
+
524
+ /**
525
+ * Generate a new Ed25519 key pair.
526
+ * @param extractable - Whether private key can be exported (default false)
527
+ */
528
+ export function generateKeyPair(extractable?: boolean): Promise<CryptoKeyPair>;
529
+
530
+ /**
531
+ * Export public key as raw 32-byte Ed25519 point.
532
+ */
533
+ export function exportPublicKeyRaw(publicKey: CryptoKey): Promise<Uint8Array>;
534
+
535
+ /**
536
+ * Export public key in SSH wire format: ssh-ed25519 AAAA...
537
+ */
538
+ export function exportPublicKeySSH(publicKey: CryptoKey): Promise<string>;
539
+
540
+ /**
541
+ * Import a raw 32-byte Ed25519 public key.
542
+ */
543
+ export function importPublicKeyRaw(raw: Uint8Array): Promise<CryptoKey>;
544
+
545
+ /**
546
+ * Export private key as PKCS8 bytes.
547
+ * @param privateKey - Must have been created with extractable=true
548
+ */
549
+ export function exportPrivateKeyPKCS8(privateKey: CryptoKey): Promise<Uint8Array>;
550
+
551
+ /**
552
+ * Import a PKCS8-encoded Ed25519 private key.
553
+ */
554
+ export function importPrivateKeyPKCS8(pkcs8: Uint8Array, extractable?: boolean): Promise<CryptoKey>;
555
+
556
+ /**
557
+ * Sign data with an Ed25519 private key.
558
+ * @returns 64-byte signature
559
+ */
560
+ export function sign(privateKey: CryptoKey, data: Uint8Array): Promise<Uint8Array>;
561
+
562
+ /**
563
+ * Verify an Ed25519 signature.
564
+ */
565
+ export function verify(publicKey: CryptoKey, signature: Uint8Array, data: Uint8Array): Promise<boolean>;
566
+
567
+ /**
568
+ * Build the authentication transcript hash for challenge-response signing.
569
+ *
570
+ * transcript = SHA-256("wsh-v1\0" || session_id || nonce || channel_binding)
571
+ *
572
+ * @returns 32-byte SHA-256 hash
573
+ */
574
+ export function buildTranscript(
575
+ sessionId: string,
576
+ nonce: Uint8Array,
577
+ channelBinding?: Uint8Array,
578
+ ): Promise<Uint8Array>;
579
+
580
+ /**
581
+ * Perform the full client-side auth signing:
582
+ * 1. Build transcript hash
583
+ * 2. Sign with private key
584
+ * 3. Export public key for sending to server
585
+ */
586
+ export function signChallenge(
587
+ privateKey: CryptoKey,
588
+ publicKey: CryptoKey,
589
+ sessionId: string,
590
+ nonce: Uint8Array,
591
+ channelBinding?: Uint8Array,
592
+ ): Promise<{ signature: Uint8Array; publicKeyRaw: Uint8Array }>;
593
+
594
+ /**
595
+ * Server-side: verify a client's challenge response.
596
+ */
597
+ export function verifyChallenge(
598
+ publicKey: CryptoKey,
599
+ signature: Uint8Array,
600
+ sessionId: string,
601
+ nonce: Uint8Array,
602
+ channelBinding?: Uint8Array,
603
+ ): Promise<boolean>;
604
+
605
+ /**
606
+ * Compute the SHA-256 fingerprint of a raw public key.
607
+ * @returns hex-encoded fingerprint
608
+ */
609
+ export function fingerprint(publicKeyRaw: Uint8Array): Promise<string>;
610
+
611
+ /**
612
+ * Get the shortest unique prefix of a fingerprint within a set.
613
+ */
614
+ export function shortFingerprint(
615
+ fp: string,
616
+ allFingerprints?: string[],
617
+ minLen?: number,
618
+ ): string;
619
+
620
+ /**
621
+ * Generate a random 32-byte nonce.
622
+ */
623
+ export function generateNonce(): Uint8Array;
624
+
625
+ /**
626
+ * Parse an SSH public key string ("ssh-ed25519 AAAA... comment").
627
+ */
628
+ export function parseSSHPublicKey(line: string): {
629
+ type: string;
630
+ data: Uint8Array;
631
+ comment: string;
632
+ } | null;
633
+
634
+ /**
635
+ * Extract the raw 32-byte Ed25519 public key from SSH wire format.
636
+ */
637
+ export function extractRawFromSSHWire(wireData: Uint8Array): Uint8Array;
638
+
639
+ /**
640
+ * Decode a base64-encoded string into bytes.
641
+ */
642
+ export function base64Decode(str: string): Uint8Array;
643
+
644
+ // ============================================================================
645
+ // transport.mjs -- Abstract transport + WebTransport implementation
646
+ // ============================================================================
647
+
648
+ /** A bidirectional data stream handle. */
649
+ export interface WshStream {
650
+ readable: ReadableStream<Uint8Array>;
651
+ writable: WritableStream<Uint8Array>;
652
+ id: number;
653
+ }
654
+
655
+ /**
656
+ * Abstract transport for the wsh protocol.
657
+ *
658
+ * Subclasses must implement:
659
+ * - _doConnect(url)
660
+ * - _doClose()
661
+ * - _doSendControl(msg)
662
+ * - _doOpenStream()
663
+ */
664
+ export class WshTransport {
665
+ /** Callback for incoming control messages. */
666
+ onControl: ((msg: WshMessage) => void) | null;
667
+
668
+ /** Callback for server-initiated streams. */
669
+ onStreamOpen: ((stream: WshStream) => void) | null;
670
+
671
+ /** Callback when transport closes. */
672
+ onClose: (() => void) | null;
673
+
674
+ /** Callback on transport error. */
675
+ onError: ((err: Error) => void) | null;
676
+
677
+ /** Current transport state. */
678
+ readonly state: 'disconnected' | 'connecting' | 'connected' | 'closed';
679
+
680
+ /** Connect to a wsh server. */
681
+ connect(url: string): Promise<void>;
682
+
683
+ /** Gracefully close the transport. */
684
+ close(): Promise<void>;
685
+
686
+ /** Send a control message (CBOR-framed). */
687
+ sendControl(msg: object): Promise<void>;
688
+
689
+ /** Open a new bidirectional data stream. */
690
+ openStream(): Promise<WshStream>;
691
+
692
+ /** @protected Update internal state. */
693
+ protected _setState(s: string): void;
694
+
695
+ /** @protected Emit a control message to the callback. */
696
+ protected _emitControl(msg: WshMessage): void;
697
+
698
+ /** @protected Emit a new server-initiated stream. */
699
+ protected _emitStreamOpen(stream: WshStream): void;
700
+
701
+ /** @protected Emit close event. */
702
+ protected _emitClose(): void;
703
+
704
+ /** @protected Emit error event. */
705
+ protected _emitError(err: Error): void;
706
+
707
+ /** @protected Must be overridden by subclasses. */
708
+ protected _doConnect(url: string): Promise<void>;
709
+
710
+ /** @protected Must be overridden by subclasses. */
711
+ protected _doClose(): Promise<void>;
712
+
713
+ /** @protected Must be overridden by subclasses. */
714
+ protected _doSendControl(msg: object): Promise<void>;
715
+
716
+ /** @protected Must be overridden by subclasses. */
717
+ protected _doOpenStream(): Promise<WshStream>;
718
+ }
719
+
720
+ /**
721
+ * wsh transport over the WebTransport API.
722
+ *
723
+ * - The first bidirectional stream becomes the control stream.
724
+ * - Control messages use length-prefixed CBOR (frameEncode / FrameDecoder).
725
+ * - Subsequent streams carry raw byte data (no framing).
726
+ * - Server-initiated streams are surfaced via onStreamOpen.
727
+ */
728
+ export class WebTransportTransport extends WshTransport {}
729
+
730
+ // ============================================================================
731
+ // transport-ws.mjs -- WebSocket transport
732
+ // ============================================================================
733
+
734
+ /**
735
+ * wsh transport over a single WebSocket with multiplexed virtual streams.
736
+ *
737
+ * Provides the same interface as WebTransportTransport so that upper
738
+ * layers (session, client) work identically over either transport.
739
+ */
740
+ export class WebSocketTransport extends WshTransport {}
741
+
742
+ // ============================================================================
743
+ // session.mjs -- WshSession
744
+ // ============================================================================
745
+
746
+ /**
747
+ * Manages a single PTY or exec channel over a wsh transport.
748
+ *
749
+ * Each session owns a pair of data streams (stdin/stdout) and receives
750
+ * control messages (EXIT, CLOSE, RESIZE) dispatched by the parent WshClient.
751
+ */
752
+ export class WshSession {
753
+ /** Unique session identifier (typically matches channelId). */
754
+ id: string;
755
+
756
+ /** Channel kind. */
757
+ kind: 'pty' | 'exec';
758
+
759
+ /** Channel identifier assigned by the server. */
760
+ channelId: number;
761
+
762
+ /** Current session state. */
763
+ readonly state: 'opening' | 'active' | 'closed';
764
+
765
+ /** Exit code, if the process has exited. */
766
+ readonly exitCode: number | null;
767
+
768
+ /** Session data plane. */
769
+ readonly dataMode: 'stream' | 'virtual';
770
+
771
+ /** Session capabilities advertised by OPEN_OK. */
772
+ readonly capabilities: string[];
773
+
774
+ /** Called when stdout/stderr data arrives. */
775
+ onData: ((data: Uint8Array) => void) | null;
776
+
777
+ /** Called when the remote process exits. */
778
+ onExit: ((code: number) => void) | null;
779
+
780
+ /** Called when the session is fully closed. */
781
+ onClose: (() => void) | null;
782
+
783
+ /** Called when the remote peer acknowledges speculative local echo. */
784
+ onEchoAck: ((payload: Record<string, unknown>) => void) | null;
785
+
786
+ /** Called when the remote peer reports current echo/cursor state. */
787
+ onEchoState: ((payload: Record<string, unknown>) => void) | null;
788
+
789
+ /** Called when the remote peer emits a terminal sync hash. */
790
+ onTermSync: ((payload: Record<string, unknown>) => void) | null;
791
+
792
+ /** Called when the remote peer emits an incremental terminal diff. */
793
+ onTermDiff: ((payload: Record<string, unknown>) => void) | null;
794
+
795
+ /** Last echo acknowledgement received for the session. */
796
+ readonly lastEchoAck: Record<string, unknown> | null;
797
+
798
+ /** Last echo state received for the session. */
799
+ readonly lastEchoState: Record<string, unknown> | null;
800
+
801
+ /** Last terminal sync received for the session. */
802
+ readonly lastTermSync: Record<string, unknown> | null;
803
+
804
+ /** Last terminal diff received for the session. */
805
+ readonly lastTermDiff: Record<string, unknown> | null;
806
+
807
+ constructor(
808
+ transport: WshTransport,
809
+ channelId: number,
810
+ streamIds: object,
811
+ kind: 'pty' | 'exec',
812
+ opts?: {
813
+ dataMode?: 'stream' | 'virtual';
814
+ capabilities?: string[];
815
+ },
816
+ );
817
+
818
+ /**
819
+ * Write data to the session's stdin stream.
820
+ * Accepts a Uint8Array for raw bytes or a string (UTF-8 encoded).
821
+ */
822
+ write(data: Uint8Array | string): Promise<void>;
823
+
824
+ /**
825
+ * Request the remote PTY to resize.
826
+ */
827
+ resize(cols: number, rows: number): Promise<void>;
828
+
829
+ /**
830
+ * Send a signal to the remote process (e.g. 'SIGINT', 'SIGTERM').
831
+ */
832
+ signal(sig: string): Promise<void>;
833
+
834
+ /**
835
+ * Close this session. Sends a CLOSE control message and tears down
836
+ * the data streams. Safe to call multiple times.
837
+ */
838
+ close(): Promise<void>;
839
+
840
+ /**
841
+ * Bind the raw data streams to this session and start the read pump.
842
+ * Called by WshClient after stream setup.
843
+ * @internal
844
+ */
845
+ _bind(readable: ReadableStream<Uint8Array>, writable: WritableStream<Uint8Array>): void;
846
+
847
+ /**
848
+ * Activate the message-backed data plane for a virtual session.
849
+ * @internal
850
+ */
851
+ _activateVirtual(sendControl: (msg: WshMessage) => Promise<void>): void;
852
+
853
+ /**
854
+ * Handle a control message dispatched by WshClient for this channel.
855
+ * @internal
856
+ */
857
+ _handleControlMessage(msg: WshMessage): void;
858
+
859
+ /**
860
+ * Continuously read from the stdout data stream and invoke onData.
861
+ * @internal
862
+ */
863
+ _pumpDataStream(): Promise<void>;
864
+ }
865
+
866
+ export function normalizeSessionData(data: Uint8Array | string): Uint8Array;
867
+
868
+ export class WshVirtualSessionBackend {
869
+ constructor(sendControl: (msg: WshMessage) => Promise<void>, channelId: number);
870
+ write(data: Uint8Array | string): Promise<void>;
871
+ }
872
+
873
+ // ============================================================================
874
+ // client.mjs -- WshClient
875
+ // ============================================================================
876
+
877
+ /** Options for WshClient.connect(). */
878
+ export interface WshConnectOptions {
879
+ username: string;
880
+ keyPair?: CryptoKeyPair;
881
+ password?: string;
882
+ transport?: 'wt' | 'ws';
883
+ timeout?: number;
884
+ }
885
+
886
+ /** Options for WshClient.openSession(). */
887
+ export interface WshOpenSessionOptions {
888
+ type?: 'pty' | 'exec';
889
+ command?: string;
890
+ cols?: number;
891
+ rows?: number;
892
+ env?: Record<string, string>;
893
+ timeout?: number;
894
+ }
895
+
896
+ /** Options for WshClient.attachSession(). */
897
+ export interface WshAttachSessionOptions {
898
+ readOnly?: boolean;
899
+ timeout?: number;
900
+ }
901
+
902
+ /** Options for WshClient.connectReverse(). */
903
+ export interface WshConnectReverseOptions {
904
+ username: string;
905
+ keyPair?: CryptoKeyPair;
906
+ password?: string;
907
+ expose?: {
908
+ shell?: boolean;
909
+ exec?: boolean;
910
+ fs?: boolean;
911
+ tools?: boolean;
912
+ };
913
+ peerType?: string;
914
+ shellBackend?: string;
915
+ supportsAttach?: boolean;
916
+ supportsReplay?: boolean;
917
+ supportsEcho?: boolean;
918
+ supportsTermSync?: boolean;
919
+ }
920
+
921
+ /** Options for WshClient.exec(). */
922
+ export interface WshExecOptions {
923
+ username: string;
924
+ keyPair?: CryptoKeyPair;
925
+ password?: string;
926
+ timeout?: number;
927
+ }
928
+
929
+ /** Result from WshClient.exec(). */
930
+ export interface WshExecResult {
931
+ stdout: Uint8Array;
932
+ exitCode: number;
933
+ }
934
+
935
+ /** Session list entry. */
936
+ export interface WshSessionInfo {
937
+ channelId: number;
938
+ kind: string;
939
+ state: string;
940
+ }
941
+
942
+ /** Peer information from reverse list. */
943
+ export interface WshPeerInfo {
944
+ fingerprint: string;
945
+ fingerprint_short: string;
946
+ username: string;
947
+ capabilities: string[];
948
+ peer_type: string;
949
+ shell_backend: string;
950
+ source: string;
951
+ supports_attach: boolean;
952
+ supports_replay: boolean;
953
+ supports_echo: boolean;
954
+ supports_term_sync: boolean;
955
+ last_seen: number | null;
956
+ }
957
+
958
+ /**
959
+ * WshClient -- manages a wsh connection, authentication, and multiple sessions.
960
+ *
961
+ * Handles the full lifecycle: transport selection, handshake, challenge-response
962
+ * or password auth, channel multiplexing, ping/pong keepalive, and teardown.
963
+ *
964
+ * Supports forward connections (client opens sessions on a remote server) and
965
+ * reverse mode (client registers as a peer for incoming connections).
966
+ */
967
+ export class WshClient {
968
+ /** Current client state. */
969
+ readonly state: 'disconnected' | 'connecting' | 'connected' | 'authenticated' | 'closed';
970
+
971
+ /** Server-assigned session ID. */
972
+ readonly sessionId: string | null;
973
+
974
+ /** Read-only view of active sessions. */
975
+ readonly sessions: Map<number, WshSession>;
976
+
977
+ /** Server-advertised features from SERVER_HELLO. */
978
+ readonly features: string[];
979
+
980
+ /**
981
+ * Low-level transport reference.
982
+ * Prefer higher-level methods for normal use.
983
+ */
984
+ readonly _transport: WshTransport | null;
985
+
986
+ /** Called when the connection is closed (intentionally or otherwise). */
987
+ onClose: (() => void) | null;
988
+
989
+ /** Called on connection-level errors. */
990
+ onError: ((err: Error) => void) | null;
991
+
992
+ /** Called when a reverse-connect request arrives (reverse mode only). */
993
+ onReverseConnect: ((msg: WshMessage) => void) | null;
994
+
995
+ /** Called when a clipboard sync message arrives (OSC 52). */
996
+ onClipboard: ((msg: WshMessage) => void) | null;
997
+
998
+ /**
999
+ * Called when a relay-forwarded message arrives from a remote peer.
1000
+ * In reverse mode, the relay bridge forwards messages from the CLI peer
1001
+ * to this browser client.
1002
+ */
1003
+ onRelayMessage: ((msg: WshMessage) => void) | null;
1004
+
1005
+ /**
1006
+ * Called when a gateway-subsystem control message arrives (opcodes 0x70-0x7f).
1007
+ */
1008
+ onGatewayMessage: ((msg: WshMessage) => void) | null;
1009
+
1010
+ /**
1011
+ * Check if the server advertised a specific feature.
1012
+ */
1013
+ hasFeature(name: string): boolean;
1014
+
1015
+ /**
1016
+ * Connect to a wsh server, authenticate, and return the session ID.
1017
+ */
1018
+ connect(url: string, opts?: WshConnectOptions): Promise<string>;
1019
+
1020
+ /**
1021
+ * Open a new PTY or exec session on the remote server.
1022
+ */
1023
+ openSession(opts?: WshOpenSessionOptions): Promise<WshSession>;
1024
+
1025
+ /**
1026
+ * List locally tracked sessions with their current state.
1027
+ */
1028
+ listSessions(): WshSessionInfo[];
1029
+
1030
+ /**
1031
+ * Attach to an existing remote session (collaborative or read-only).
1032
+ */
1033
+ attachSession(targetSessionId: string, opts?: WshAttachSessionOptions): Promise<WshMessage>;
1034
+
1035
+ /**
1036
+ * Resume a previously disconnected session.
1037
+ */
1038
+ resumeSession(targetSessionId: string, token: string, opts?: { timeout?: number }): Promise<WshMessage>;
1039
+
1040
+ /**
1041
+ * Gracefully disconnect: close all sessions and the transport.
1042
+ */
1043
+ disconnect(): Promise<void>;
1044
+
1045
+ /**
1046
+ * One-shot command execution: connect, authenticate, run a command,
1047
+ * collect all output, disconnect, and return the result.
1048
+ */
1049
+ static exec(url: string, command: string, opts?: WshExecOptions): Promise<WshExecResult>;
1050
+
1051
+ /**
1052
+ * Create a client with a pre-configured transport instance.
1053
+ * Useful for WebSocket transport or custom transports.
1054
+ */
1055
+ static withTransport(transport: WshTransport): WshClient;
1056
+
1057
+ /**
1058
+ * Connect in reverse mode: register as a peer that can accept incoming
1059
+ * connections from other clients.
1060
+ */
1061
+ connectReverse(url: string, opts?: WshConnectReverseOptions): Promise<string>;
1062
+
1063
+ /**
1064
+ * List peers registered on the relay server.
1065
+ */
1066
+ listPeers(timeout?: number): Promise<WshPeerInfo[]>;
1067
+
1068
+ /**
1069
+ * Initiate a reverse connection to a registered peer.
1070
+ */
1071
+ reverseConnectTo(targetFingerprint: string, timeout?: number): Promise<void>;
1072
+
1073
+ /**
1074
+ * Send a control message over the authenticated relay connection.
1075
+ */
1076
+ sendRelayControl(msg: WshMessage): Promise<void>;
1077
+
1078
+ /**
1079
+ * Upload a blob to a remote path.
1080
+ */
1081
+ upload(blob: Blob | Uint8Array, remotePath: string, opts?: { onProgress?: (bytesSent: number) => void }): Promise<void>;
1082
+
1083
+ /**
1084
+ * Download a file from a remote path.
1085
+ */
1086
+ download(remotePath: string): Promise<Uint8Array>;
1087
+
1088
+ /**
1089
+ * Discover MCP tools available on the remote server.
1090
+ */
1091
+ discoverTools(timeout?: number): Promise<unknown[]>;
1092
+
1093
+ /**
1094
+ * Call an MCP tool on the remote server.
1095
+ */
1096
+ callTool(name: string, args: Record<string, unknown>, timeout?: number): Promise<unknown>;
1097
+
1098
+ /**
1099
+ * Connect using an externally created transport.
1100
+ * Use this when you need WebSocket or a custom transport.
1101
+ */
1102
+ connectWithTransport(transport: WshTransport, url: string, opts: WshConnectOptions): Promise<string>;
1103
+
1104
+ /**
1105
+ * Check whether a message type is relay-forwardable.
1106
+ * @internal
1107
+ */
1108
+ _isRelayForwardable(type: number): boolean;
1109
+
1110
+ /**
1111
+ * Get the next channel ID.
1112
+ * @internal
1113
+ */
1114
+ _nextChannelId(): number;
1115
+ }
1116
+
1117
+ // ============================================================================
1118
+ // keystore.mjs -- WshKeyStore
1119
+ // ============================================================================
1120
+
1121
+ /** Key entry from the key store. */
1122
+ export interface WshKeyEntry {
1123
+ name: string;
1124
+ publicKey: CryptoKey;
1125
+ privateKey: CryptoKey;
1126
+ createdAt: number;
1127
+ fingerprint: string;
1128
+ }
1129
+
1130
+ /** Key listing entry (without CryptoKey objects). */
1131
+ export interface WshKeyListEntry {
1132
+ name: string;
1133
+ fingerprint: string;
1134
+ createdAt: number;
1135
+ }
1136
+
1137
+ /** Result of key generation. */
1138
+ export interface WshKeyGenerateResult {
1139
+ name: string;
1140
+ fingerprint: string;
1141
+ publicKeySSH: string;
1142
+ }
1143
+
1144
+ /** Result of backup operation. */
1145
+ export interface WshBackupResult {
1146
+ backedUp: number;
1147
+ skipped: number;
1148
+ }
1149
+
1150
+ /** Result of restore operation. */
1151
+ export interface WshRestoreResult {
1152
+ restored: number;
1153
+ skipped: number;
1154
+ }
1155
+
1156
+ /**
1157
+ * WshKeyStore -- Ed25519 key management via IndexedDB with OPFS encrypted backup.
1158
+ *
1159
+ * Keys are stored as non-extractable CryptoKey objects in IndexedDB for
1160
+ * day-to-day use. An optional passphrase-encrypted backup can be written
1161
+ * to OPFS (requires extractable keys at generation time).
1162
+ */
1163
+ export class WshKeyStore {
1164
+ /** @internal */
1165
+ _db: IDBDatabase | null;
1166
+
1167
+ /** Open or create the IndexedDB database. */
1168
+ open(): Promise<void>;
1169
+
1170
+ /** Close the database connection. */
1171
+ close(): void;
1172
+
1173
+ /**
1174
+ * Generate a new Ed25519 key pair and store it in IndexedDB.
1175
+ * @param name - Key name / identifier (default 'default')
1176
+ * @param opts.extractable - Whether the private key can be exported (default false)
1177
+ */
1178
+ generateKey(name?: string, opts?: { extractable?: boolean }): Promise<WshKeyGenerateResult>;
1179
+
1180
+ /**
1181
+ * Get a stored key entry by name.
1182
+ */
1183
+ getKey(name: string): Promise<WshKeyEntry | null>;
1184
+
1185
+ /**
1186
+ * List all stored key names and fingerprints.
1187
+ */
1188
+ listKeys(): Promise<WshKeyListEntry[]>;
1189
+
1190
+ /**
1191
+ * Delete a key by name.
1192
+ * @returns true if the key existed and was deleted
1193
+ */
1194
+ deleteKey(name: string): Promise<boolean>;
1195
+
1196
+ /**
1197
+ * Export a public key as an SSH-formatted string.
1198
+ * @returns ssh-ed25519 AAAA... format
1199
+ */
1200
+ exportPublicKey(name: string): Promise<string>;
1201
+
1202
+ /**
1203
+ * Get the CryptoKey pair for a named key.
1204
+ */
1205
+ getKeyPair(name: string): Promise<{ publicKey: CryptoKey; privateKey: CryptoKey }>;
1206
+
1207
+ /**
1208
+ * Export all extractable keys, encrypt with a passphrase, and store in OPFS.
1209
+ */
1210
+ backup(passphrase: string): Promise<WshBackupResult>;
1211
+
1212
+ /**
1213
+ * Restore keys from an OPFS encrypted backup.
1214
+ */
1215
+ restore(passphrase: string): Promise<WshRestoreResult>;
1216
+ }
1217
+
1218
+ // ============================================================================
1219
+ // file-transfer.mjs -- WshFileTransfer
1220
+ // ============================================================================
1221
+
1222
+ /** Upload progress callback payload. */
1223
+ export interface WshFileUploadProgress {
1224
+ sent: number;
1225
+ total: number;
1226
+ }
1227
+
1228
+ /** Download progress callback payload. */
1229
+ export interface WshFileDownloadProgress {
1230
+ received: number;
1231
+ total?: number;
1232
+ }
1233
+
1234
+ /** Upload result. */
1235
+ export interface WshFileUploadResult {
1236
+ success: boolean;
1237
+ bytesTransferred: number;
1238
+ }
1239
+
1240
+ /** File listing entry. */
1241
+ export interface WshFileListEntry {
1242
+ name: string;
1243
+ size: number;
1244
+ modified: string;
1245
+ type: 'file' | 'directory' | 'symlink' | 'device' | 'pipe' | 'socket';
1246
+ }
1247
+
1248
+ /**
1249
+ * WshFileTransfer -- dedicated stream file transfer (scp-like) over a wsh connection.
1250
+ *
1251
+ * Uploads and downloads files using the wsh channel protocol with 'file' kind
1252
+ * channels. Data flows over a dedicated bidirectional stream in 64KB chunks.
1253
+ */
1254
+ export class WshFileTransfer {
1255
+ constructor(client: object);
1256
+
1257
+ /**
1258
+ * Upload data to a remote path.
1259
+ */
1260
+ upload(
1261
+ data: Uint8Array | ArrayBuffer,
1262
+ remotePath: string,
1263
+ opts?: {
1264
+ onProgress?: (progress: WshFileUploadProgress) => void;
1265
+ timeout?: number;
1266
+ },
1267
+ ): Promise<WshFileUploadResult>;
1268
+
1269
+ /**
1270
+ * Download a file from a remote path.
1271
+ */
1272
+ download(
1273
+ remotePath: string,
1274
+ opts?: {
1275
+ onProgress?: (progress: WshFileDownloadProgress) => void;
1276
+ timeout?: number;
1277
+ },
1278
+ ): Promise<Uint8Array>;
1279
+
1280
+ /**
1281
+ * List files at a remote path.
1282
+ */
1283
+ list(remotePath: string): Promise<WshFileListEntry[]>;
1284
+ }
1285
+
1286
+ // ============================================================================
1287
+ // recording.mjs -- SessionRecorder + SessionPlayer
1288
+ // ============================================================================
1289
+
1290
+ /** Event type for session recording. */
1291
+ export type SessionRecordingEventType = 'input' | 'output' | 'resize' | 'open' | 'exit';
1292
+
1293
+ /** A single recorded event entry. */
1294
+ export interface SessionRecordingEntry {
1295
+ t: number;
1296
+ type: string;
1297
+ data: unknown;
1298
+ }
1299
+
1300
+ /** Asciicast v2 export format. */
1301
+ export interface SessionRecordingJSON {
1302
+ version: 2;
1303
+ width: number;
1304
+ height: number;
1305
+ timestamp: number;
1306
+ env: Record<string, string>;
1307
+ sessionId: string;
1308
+ events: Array<[number, string, string]>;
1309
+ }
1310
+
1311
+ /**
1312
+ * Records PTY I/O events with relative timestamps for session replay.
1313
+ */
1314
+ export class SessionRecorder {
1315
+ /** Unique session identifier. */
1316
+ sessionId: string;
1317
+
1318
+ /** Unix epoch ms when recording started. */
1319
+ startTime: number;
1320
+
1321
+ /** Recorded event entries. */
1322
+ entries: SessionRecordingEntry[];
1323
+
1324
+ /** Total duration in milliseconds. */
1325
+ readonly duration: number;
1326
+
1327
+ /** Number of recorded events. */
1328
+ readonly length: number;
1329
+
1330
+ constructor(sessionId: string, opts?: { width?: number; height?: number });
1331
+
1332
+ /**
1333
+ * Record an event.
1334
+ */
1335
+ record(type: SessionRecordingEventType | string, data: unknown): void;
1336
+
1337
+ /**
1338
+ * Export the recording as a JSON-serializable object (asciicast v2 format).
1339
+ */
1340
+ toJSON(): SessionRecordingJSON;
1341
+
1342
+ /**
1343
+ * Import a recording from JSON.
1344
+ */
1345
+ static fromJSON(json: SessionRecordingJSON | string): SessionRecorder;
1346
+ }
1347
+
1348
+ /** Playback controller interface. */
1349
+ export interface SessionPlaybackController {
1350
+ /** Pause playback. */
1351
+ pause(): void;
1352
+
1353
+ /** Resume playback after pause. */
1354
+ resume(): void;
1355
+
1356
+ /** Stop playback entirely. */
1357
+ stop(): void;
1358
+
1359
+ /**
1360
+ * Seek to a specific time in the recording.
1361
+ * Immediately replays all output events up to the target time,
1362
+ * then resumes normal timed playback from that point.
1363
+ * @param timeMs - Target time in milliseconds from start
1364
+ */
1365
+ seek(timeMs: number): void;
1366
+ }
1367
+
1368
+ /** Recording metadata. */
1369
+ export interface SessionPlayerMetadata {
1370
+ width: number;
1371
+ height: number;
1372
+ duration: number;
1373
+ eventCount: number;
1374
+ }
1375
+
1376
+ /**
1377
+ * Replays a recorded session with original timing.
1378
+ */
1379
+ export class SessionPlayer {
1380
+ constructor(recording: SessionRecorder | SessionRecordingJSON);
1381
+
1382
+ /** Get the recording metadata. */
1383
+ readonly metadata: SessionPlayerMetadata;
1384
+
1385
+ /**
1386
+ * Replay the recording with original timing.
1387
+ */
1388
+ play(
1389
+ onData: (data: string) => void,
1390
+ opts?: {
1391
+ speed?: number;
1392
+ onEvent?: (type: string, data: unknown) => void;
1393
+ },
1394
+ ): SessionPlaybackController;
1395
+ }
1396
+
1397
+ // ============================================================================
1398
+ // mcp-bridge.mjs -- WshMcpBridge
1399
+ // ============================================================================
1400
+
1401
+ /** MCP tool specification. */
1402
+ export interface WshMcpToolSpec {
1403
+ name: string;
1404
+ description: string;
1405
+ parameters: Record<string, unknown>;
1406
+ }
1407
+
1408
+ /** MCP call result. */
1409
+ export interface WshMcpCallResult {
1410
+ success: boolean;
1411
+ output: unknown;
1412
+ error?: string;
1413
+ }
1414
+
1415
+ /**
1416
+ * WshMcpBridge -- bridges remote MCP tools over the wsh meta channel.
1417
+ *
1418
+ * Sends MCP_DISCOVER and MCP_CALL messages through the wsh control channel,
1419
+ * enabling a wsh client to discover and invoke MCP tools hosted on the
1420
+ * remote server.
1421
+ */
1422
+ export class WshMcpBridge {
1423
+ constructor(client: object);
1424
+
1425
+ /**
1426
+ * Discover available MCP tools on the remote server.
1427
+ */
1428
+ discover(opts?: { timeout?: number }): Promise<WshMcpToolSpec[]>;
1429
+
1430
+ /**
1431
+ * Call a remote MCP tool by name.
1432
+ */
1433
+ call(
1434
+ toolName: string,
1435
+ args?: Record<string, unknown>,
1436
+ opts?: { timeout?: number },
1437
+ ): Promise<WshMcpCallResult>;
1438
+
1439
+ /**
1440
+ * Get cached tool specs (after discover() has been called).
1441
+ */
1442
+ getToolSpecs(): WshMcpToolSpec[];
1443
+
1444
+ /**
1445
+ * Check whether a specific tool is available (based on cached discovery).
1446
+ */
1447
+ hasTool(toolName: string): boolean;
1448
+
1449
+ /** Get the number of cached tools. */
1450
+ readonly toolCount: number;
1451
+
1452
+ /** Clear the cached tool specs. Call discover() again to refresh. */
1453
+ clearCache(): void;
1454
+ }