ai-remote 0.1.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.
Files changed (114) hide show
  1. package/README.md +50 -0
  2. package/dist/index.js +7 -0
  3. package/dist/index.js.map +7 -0
  4. package/dist/protocols/index.js +35 -0
  5. package/dist/protocols/index.js.map +7 -0
  6. package/dist/protocols/rdp/buffer.js +170 -0
  7. package/dist/protocols/rdp/buffer.js.map +7 -0
  8. package/dist/protocols/rdp/caps.js +260 -0
  9. package/dist/protocols/rdp/caps.js.map +7 -0
  10. package/dist/protocols/rdp/cert.js +135 -0
  11. package/dist/protocols/rdp/cert.js.map +7 -0
  12. package/dist/protocols/rdp/client.js +1068 -0
  13. package/dist/protocols/rdp/client.js.map +7 -0
  14. package/dist/protocols/rdp/cliprdr.js +240 -0
  15. package/dist/protocols/rdp/cliprdr.js.map +7 -0
  16. package/dist/protocols/rdp/credssp.js +254 -0
  17. package/dist/protocols/rdp/credssp.js.map +7 -0
  18. package/dist/protocols/rdp/crypto.js +369 -0
  19. package/dist/protocols/rdp/crypto.js.map +7 -0
  20. package/dist/protocols/rdp/display.js +589 -0
  21. package/dist/protocols/rdp/display.js.map +7 -0
  22. package/dist/protocols/rdp/gcc.js +132 -0
  23. package/dist/protocols/rdp/gcc.js.map +7 -0
  24. package/dist/protocols/rdp/keymap.js +132 -0
  25. package/dist/protocols/rdp/keymap.js.map +7 -0
  26. package/dist/protocols/rdp/mcs.js +223 -0
  27. package/dist/protocols/rdp/mcs.js.map +7 -0
  28. package/dist/protocols/rdp/ntlm.js +305 -0
  29. package/dist/protocols/rdp/ntlm.js.map +7 -0
  30. package/dist/protocols/rdp/pdu.js +444 -0
  31. package/dist/protocols/rdp/pdu.js.map +7 -0
  32. package/dist/protocols/rdp/rail.js +383 -0
  33. package/dist/protocols/rdp/rail.js.map +7 -0
  34. package/dist/protocols/rdp/rle.js +276 -0
  35. package/dist/protocols/rdp/rle.js.map +7 -0
  36. package/dist/protocols/rdp/sec.js +156 -0
  37. package/dist/protocols/rdp/sec.js.map +7 -0
  38. package/dist/protocols/rdp/session.js +307 -0
  39. package/dist/protocols/rdp/session.js.map +7 -0
  40. package/dist/protocols/rdp/tls.js +354 -0
  41. package/dist/protocols/rdp/tls.js.map +7 -0
  42. package/dist/protocols/rdp/vchannel.js +59 -0
  43. package/dist/protocols/rdp/vchannel.js.map +7 -0
  44. package/dist/protocols/rdp/x224.js +110 -0
  45. package/dist/protocols/rdp/x224.js.map +7 -0
  46. package/dist/protocols/ssh/kex.js +177 -0
  47. package/dist/protocols/ssh/kex.js.map +7 -0
  48. package/dist/protocols/ssh/messages.js +55 -0
  49. package/dist/protocols/ssh/messages.js.map +7 -0
  50. package/dist/protocols/ssh/session.js +608 -0
  51. package/dist/protocols/ssh/session.js.map +7 -0
  52. package/dist/protocols/ssh/terminal.js +191 -0
  53. package/dist/protocols/ssh/terminal.js.map +7 -0
  54. package/dist/protocols/ssh/transport.js +611 -0
  55. package/dist/protocols/ssh/transport.js.map +7 -0
  56. package/dist/protocols/ssh/wire.js +189 -0
  57. package/dist/protocols/ssh/wire.js.map +7 -0
  58. package/dist/protocols/types.js +1 -0
  59. package/dist/protocols/types.js.map +7 -0
  60. package/dist/protocols/vnc/input.js +16 -0
  61. package/dist/protocols/vnc/input.js.map +7 -0
  62. package/dist/protocols/vnc/session.js +259 -0
  63. package/dist/protocols/vnc/session.js.map +7 -0
  64. package/dist/shared/connection.js +114 -0
  65. package/dist/shared/connection.js.map +7 -0
  66. package/dist/shared/device.js +1 -0
  67. package/dist/shared/device.js.map +7 -0
  68. package/dist/shared/hosts.js +133 -0
  69. package/dist/shared/hosts.js.map +7 -0
  70. package/dist/shared/icons.js +42 -0
  71. package/dist/shared/icons.js.map +7 -0
  72. package/dist/shared/protocol.js +40 -0
  73. package/dist/shared/protocol.js.map +7 -0
  74. package/dist/shared/signals.js +42 -0
  75. package/dist/shared/signals.js.map +7 -0
  76. package/package.json +82 -0
  77. package/src/index.ts +14 -0
  78. package/src/protocols/index.ts +72 -0
  79. package/src/protocols/rdp/buffer.ts +204 -0
  80. package/src/protocols/rdp/caps.ts +304 -0
  81. package/src/protocols/rdp/cert.ts +190 -0
  82. package/src/protocols/rdp/client.ts +1363 -0
  83. package/src/protocols/rdp/cliprdr.ts +344 -0
  84. package/src/protocols/rdp/credssp.ts +319 -0
  85. package/src/protocols/rdp/crypto.ts +416 -0
  86. package/src/protocols/rdp/display.ts +750 -0
  87. package/src/protocols/rdp/gcc.ts +162 -0
  88. package/src/protocols/rdp/keymap.ts +68 -0
  89. package/src/protocols/rdp/mcs.ts +283 -0
  90. package/src/protocols/rdp/ntlm.ts +380 -0
  91. package/src/protocols/rdp/pdu.ts +497 -0
  92. package/src/protocols/rdp/rail.ts +480 -0
  93. package/src/protocols/rdp/rle.ts +343 -0
  94. package/src/protocols/rdp/sec.ts +184 -0
  95. package/src/protocols/rdp/session.ts +393 -0
  96. package/src/protocols/rdp/tls.ts +452 -0
  97. package/src/protocols/rdp/vchannel.ts +89 -0
  98. package/src/protocols/rdp/x224.ts +139 -0
  99. package/src/protocols/ssh/kex.ts +227 -0
  100. package/src/protocols/ssh/messages.ts +54 -0
  101. package/src/protocols/ssh/session.ts +878 -0
  102. package/src/protocols/ssh/terminal.ts +242 -0
  103. package/src/protocols/ssh/transport.ts +732 -0
  104. package/src/protocols/ssh/wire.ts +220 -0
  105. package/src/protocols/types.ts +146 -0
  106. package/src/protocols/vnc/input.ts +13 -0
  107. package/src/protocols/vnc/novnc.d.ts +75 -0
  108. package/src/protocols/vnc/session.ts +367 -0
  109. package/src/shared/connection.ts +206 -0
  110. package/src/shared/device.ts +28 -0
  111. package/src/shared/hosts.ts +240 -0
  112. package/src/shared/icons.ts +49 -0
  113. package/src/shared/protocol.ts +55 -0
  114. package/src/shared/signals.ts +67 -0
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # ai-remote
2
+
3
+ > Pure TypeScript protocol engines (RDP, VNC, SSH) and remote desktop vocabulary for AI agents, Node.js, and web browsers.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/ai-remote.svg)](https://www.npmjs.com/package/ai-remote)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ ## Overview
9
+
10
+ `ai-remote` is an environment-neutral core protocol library providing client-side implementations for **RDP**, **VNC**, and **SSH**. It powers browser-based remote gateways, terminal sessions, and automated AI agents driving remote desktops.
11
+
12
+ ## Features
13
+
14
+ - **RDP Engine:**
15
+ - Complete X.224, MCS, and GCC capability negotiation.
16
+ - CredSSP v6 with NTLMv2 and TLS 1.2 client implementation over WebCrypto.
17
+ - Fast-path 16-bit interleaved RLE bitmap decoding.
18
+ - Shared clipboard support (`cliprdr` virtual channel).
19
+ - RemoteApp (RAIL) window orders and execution.
20
+ - **SSH Engine:**
21
+ - RFC 4253 binary packet protocol with `curve25519-sha256` / `ecdh-sha2-nistp256`.
22
+ - AEAD ciphers (`aes256-gcm@openssh.com`, `aes128-gcm@openssh.com`).
23
+ - Terminal interactive and agent command execution.
24
+ - **VNC Engine:**
25
+ - RFB protocol integration with Apple Diffie-Hellman handshake support.
26
+ - **AI Agent Tool Vocabulary:**
27
+ - Standardized connection, device, protocol, and signal definitions.
28
+
29
+ ## Installation
30
+
31
+ ```bash
32
+ pnpm add ai-remote
33
+ # or
34
+ npm install ai-remote
35
+ ```
36
+
37
+ ## Subpath Exports
38
+
39
+ | Export | Description |
40
+ |---|---|
41
+ | `ai-remote` | Shared vocabulary (`ConnectionSettings`, `RemoteProtocol`, `signals`, etc.) |
42
+ | `ai-remote/protocols` | Driver registry (`driverFor`, `DRIVERS`, `explainDisconnect`) |
43
+ | `ai-remote/protocols/rdp` | RDP client, session, credentials, and crypto engines |
44
+ | `ai-remote/protocols/ssh` | SSH session, transport, wire protocol, and terminal |
45
+ | `ai-remote/protocols/vnc` | VNC session and input management |
46
+ | `ai-remote/shared/*` | Direct access to shared models and utilities |
47
+
48
+ ## License
49
+
50
+ MIT © Leon Guo
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ export * from "./shared/connection";
2
+ export * from "./shared/device";
3
+ export * from "./shared/hosts";
4
+ export * from "./shared/icons";
5
+ export * from "./shared/protocol";
6
+ export * from "./shared/signals";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index.ts"],
4
+ "sourcesContent": ["/**\n * The vocabulary every host of this stack shares.\n *\n * Deliberately no protocol engines: a Worker that only needs to know what a\n * host roster is should not pull noVNC or an RDP state machine into its\n * bundle. The engines live behind `@remotectl/core/protocols`.\n */\n\nexport * from './shared/connection';\nexport * from './shared/device';\nexport * from './shared/hosts';\nexport * from './shared/icons';\nexport * from './shared/protocol';\nexport * from './shared/signals';\n"],
5
+ "mappings": "AAQA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;",
6
+ "names": []
7
+ }
@@ -0,0 +1,35 @@
1
+ import { rdpDriver } from "./rdp/session";
2
+ import { vncDriver } from "./vnc/session";
3
+ const DRIVERS = {
4
+ vnc: vncDriver,
5
+ rdp: rdpDriver
6
+ };
7
+ function driverFor(protocol) {
8
+ return DRIVERS[protocol] ?? DRIVERS.vnc;
9
+ }
10
+ function explainDisconnect(diagnostic, clean, protocolMessage) {
11
+ if (protocolMessage) return protocolMessage;
12
+ const code = diagnostic.close?.code;
13
+ const reason = diagnostic.close?.reason.trim() ?? "";
14
+ const suffix = code ? ` (WebSocket ${code}${reason ? `: ${reason}` : ""})` : "";
15
+ const context = { ...code === void 0 ? {} : { code }, reason, suffix };
16
+ const driver = driverFor(diagnostic.protocol);
17
+ const specific = driver.describeDisconnect(context);
18
+ if (specific) return specific;
19
+ const name = driver.protocol.toUpperCase();
20
+ if (/tcp (reached )?eof/i.test(reason)) {
21
+ return `The ${name} host accepted the connection and then closed it${suffix}. Check its remote-access permissions and the account name.`;
22
+ }
23
+ if (code === 1006) {
24
+ return "The connection was interrupted before a close reason arrived (WebSocket 1006). This can happen during a deployment or a brief network change. Automatic reconnect did not restore the session; reconnect once, then check Worker live logs if it repeats.";
25
+ }
26
+ if (reason) return `The remote session ended${suffix}.`;
27
+ if (clean) return `The remote server closed the session cleanly${suffix || " (no reason supplied)"}.`;
28
+ return `The ${name} negotiation failed${suffix || " before the server supplied a close reason"}.`;
29
+ }
30
+ export {
31
+ DRIVERS,
32
+ driverFor,
33
+ explainDisconnect
34
+ };
35
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/protocols/index.ts"],
4
+ "sourcesContent": ["/**\n * The protocol registry the workspace talks to.\n *\n * The UI owns the toolbar, the dialogs, the stats rail and the on-screen\n * keyboard, none of which care whether a session is VNC or RDP. Everything that\n * does differ -- the endpoint, how a client is constructed, the wording of a\n * failure, the labels in the connect dialog -- is looked up here instead of\n * branching at each site.\n */\n\nimport { rdpDriver } from './rdp/session';\nimport { vncDriver } from './vnc/session';\nimport type { DisconnectContext, ProtocolDriver } from './types';\nimport type { RemoteProtocol } from '../shared/protocol';\n\nexport type { ProtocolDriver, RemoteSession, SessionOptions, ScreenGeometry } from './types';\n\nexport const DRIVERS: Record<RemoteProtocol, ProtocolDriver> = {\n vnc: vncDriver,\n rdp: rdpDriver,\n};\n\n/** Unknown names fall back to VNC, which is what the dialog opens on. */\nexport function driverFor(protocol: string | undefined): ProtocolDriver {\n return DRIVERS[protocol as RemoteProtocol] ?? DRIVERS.vnc;\n}\n\n/** What the workspace knows about a session that has just ended. */\nexport interface DisconnectDiagnostic {\n protocol: RemoteProtocol;\n /** The transport close, when one arrived. A 1006 means it did not. */\n close: { code: number; reason: string } | null;\n /** True once the session had reached the desktop. */\n established: boolean;\n}\n\n/**\n * Explain a disconnect in terms someone can act on. The protocol gets first\n * say, then the close codes that mean the same thing either way.\n */\nexport function explainDisconnect(\n diagnostic: DisconnectDiagnostic,\n clean: boolean,\n protocolMessage: string\n): string {\n // A client that reports its own failure states it in the protocol's terms,\n // which is always more specific than anything the transport can say.\n if (protocolMessage) return protocolMessage;\n\n const code = diagnostic.close?.code;\n const reason = diagnostic.close?.reason.trim() ?? '';\n const suffix = code ? ` (WebSocket ${code}${reason ? `: ${reason}` : ''})` : '';\n const context: DisconnectContext = { ...(code === undefined ? {} : { code }), reason, suffix };\n\n const driver = driverFor(diagnostic.protocol);\n const specific = driver.describeDisconnect(context);\n if (specific) return specific;\n\n const name = driver.protocol.toUpperCase();\n if (/tcp (reached )?eof/i.test(reason)) {\n return `The ${name} host accepted the connection and then closed it${suffix}. `\n + 'Check its remote-access permissions and the account name.';\n }\n if (code === 1006) {\n return 'The connection was interrupted before a close reason arrived (WebSocket 1006). '\n + 'This can happen during a deployment or a brief network change. Automatic reconnect '\n + 'did not restore the session; reconnect once, then check Worker live logs if it repeats.';\n }\n if (reason) return `The remote session ended${suffix}.`;\n if (clean) return `The remote server closed the session cleanly${suffix || ' (no reason supplied)'}.`;\n return `The ${name} negotiation failed${suffix || ' before the server supplied a close reason'}.`;\n}\n"],
5
+ "mappings": "AAUA,SAAS,iBAAiB;AAC1B,SAAS,iBAAiB;AAMnB,MAAM,UAAkD;AAAA,EAC7D,KAAK;AAAA,EACL,KAAK;AACP;AAGO,SAAS,UAAU,UAA8C;AACtE,SAAO,QAAQ,QAA0B,KAAK,QAAQ;AACxD;AAeO,SAAS,kBACd,YACA,OACA,iBACQ;AAGR,MAAI,gBAAiB,QAAO;AAE5B,QAAM,OAAO,WAAW,OAAO;AAC/B,QAAM,SAAS,WAAW,OAAO,OAAO,KAAK,KAAK;AAClD,QAAM,SAAS,OAAO,eAAe,IAAI,GAAG,SAAS,KAAK,MAAM,KAAK,EAAE,MAAM;AAC7E,QAAM,UAA6B,EAAE,GAAI,SAAS,SAAY,CAAC,IAAI,EAAE,KAAK,GAAI,QAAQ,OAAO;AAE7F,QAAM,SAAS,UAAU,WAAW,QAAQ;AAC5C,QAAM,WAAW,OAAO,mBAAmB,OAAO;AAClD,MAAI,SAAU,QAAO;AAErB,QAAM,OAAO,OAAO,SAAS,YAAY;AACzC,MAAI,sBAAsB,KAAK,MAAM,GAAG;AACtC,WAAO,OAAO,IAAI,mDAAmD,MAAM;AAAA,EAE7E;AACA,MAAI,SAAS,MAAM;AACjB,WAAO;AAAA,EAGT;AACA,MAAI,OAAQ,QAAO,2BAA2B,MAAM;AACpD,MAAI,MAAO,QAAO,+CAA+C,UAAU,uBAAuB;AAClG,SAAO,OAAO,IAAI,sBAAsB,UAAU,4CAA4C;AAChG;",
6
+ "names": []
7
+ }
@@ -0,0 +1,170 @@
1
+ function allocate(length) {
2
+ return new Uint8Array(length);
3
+ }
4
+ class Writer {
5
+ bytes;
6
+ view;
7
+ offset = 0;
8
+ constructor(capacity = 512) {
9
+ this.bytes = allocate(capacity);
10
+ this.view = new DataView(this.bytes.buffer);
11
+ }
12
+ #ensure(extra) {
13
+ if (this.offset + extra <= this.bytes.length) return;
14
+ let capacity = this.bytes.length * 2;
15
+ while (capacity < this.offset + extra) capacity *= 2;
16
+ const grown = allocate(capacity);
17
+ grown.set(this.bytes.subarray(0, this.offset));
18
+ this.bytes = grown;
19
+ this.view = new DataView(grown.buffer);
20
+ }
21
+ u8(value) {
22
+ this.#ensure(1);
23
+ this.view.setUint8(this.offset, value);
24
+ this.offset += 1;
25
+ return this;
26
+ }
27
+ u16le(value) {
28
+ this.#ensure(2);
29
+ this.view.setUint16(this.offset, value, true);
30
+ this.offset += 2;
31
+ return this;
32
+ }
33
+ u16be(value) {
34
+ this.#ensure(2);
35
+ this.view.setUint16(this.offset, value, false);
36
+ this.offset += 2;
37
+ return this;
38
+ }
39
+ u32le(value) {
40
+ this.#ensure(4);
41
+ this.view.setUint32(this.offset, value >>> 0, true);
42
+ this.offset += 4;
43
+ return this;
44
+ }
45
+ u32be(value) {
46
+ this.#ensure(4);
47
+ this.view.setUint32(this.offset, value >>> 0, false);
48
+ this.offset += 4;
49
+ return this;
50
+ }
51
+ raw(bytes) {
52
+ this.#ensure(bytes.length);
53
+ this.bytes.set(bytes, this.offset);
54
+ this.offset += bytes.length;
55
+ return this;
56
+ }
57
+ zeros(count) {
58
+ this.#ensure(count);
59
+ this.offset += count;
60
+ return this;
61
+ }
62
+ /** UTF-16LE, optionally padded or truncated to a fixed byte width. */
63
+ utf16le(text, fixedBytes) {
64
+ const start = this.offset;
65
+ for (const unit of text) {
66
+ const code = unit.codePointAt(0) ?? 0;
67
+ if (fixedBytes !== void 0 && this.offset - start >= fixedBytes - 2) break;
68
+ if (code > 65535) {
69
+ for (let i = 0; i < unit.length; i++) this.u16le(unit.charCodeAt(i));
70
+ } else {
71
+ this.u16le(code);
72
+ }
73
+ }
74
+ if (fixedBytes !== void 0) {
75
+ this.zeros(fixedBytes - (this.offset - start));
76
+ }
77
+ return this;
78
+ }
79
+ /** Overwrite an earlier 16-bit slot, for length fields known only later. */
80
+ patchU16le(position, value) {
81
+ this.view.setUint16(position, value, true);
82
+ return this;
83
+ }
84
+ patchU16be(position, value) {
85
+ this.view.setUint16(position, value, false);
86
+ return this;
87
+ }
88
+ get length() {
89
+ return this.offset;
90
+ }
91
+ take() {
92
+ return this.bytes.slice(0, this.offset);
93
+ }
94
+ }
95
+ class Reader {
96
+ bytes;
97
+ view;
98
+ offset;
99
+ constructor(bytes, offset = 0) {
100
+ this.bytes = bytes;
101
+ this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
102
+ this.offset = offset;
103
+ }
104
+ get remaining() {
105
+ return this.bytes.length - this.offset;
106
+ }
107
+ #need(count) {
108
+ if (this.remaining < count) {
109
+ throw new Error(`RDP: truncated frame (needed ${count} bytes, ${this.remaining} left)`);
110
+ }
111
+ }
112
+ u8() {
113
+ this.#need(1);
114
+ return this.view.getUint8(this.offset++);
115
+ }
116
+ u16le() {
117
+ this.#need(2);
118
+ const value = this.view.getUint16(this.offset, true);
119
+ this.offset += 2;
120
+ return value;
121
+ }
122
+ u16be() {
123
+ this.#need(2);
124
+ const value = this.view.getUint16(this.offset, false);
125
+ this.offset += 2;
126
+ return value;
127
+ }
128
+ u32le() {
129
+ this.#need(4);
130
+ const value = this.view.getUint32(this.offset, true);
131
+ this.offset += 4;
132
+ return value;
133
+ }
134
+ u32be() {
135
+ this.#need(4);
136
+ const value = this.view.getUint32(this.offset, false);
137
+ this.offset += 4;
138
+ return value;
139
+ }
140
+ raw(count) {
141
+ this.#need(count);
142
+ const slice = this.bytes.subarray(this.offset, this.offset + count);
143
+ this.offset += count;
144
+ return slice;
145
+ }
146
+ skip(count) {
147
+ this.#need(count);
148
+ this.offset += count;
149
+ return this;
150
+ }
151
+ rest() {
152
+ return this.bytes.subarray(this.offset);
153
+ }
154
+ }
155
+ function concatBytes(...parts) {
156
+ const total = parts.reduce((sum, part) => sum + part.length, 0);
157
+ const out = allocate(total);
158
+ let offset = 0;
159
+ for (const part of parts) {
160
+ out.set(part, offset);
161
+ offset += part.length;
162
+ }
163
+ return out;
164
+ }
165
+ export {
166
+ Reader,
167
+ Writer,
168
+ concatBytes
169
+ };
170
+ //# sourceMappingURL=buffer.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/protocols/rdp/buffer.ts"],
4
+ "sourcesContent": ["// Binary reader/writer used by every RDP layer. RDP mixes endianness freely --\n// TPKT and MCS are big endian, everything above them is little endian -- so the\n// byte order is part of each call rather than a mode on the object.\n\n/**\n * Plain `Uint8Array` throughout. Pinning the buffer type parameter would be\n * more precise, but every layer above this one passes byte slices around and\n * the variance between `ArrayBuffer` and `ArrayBufferLike` then has to be\n * restated at each boundary for no benefit the protocol code can use.\n */\nfunction allocate(length: number): Uint8Array {\n return new Uint8Array(length);\n}\n\nexport class Writer {\n private bytes: Uint8Array;\n private view: DataView;\n private offset = 0;\n\n constructor(capacity = 512) {\n this.bytes = allocate(capacity);\n this.view = new DataView(this.bytes.buffer);\n }\n\n #ensure(extra: number): void {\n if (this.offset + extra <= this.bytes.length) return;\n let capacity = this.bytes.length * 2;\n while (capacity < this.offset + extra) capacity *= 2;\n const grown = allocate(capacity);\n grown.set(this.bytes.subarray(0, this.offset));\n this.bytes = grown;\n this.view = new DataView(grown.buffer);\n }\n\n u8(value: number): this {\n this.#ensure(1);\n this.view.setUint8(this.offset, value);\n this.offset += 1;\n return this;\n }\n\n u16le(value: number): this {\n this.#ensure(2);\n this.view.setUint16(this.offset, value, true);\n this.offset += 2;\n return this;\n }\n\n u16be(value: number): this {\n this.#ensure(2);\n this.view.setUint16(this.offset, value, false);\n this.offset += 2;\n return this;\n }\n\n u32le(value: number): this {\n this.#ensure(4);\n this.view.setUint32(this.offset, value >>> 0, true);\n this.offset += 4;\n return this;\n }\n\n u32be(value: number): this {\n this.#ensure(4);\n this.view.setUint32(this.offset, value >>> 0, false);\n this.offset += 4;\n return this;\n }\n\n raw(bytes: Uint8Array): this {\n this.#ensure(bytes.length);\n this.bytes.set(bytes, this.offset);\n this.offset += bytes.length;\n return this;\n }\n\n zeros(count: number): this {\n this.#ensure(count);\n this.offset += count;\n return this;\n }\n\n /** UTF-16LE, optionally padded or truncated to a fixed byte width. */\n utf16le(text: string, fixedBytes?: number): this {\n const start = this.offset;\n for (const unit of text) {\n const code = unit.codePointAt(0) ?? 0;\n // Fixed-width fields must keep room for the terminating NUL.\n if (fixedBytes !== undefined && this.offset - start >= fixedBytes - 2) break;\n if (code > 0xffff) {\n // Surrogate pair: both halves are already what UTF-16 needs.\n for (let i = 0; i < unit.length; i++) this.u16le(unit.charCodeAt(i));\n } else {\n this.u16le(code);\n }\n }\n if (fixedBytes !== undefined) {\n this.zeros(fixedBytes - (this.offset - start));\n }\n return this;\n }\n\n /** Overwrite an earlier 16-bit slot, for length fields known only later. */\n patchU16le(position: number, value: number): this {\n this.view.setUint16(position, value, true);\n return this;\n }\n\n patchU16be(position: number, value: number): this {\n this.view.setUint16(position, value, false);\n return this;\n }\n\n get length(): number {\n return this.offset;\n }\n\n take(): Uint8Array {\n return this.bytes.slice(0, this.offset);\n }\n}\n\nexport class Reader {\n private readonly bytes: Uint8Array;\n private readonly view: DataView;\n offset: number;\n\n constructor(bytes: Uint8Array, offset = 0) {\n this.bytes = bytes;\n this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n this.offset = offset;\n }\n\n get remaining(): number {\n return this.bytes.length - this.offset;\n }\n\n #need(count: number): void {\n if (this.remaining < count) {\n throw new Error(`RDP: truncated frame (needed ${count} bytes, ${this.remaining} left)`);\n }\n }\n\n u8(): number {\n this.#need(1);\n return this.view.getUint8(this.offset++);\n }\n\n u16le(): number {\n this.#need(2);\n const value = this.view.getUint16(this.offset, true);\n this.offset += 2;\n return value;\n }\n\n u16be(): number {\n this.#need(2);\n const value = this.view.getUint16(this.offset, false);\n this.offset += 2;\n return value;\n }\n\n u32le(): number {\n this.#need(4);\n const value = this.view.getUint32(this.offset, true);\n this.offset += 4;\n return value;\n }\n\n u32be(): number {\n this.#need(4);\n const value = this.view.getUint32(this.offset, false);\n this.offset += 4;\n return value;\n }\n\n raw(count: number): Uint8Array {\n this.#need(count);\n const slice = this.bytes.subarray(this.offset, this.offset + count);\n this.offset += count;\n return slice;\n }\n\n skip(count: number): this {\n this.#need(count);\n this.offset += count;\n return this;\n }\n\n rest(): Uint8Array {\n return this.bytes.subarray(this.offset);\n }\n}\n\nexport function concatBytes(...parts: readonly Uint8Array[]): Uint8Array {\n const total = parts.reduce((sum, part) => sum + part.length, 0);\n const out = allocate(total);\n let offset = 0;\n for (const part of parts) {\n out.set(part, offset);\n offset += part.length;\n }\n return out;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,260 @@
1
+ import { Reader, Writer, concatBytes } from "./buffer";
2
+ const CAPSET = {
3
+ GENERAL: 1,
4
+ BITMAP: 2,
5
+ ORDER: 3,
6
+ BITMAPCACHE: 4,
7
+ CONTROL: 5,
8
+ ACTIVATION: 7,
9
+ POINTER: 8,
10
+ SHARE: 9,
11
+ COLORCACHE: 10,
12
+ SOUND: 12,
13
+ INPUT: 13,
14
+ FONT: 14,
15
+ BRUSH: 15,
16
+ GLYPHCACHE: 16,
17
+ OFFSCREENCACHE: 17,
18
+ VIRTUALCHANNEL: 20,
19
+ RAIL: 23,
20
+ WINDOW: 24,
21
+ MULTIFRAGMENTUPDATE: 26,
22
+ LARGE_POINTER: 27,
23
+ SURFACE_COMMANDS: 28,
24
+ BITMAP_CODECS: 29
25
+ };
26
+ const INPUT_FLAG_SCANCODES = 1;
27
+ const INPUT_FLAG_MOUSEX = 4;
28
+ const INPUT_FLAG_UNICODE = 16;
29
+ const FASTPATH_OUTPUT_SUPPORTED = 1;
30
+ const LONG_CREDENTIALS_SUPPORTED = 4;
31
+ const NEGOTIATE_ORDER_SUPPORT = 2;
32
+ const ZERO_BOUNDS_DELTA_SUPPORT = 8;
33
+ function capabilitySet(type, body) {
34
+ const writer = new Writer(body.length + 4);
35
+ writer.u16le(type).u16le(body.length + 4).raw(body);
36
+ return writer.take();
37
+ }
38
+ function generalCapability() {
39
+ const writer = new Writer(24);
40
+ writer.u16le(1);
41
+ writer.u16le(3);
42
+ writer.u16le(512);
43
+ writer.u16le(0);
44
+ writer.u16le(0);
45
+ writer.u16le(FASTPATH_OUTPUT_SUPPORTED | LONG_CREDENTIALS_SUPPORTED);
46
+ writer.u16le(0);
47
+ writer.u16le(0);
48
+ writer.u16le(0);
49
+ writer.u8(1);
50
+ writer.u8(1);
51
+ return capabilitySet(CAPSET.GENERAL, writer.take());
52
+ }
53
+ function bitmapCapability(width, height) {
54
+ const writer = new Writer(28);
55
+ writer.u16le(16);
56
+ writer.u16le(1);
57
+ writer.u16le(1);
58
+ writer.u16le(1);
59
+ writer.u16le(width);
60
+ writer.u16le(height);
61
+ writer.u16le(0);
62
+ writer.u16le(1);
63
+ writer.u16le(1);
64
+ writer.u8(0);
65
+ writer.u8(0);
66
+ writer.u16le(1);
67
+ writer.u16le(0);
68
+ return capabilitySet(CAPSET.BITMAP, writer.take());
69
+ }
70
+ function orderCapability() {
71
+ const writer = new Writer(88);
72
+ writer.zeros(16);
73
+ writer.u32le(0);
74
+ writer.u16le(1);
75
+ writer.u16le(20);
76
+ writer.u16le(0);
77
+ writer.u16le(1);
78
+ writer.u16le(0);
79
+ writer.u16le(NEGOTIATE_ORDER_SUPPORT | ZERO_BOUNDS_DELTA_SUPPORT);
80
+ writer.zeros(32);
81
+ writer.u16le(0);
82
+ writer.u16le(0);
83
+ writer.u32le(0);
84
+ writer.u32le(230400);
85
+ writer.u16le(0);
86
+ writer.u16le(0);
87
+ writer.u16le(0);
88
+ writer.u16le(0);
89
+ return capabilitySet(CAPSET.ORDER, writer.take());
90
+ }
91
+ function bitmapCacheCapability() {
92
+ const writer = new Writer(40);
93
+ writer.zeros(24);
94
+ writer.u16le(0).u16le(0);
95
+ writer.u16le(0).u16le(0);
96
+ writer.u16le(0).u16le(0);
97
+ return capabilitySet(CAPSET.BITMAPCACHE, writer.take());
98
+ }
99
+ function controlCapability() {
100
+ const writer = new Writer(12);
101
+ writer.u16le(0);
102
+ writer.u16le(0);
103
+ writer.u16le(2);
104
+ writer.u16le(2);
105
+ return capabilitySet(CAPSET.CONTROL, writer.take());
106
+ }
107
+ function activationCapability() {
108
+ const writer = new Writer(12);
109
+ writer.u16le(0).u16le(0).u16le(0).u16le(0);
110
+ return capabilitySet(CAPSET.ACTIVATION, writer.take());
111
+ }
112
+ function pointerCapability() {
113
+ const writer = new Writer(10);
114
+ writer.u16le(1);
115
+ writer.u16le(20);
116
+ writer.u16le(20);
117
+ return capabilitySet(CAPSET.POINTER, writer.take());
118
+ }
119
+ function shareCapability() {
120
+ const writer = new Writer(8);
121
+ writer.u16le(0).u16le(0);
122
+ return capabilitySet(CAPSET.SHARE, writer.take());
123
+ }
124
+ function colorCacheCapability() {
125
+ const writer = new Writer(8);
126
+ writer.u16le(6).u16le(0);
127
+ return capabilitySet(CAPSET.COLORCACHE, writer.take());
128
+ }
129
+ function soundCapability() {
130
+ const writer = new Writer(8);
131
+ writer.u16le(0).u16le(0);
132
+ return capabilitySet(CAPSET.SOUND, writer.take());
133
+ }
134
+ function inputCapability(keyboardLayout) {
135
+ const writer = new Writer(88);
136
+ writer.u16le(INPUT_FLAG_SCANCODES | INPUT_FLAG_MOUSEX | INPUT_FLAG_UNICODE);
137
+ writer.u16le(0);
138
+ writer.u32le(keyboardLayout);
139
+ writer.u32le(4);
140
+ writer.u32le(0);
141
+ writer.u32le(12);
142
+ writer.zeros(64);
143
+ return capabilitySet(CAPSET.INPUT, writer.take());
144
+ }
145
+ function fontCapability() {
146
+ const writer = new Writer(8);
147
+ writer.u16le(1).u16le(0);
148
+ return capabilitySet(CAPSET.FONT, writer.take());
149
+ }
150
+ function brushCapability() {
151
+ const writer = new Writer(8);
152
+ writer.u32le(0);
153
+ return capabilitySet(CAPSET.BRUSH, writer.take());
154
+ }
155
+ function glyphCacheCapability() {
156
+ const writer = new Writer(52);
157
+ for (let i = 0; i < 10; i++) writer.u16le(0).u16le(0);
158
+ writer.u32le(0);
159
+ writer.u16le(0);
160
+ writer.u16le(0);
161
+ return capabilitySet(CAPSET.GLYPHCACHE, writer.take());
162
+ }
163
+ function offscreenCacheCapability() {
164
+ const writer = new Writer(12);
165
+ writer.u32le(0);
166
+ writer.u16le(0);
167
+ writer.u16le(0);
168
+ return capabilitySet(CAPSET.OFFSCREENCACHE, writer.take());
169
+ }
170
+ function remoteProgramsCapability() {
171
+ const writer = new Writer(4);
172
+ writer.u32le(1);
173
+ return capabilitySet(CAPSET.RAIL, writer.take());
174
+ }
175
+ function windowListCapability(iconCaches = 0, iconCacheEntries = 0) {
176
+ const writer = new Writer(7);
177
+ writer.u32le(1);
178
+ writer.u8(iconCaches);
179
+ writer.u16le(iconCacheEntries);
180
+ return capabilitySet(CAPSET.WINDOW, writer.take());
181
+ }
182
+ function buildClientCapabilities({ width, height, keyboardLayout, remoteApp = false, remoteAppCaps = null }) {
183
+ const sets = [
184
+ generalCapability(),
185
+ bitmapCapability(width, height),
186
+ orderCapability(),
187
+ bitmapCacheCapability(),
188
+ colorCacheCapability(),
189
+ activationCapability(),
190
+ controlCapability(),
191
+ pointerCapability(),
192
+ shareCapability(),
193
+ inputCapability(keyboardLayout),
194
+ brushCapability(),
195
+ glyphCacheCapability(),
196
+ soundCapability(),
197
+ fontCapability(),
198
+ offscreenCacheCapability()
199
+ ];
200
+ if (remoteApp) {
201
+ sets.push(remoteProgramsCapability());
202
+ sets.push(windowListCapability(
203
+ remoteAppCaps?.numIconCaches || 0,
204
+ remoteAppCaps?.numIconCacheEntries || 0
205
+ ));
206
+ }
207
+ return { bytes: concatBytes(...sets), count: sets.length };
208
+ }
209
+ function parseServerCapabilities(bytes) {
210
+ const reader = new Reader(bytes);
211
+ const numberCapabilities = reader.u16le();
212
+ reader.u16le();
213
+ const result = {
214
+ desktopWidth: 0,
215
+ desktopHeight: 0,
216
+ preferredBitsPerPixel: 16,
217
+ refreshRectSupport: false,
218
+ railSupportLevel: 0,
219
+ windowSupportLevel: 0,
220
+ numIconCaches: 0,
221
+ numIconCacheEntries: 0,
222
+ types: []
223
+ };
224
+ for (let i = 0; i < numberCapabilities && reader.remaining >= 4; i++) {
225
+ const type = reader.u16le();
226
+ const length = reader.u16le();
227
+ if (length < 4 || length - 4 > reader.remaining) break;
228
+ const body = new Reader(reader.raw(length - 4));
229
+ result.types.push(type);
230
+ if (type === CAPSET.BITMAP && body.remaining >= 20) {
231
+ result.preferredBitsPerPixel = body.u16le();
232
+ body.skip(6);
233
+ result.desktopWidth = body.u16le();
234
+ result.desktopHeight = body.u16le();
235
+ continue;
236
+ }
237
+ if (type === CAPSET.GENERAL && body.remaining >= 20) {
238
+ body.skip(18);
239
+ result.refreshRectSupport = body.u8() !== 0;
240
+ continue;
241
+ }
242
+ if (type === CAPSET.RAIL && body.remaining >= 4) {
243
+ result.railSupportLevel = body.u32le();
244
+ continue;
245
+ }
246
+ if (type === CAPSET.WINDOW && body.remaining >= 7) {
247
+ result.windowSupportLevel = body.u32le();
248
+ result.numIconCaches = body.u8();
249
+ result.numIconCacheEntries = body.u16le();
250
+ continue;
251
+ }
252
+ }
253
+ return result;
254
+ }
255
+ export {
256
+ CAPSET,
257
+ buildClientCapabilities,
258
+ parseServerCapabilities
259
+ };
260
+ //# sourceMappingURL=caps.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/protocols/rdp/caps.ts"],
4
+ "sourcesContent": ["// Capability exchange (MS-RDPBCGR 2.2.7).\n//\n// This client deliberately advertises no drawing-order support and no bitmap\n// cache, which makes the host fall back to plain bitmap updates for the whole\n// screen. That is the one output path a from-scratch client can implement\n// correctly, and it is what keeps the rest of this module small.\n\nimport { Reader, Writer, concatBytes } from './buffer';\n\nexport const CAPSET = {\n GENERAL: 1,\n BITMAP: 2,\n ORDER: 3,\n BITMAPCACHE: 4,\n CONTROL: 5,\n ACTIVATION: 7,\n POINTER: 8,\n SHARE: 9,\n COLORCACHE: 10,\n SOUND: 12,\n INPUT: 13,\n FONT: 14,\n BRUSH: 15,\n GLYPHCACHE: 16,\n OFFSCREENCACHE: 17,\n VIRTUALCHANNEL: 20,\n RAIL: 23,\n WINDOW: 24,\n MULTIFRAGMENTUPDATE: 26,\n LARGE_POINTER: 27,\n SURFACE_COMMANDS: 28,\n BITMAP_CODECS: 29,\n};\n\nconst INPUT_FLAG_SCANCODES = 0x0001;\nconst INPUT_FLAG_MOUSEX = 0x0004;\nconst INPUT_FLAG_UNICODE = 0x0010;\n\nconst FASTPATH_OUTPUT_SUPPORTED = 0x0001;\nconst LONG_CREDENTIALS_SUPPORTED = 0x0004;\nconst NEGOTIATE_ORDER_SUPPORT = 0x0002;\nconst ZERO_BOUNDS_DELTA_SUPPORT = 0x0008;\n\nfunction capabilitySet(type: number, body: Uint8Array) {\n const writer = new Writer(body.length + 4);\n writer.u16le(type).u16le(body.length + 4).raw(body);\n return writer.take();\n}\n\nfunction generalCapability() {\n const writer = new Writer(24);\n writer.u16le(1); // osMajorType: Windows\n writer.u16le(3); // osMinorType: Windows NT\n writer.u16le(0x0200); // protocolVersion\n writer.u16le(0); // pad\n writer.u16le(0); // generalCompressionTypes\n // Fast-path output has to be advertised. Windows 8 and later send graphics\n // only on the fast path, so a client that asks for the slow path alone gets a\n // clean session that never paints anything.\n writer.u16le(FASTPATH_OUTPUT_SUPPORTED | LONG_CREDENTIALS_SUPPORTED); // extraFlags\n writer.u16le(0); // updateCapabilityFlag\n writer.u16le(0); // remoteUnshareFlag\n writer.u16le(0); // generalCompressionLevel\n // Both are declarations that the client may SEND these PDUs. The initial\n // full-screen paint depends on it: a host that thinks the client cannot ask\n // for a refresh has no reason to honour the request.\n writer.u8(1); // refreshRectSupport\n writer.u8(1); // suppressOutputSupport\n return capabilitySet(CAPSET.GENERAL, writer.take());\n}\n\nfunction bitmapCapability(width: number, height: number) {\n const writer = new Writer(28);\n writer.u16le(16); // preferredBitsPerPixel\n writer.u16le(1); // receive1BitPerPixel\n writer.u16le(1); // receive4BitsPerPixel\n writer.u16le(1); // receive8BitsPerPixel\n writer.u16le(width);\n writer.u16le(height);\n writer.u16le(0); // pad\n writer.u16le(1); // desktopResizeFlag\n writer.u16le(1); // bitmapCompressionFlag\n writer.u8(0); // highColorFlags\n writer.u8(0); // drawingFlags\n writer.u16le(1); // multipleRectangleSupport\n writer.u16le(0); // pad\n return capabilitySet(CAPSET.BITMAP, writer.take());\n}\n\nfunction orderCapability() {\n const writer = new Writer(88);\n writer.zeros(16); // terminalDescriptor\n writer.u32le(0); // pad\n writer.u16le(1); // desktopSaveXGranularity\n writer.u16le(20); // desktopSaveYGranularity\n writer.u16le(0); // pad\n writer.u16le(1); // maximumOrderLevel\n writer.u16le(0); // numberFonts\n writer.u16le(NEGOTIATE_ORDER_SUPPORT | ZERO_BOUNDS_DELTA_SUPPORT);\n writer.zeros(32); // orderSupport: none\n writer.u16le(0); // textFlags\n writer.u16le(0); // orderSupportExFlags\n writer.u32le(0); // pad\n writer.u32le(230400); // desktopSaveSize\n writer.u16le(0); // pad\n writer.u16le(0); // pad\n writer.u16le(0); // textANSICodePage\n writer.u16le(0); // pad\n return capabilitySet(CAPSET.ORDER, writer.take());\n}\n\n/** Zero entries in every cache: the host must send bitmaps, never cache hits. */\nfunction bitmapCacheCapability() {\n const writer = new Writer(40);\n writer.zeros(24); // pad1..pad6\n writer.u16le(0).u16le(0);\n writer.u16le(0).u16le(0);\n writer.u16le(0).u16le(0);\n return capabilitySet(CAPSET.BITMAPCACHE, writer.take());\n}\n\nfunction controlCapability() {\n const writer = new Writer(12);\n writer.u16le(0); // controlFlags\n writer.u16le(0); // remoteDetachFlag\n writer.u16le(2); // controlInterest: never\n writer.u16le(2); // detachInterest: never\n return capabilitySet(CAPSET.CONTROL, writer.take());\n}\n\nfunction activationCapability() {\n const writer = new Writer(12);\n writer.u16le(0).u16le(0).u16le(0).u16le(0);\n return capabilitySet(CAPSET.ACTIVATION, writer.take());\n}\n\nfunction pointerCapability() {\n const writer = new Writer(10);\n writer.u16le(1); // colorPointerFlag\n writer.u16le(20); // colorPointerCacheSize\n writer.u16le(20); // pointerCacheSize\n return capabilitySet(CAPSET.POINTER, writer.take());\n}\n\nfunction shareCapability() {\n const writer = new Writer(8);\n writer.u16le(0).u16le(0);\n return capabilitySet(CAPSET.SHARE, writer.take());\n}\n\nfunction colorCacheCapability() {\n const writer = new Writer(8);\n writer.u16le(6).u16le(0);\n return capabilitySet(CAPSET.COLORCACHE, writer.take());\n}\n\nfunction soundCapability() {\n const writer = new Writer(8);\n writer.u16le(0).u16le(0);\n return capabilitySet(CAPSET.SOUND, writer.take());\n}\n\nfunction inputCapability(keyboardLayout: number) {\n const writer = new Writer(88);\n writer.u16le(INPUT_FLAG_SCANCODES | INPUT_FLAG_MOUSEX | INPUT_FLAG_UNICODE);\n writer.u16le(0); // pad\n writer.u32le(keyboardLayout);\n writer.u32le(4); // keyboardType: IBM enhanced\n writer.u32le(0); // keyboardSubType\n writer.u32le(12); // keyboardFunctionKey\n writer.zeros(64); // imeFileName\n return capabilitySet(CAPSET.INPUT, writer.take());\n}\n\nfunction fontCapability() {\n const writer = new Writer(8);\n writer.u16le(1).u16le(0);\n return capabilitySet(CAPSET.FONT, writer.take());\n}\n\nfunction brushCapability() {\n const writer = new Writer(8);\n writer.u32le(0); // BRUSH_DEFAULT\n return capabilitySet(CAPSET.BRUSH, writer.take());\n}\n\nfunction glyphCacheCapability() {\n const writer = new Writer(52);\n for (let i = 0; i < 10; i++) writer.u16le(0).u16le(0); // GlyphCache entries\n writer.u32le(0); // FragCache\n writer.u16le(0); // GLYPH_SUPPORT_NONE\n writer.u16le(0); // pad\n return capabilitySet(CAPSET.GLYPHCACHE, writer.take());\n}\n\nfunction offscreenCacheCapability() {\n const writer = new Writer(12);\n writer.u32le(0); // offscreenSupportLevel: none\n writer.u16le(0); // offscreenCacheSize\n writer.u16le(0); // offscreenCacheEntries\n return capabilitySet(CAPSET.OFFSCREENCACHE, writer.take());\n}\n\nfunction remoteProgramsCapability() {\n const writer = new Writer(4);\n writer.u32le(0x00000001); // TS_RAIL_LEVEL_SUPPORTED\n return capabilitySet(CAPSET.RAIL, writer.take());\n}\n\nfunction windowListCapability(iconCaches = 0, iconCacheEntries = 0) {\n const writer = new Writer(7);\n writer.u32le(0x00000001); // TS_WINDOW_LEVEL_SUPPORTED\n writer.u8(iconCaches);\n writer.u16le(iconCacheEntries);\n return capabilitySet(CAPSET.WINDOW, writer.take());\n}\n\nexport function buildClientCapabilities({ width, height, keyboardLayout, remoteApp = false, remoteAppCaps = null }) {\n const sets = [\n generalCapability(),\n bitmapCapability(width, height),\n orderCapability(),\n bitmapCacheCapability(),\n colorCacheCapability(),\n activationCapability(),\n controlCapability(),\n pointerCapability(),\n shareCapability(),\n inputCapability(keyboardLayout),\n brushCapability(),\n glyphCacheCapability(),\n soundCapability(),\n fontCapability(),\n offscreenCacheCapability(),\n ];\n\n if (remoteApp) {\n sets.push(remoteProgramsCapability());\n sets.push(windowListCapability(\n remoteAppCaps?.numIconCaches || 0,\n remoteAppCaps?.numIconCacheEntries || 0\n ));\n }\n\n return { bytes: concatBytes(...sets), count: sets.length };\n}\n\n/**\n * Read the host's Demand Active capabilities. The desktop size it reports wins\n * over anything the client asked for.\n */\nexport function parseServerCapabilities(bytes: Uint8Array) {\n const reader = new Reader(bytes);\n const numberCapabilities = reader.u16le();\n reader.u16le(); // pad\n\n const result = {\n desktopWidth: 0,\n desktopHeight: 0,\n preferredBitsPerPixel: 16,\n refreshRectSupport: false,\n railSupportLevel: 0,\n windowSupportLevel: 0,\n numIconCaches: 0,\n numIconCacheEntries: 0,\n types: [],\n };\n\n for (let i = 0; i < numberCapabilities && reader.remaining >= 4; i++) {\n const type = reader.u16le();\n const length = reader.u16le();\n if (length < 4 || length - 4 > reader.remaining) break;\n const body = new Reader(reader.raw(length - 4));\n result.types.push(type);\n\n if (type === CAPSET.BITMAP && body.remaining >= 20) {\n result.preferredBitsPerPixel = body.u16le();\n body.skip(6); // receive1/4/8BitsPerPixel\n result.desktopWidth = body.u16le();\n result.desktopHeight = body.u16le();\n continue;\n }\n\n if (type === CAPSET.GENERAL && body.remaining >= 20) {\n body.skip(18);\n result.refreshRectSupport = body.u8() !== 0;\n continue;\n }\n\n if (type === CAPSET.RAIL && body.remaining >= 4) {\n result.railSupportLevel = body.u32le();\n continue;\n }\n\n if (type === CAPSET.WINDOW && body.remaining >= 7) {\n result.windowSupportLevel = body.u32le();\n result.numIconCaches = body.u8();\n result.numIconCacheEntries = body.u16le();\n continue;\n }\n }\n\n return result;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }