ai-remote 0.4.2 → 0.4.3

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 (62) hide show
  1. package/dist/index.js +1 -6
  2. package/dist/protocols/index.js +3 -34
  3. package/dist/protocols/ssh/session.js +17 -608
  4. package/dist/protocols/ssh/terminal.js +6 -190
  5. package/dist/shared/connection.js +1 -113
  6. package/dist/shared/hosts.js +1 -132
  7. package/dist/shared/icons.js +1 -41
  8. package/dist/shared/protocol.js +1 -39
  9. package/dist/shared/signals.js +1 -41
  10. package/package.json +1 -1
  11. package/dist/protocols/rdp/buffer.d.ts +0 -38
  12. package/dist/protocols/rdp/buffer.js +0 -169
  13. package/dist/protocols/rdp/caps.d.ts +0 -49
  14. package/dist/protocols/rdp/caps.js +0 -259
  15. package/dist/protocols/rdp/cert.d.ts +0 -18
  16. package/dist/protocols/rdp/cert.js +0 -134
  17. package/dist/protocols/rdp/client.d.ts +0 -56
  18. package/dist/protocols/rdp/client.js +0 -1069
  19. package/dist/protocols/rdp/cliprdr.d.ts +0 -27
  20. package/dist/protocols/rdp/cliprdr.js +0 -239
  21. package/dist/protocols/rdp/credssp.d.ts +0 -34
  22. package/dist/protocols/rdp/credssp.js +0 -253
  23. package/dist/protocols/rdp/crypto.d.ts +0 -63
  24. package/dist/protocols/rdp/crypto.js +0 -368
  25. package/dist/protocols/rdp/display.d.ts +0 -38
  26. package/dist/protocols/rdp/display.js +0 -588
  27. package/dist/protocols/rdp/gcc.d.ts +0 -23
  28. package/dist/protocols/rdp/gcc.js +0 -131
  29. package/dist/protocols/rdp/keymap.d.ts +0 -9
  30. package/dist/protocols/rdp/keymap.js +0 -131
  31. package/dist/protocols/rdp/mcs.d.ts +0 -40
  32. package/dist/protocols/rdp/mcs.js +0 -222
  33. package/dist/protocols/rdp/ntlm.d.ts +0 -61
  34. package/dist/protocols/rdp/ntlm.js +0 -304
  35. package/dist/protocols/rdp/pdu.d.ts +0 -155
  36. package/dist/protocols/rdp/pdu.js +0 -443
  37. package/dist/protocols/rdp/rail.d.ts +0 -119
  38. package/dist/protocols/rdp/rail.js +0 -382
  39. package/dist/protocols/rdp/rle.d.ts +0 -13
  40. package/dist/protocols/rdp/rle.js +0 -275
  41. package/dist/protocols/rdp/sec.d.ts +0 -59
  42. package/dist/protocols/rdp/sec.js +0 -155
  43. package/dist/protocols/rdp/session.d.ts +0 -62
  44. package/dist/protocols/rdp/session.js +0 -306
  45. package/dist/protocols/rdp/tls.d.ts +0 -18
  46. package/dist/protocols/rdp/tls.js +0 -353
  47. package/dist/protocols/rdp/vchannel.d.ts +0 -28
  48. package/dist/protocols/rdp/vchannel.js +0 -58
  49. package/dist/protocols/rdp/x224.d.ts +0 -40
  50. package/dist/protocols/rdp/x224.js +0 -109
  51. package/dist/protocols/ssh/kex.d.ts +0 -97
  52. package/dist/protocols/ssh/kex.js +0 -176
  53. package/dist/protocols/ssh/messages.d.ts +0 -50
  54. package/dist/protocols/ssh/messages.js +0 -54
  55. package/dist/protocols/ssh/transport.d.ts +0 -62
  56. package/dist/protocols/ssh/transport.js +0 -612
  57. package/dist/protocols/ssh/wire.d.ts +0 -52
  58. package/dist/protocols/ssh/wire.js +0 -188
  59. package/dist/protocols/vnc/input.d.ts +0 -5
  60. package/dist/protocols/vnc/input.js +0 -15
  61. package/dist/protocols/vnc/session.d.ts +0 -12
  62. package/dist/protocols/vnc/session.js +0 -258
@@ -1,188 +0,0 @@
1
- const textEncoder = new TextEncoder();
2
- const textDecoder = new TextDecoder();
3
- function encodeUtf8(text) {
4
- return textEncoder.encode(text);
5
- }
6
- function decodeUtf8(bytes) {
7
- return textDecoder.decode(bytes);
8
- }
9
- function concatBytes(...parts) {
10
- const total = parts.reduce((sum, part) => sum + part.length, 0);
11
- const out = new Uint8Array(total);
12
- let offset = 0;
13
- for (const part of parts) {
14
- out.set(part, offset);
15
- offset += part.length;
16
- }
17
- return out;
18
- }
19
- function bytesEqual(a, b) {
20
- if (a.length !== b.length) return false;
21
- let difference = 0;
22
- for (let index = 0; index < a.length; index++) difference |= (a[index] ?? 0) ^ (b[index] ?? 0);
23
- return difference === 0;
24
- }
25
- function toBase64(bytes) {
26
- let binary = "";
27
- for (const byte of bytes) binary += String.fromCharCode(byte);
28
- return btoa(binary);
29
- }
30
- function fromBase64(text) {
31
- const binary = atob(text);
32
- const bytes = new Uint8Array(binary.length);
33
- for (let index = 0; index < binary.length; index++) bytes[index] = binary.charCodeAt(index);
34
- return bytes;
35
- }
36
- function toBase64Url(bytes) {
37
- return toBase64(bytes).replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "");
38
- }
39
- class SshWriter {
40
- bytes;
41
- view;
42
- offset = 0;
43
- constructor(capacity = 256) {
44
- this.bytes = new Uint8Array(new ArrayBuffer(capacity));
45
- this.view = new DataView(this.bytes.buffer);
46
- }
47
- #ensure(extra) {
48
- if (this.offset + extra <= this.bytes.length) return;
49
- let capacity = Math.max(this.bytes.length * 2, 64);
50
- while (capacity < this.offset + extra) capacity *= 2;
51
- const grown = new Uint8Array(new ArrayBuffer(capacity));
52
- grown.set(this.bytes.subarray(0, this.offset));
53
- this.bytes = grown;
54
- this.view = new DataView(grown.buffer);
55
- }
56
- u8(value) {
57
- this.#ensure(1);
58
- this.view.setUint8(this.offset, value);
59
- this.offset += 1;
60
- return this;
61
- }
62
- boolean(value) {
63
- return this.u8(value ? 1 : 0);
64
- }
65
- u32(value) {
66
- this.#ensure(4);
67
- this.view.setUint32(this.offset, value >>> 0, false);
68
- this.offset += 4;
69
- return this;
70
- }
71
- raw(bytes) {
72
- this.#ensure(bytes.length);
73
- this.bytes.set(bytes, this.offset);
74
- this.offset += bytes.length;
75
- return this;
76
- }
77
- /** A length-prefixed string. Text is encoded as UTF-8. */
78
- string(value) {
79
- const bytes = typeof value === "string" ? encodeUtf8(value) : value;
80
- return this.u32(bytes.length).raw(bytes);
81
- }
82
- nameList(names) {
83
- return this.string(names.join(","));
84
- }
85
- /**
86
- * An mpint: two's complement, big endian, with no leading zero bytes except
87
- * the one that keeps a positive number from looking negative.
88
- */
89
- mpint(bytes) {
90
- let start = 0;
91
- while (start < bytes.length && bytes[start] === 0) start++;
92
- const magnitude = bytes.subarray(start);
93
- if (magnitude.length === 0) return this.u32(0);
94
- if ((magnitude[0] ?? 0) & 128) {
95
- this.u32(magnitude.length + 1).u8(0);
96
- return this.raw(magnitude);
97
- }
98
- return this.u32(magnitude.length).raw(magnitude);
99
- }
100
- get length() {
101
- return this.offset;
102
- }
103
- take() {
104
- return this.bytes.slice(0, this.offset);
105
- }
106
- }
107
- class SshReader {
108
- bytes;
109
- view;
110
- offset;
111
- constructor(bytes, offset = 0) {
112
- this.bytes = bytes;
113
- this.view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
114
- this.offset = offset;
115
- }
116
- get remaining() {
117
- return this.bytes.length - this.offset;
118
- }
119
- #need(count) {
120
- if (count < 0 || this.remaining < count) {
121
- throw new Error(`SSH: truncated packet (needed ${count} bytes, ${this.remaining} left)`);
122
- }
123
- }
124
- u8() {
125
- this.#need(1);
126
- return this.view.getUint8(this.offset++);
127
- }
128
- boolean() {
129
- return this.u8() !== 0;
130
- }
131
- u32() {
132
- this.#need(4);
133
- const value = this.view.getUint32(this.offset, false);
134
- this.offset += 4;
135
- return value;
136
- }
137
- raw(count) {
138
- this.#need(count);
139
- const slice = this.bytes.subarray(this.offset, this.offset + count);
140
- this.offset += count;
141
- return slice;
142
- }
143
- /** The bytes of a length-prefixed string. */
144
- stringBytes() {
145
- return this.raw(this.u32());
146
- }
147
- string() {
148
- return decodeUtf8(this.stringBytes());
149
- }
150
- nameList() {
151
- const value = this.string();
152
- return value ? value.split(",") : [];
153
- }
154
- /** An mpint's magnitude, with the sign byte removed. */
155
- mpint() {
156
- const bytes = this.stringBytes();
157
- let start = 0;
158
- while (start < bytes.length && bytes[start] === 0) start++;
159
- return bytes.subarray(start);
160
- }
161
- skip(count) {
162
- this.#need(count);
163
- this.offset += count;
164
- return this;
165
- }
166
- rest() {
167
- return this.bytes.subarray(this.offset);
168
- }
169
- }
170
- function padStart(bytes, width) {
171
- if (bytes.length === width) return bytes;
172
- if (bytes.length > width) return bytes.subarray(bytes.length - width);
173
- const out = new Uint8Array(width);
174
- out.set(bytes, width - bytes.length);
175
- return out;
176
- }
177
- export {
178
- SshReader,
179
- SshWriter,
180
- bytesEqual,
181
- concatBytes,
182
- decodeUtf8,
183
- encodeUtf8,
184
- fromBase64,
185
- padStart,
186
- toBase64,
187
- toBase64Url
188
- };
@@ -1,5 +0,0 @@
1
- /** RFB wheel buttons for a bounded two-axis scroll, in wire order. */
2
- export declare function vncWheelMasks(delta: {
3
- x: number;
4
- y: number;
5
- }): number[];
@@ -1,15 +0,0 @@
1
- function vncWheelMasks(delta) {
2
- const masks = [];
3
- const wheels = [
4
- { amount: delta.y, negativeMask: 1 << 3, positiveMask: 1 << 4 },
5
- { amount: delta.x, negativeMask: 1 << 5, positiveMask: 1 << 6 }
6
- ];
7
- for (const { amount, negativeMask, positiveMask } of wheels) {
8
- const mask = amount < 0 ? negativeMask : positiveMask;
9
- for (let step = 0; step < Math.min(20, Math.abs(amount)); step++) masks.push(mask);
10
- }
11
- return masks;
12
- }
13
- export {
14
- vncWheelMasks
15
- };
@@ -1,12 +0,0 @@
1
- /**
2
- * Everything VNC-specific, behind the driver interface the RDP side also
3
- * implements. noVNC's RFB object does the protocol work; this module decides
4
- * how it is addressed, constructed, controlled and explained.
5
- *
6
- * VNC splits the handshake differently from RDP: noVNC hands authentication to
7
- * the Worker, because macOS Screen Sharing needs the Apple Diffie-Hellman
8
- * (type 30) exchange that noVNC does not implement. That is why credentials
9
- * travel in the URL here and never do for RDP.
10
- */
11
- import type { ProtocolDriver } from '../types';
12
- export declare const vncDriver: ProtocolDriver;
@@ -1,258 +0,0 @@
1
- import RFB from "@novnc/novnc";
2
- import { DEFAULT_PORTS } from "../../shared/protocol.js";
3
- import {
4
- GATEWAY_KEEPALIVE_SIGNAL,
5
- GATEWAY_READY_SIGNAL,
6
- KEEPALIVE_INTERVAL_MS
7
- } from "../../shared/signals.js";
8
- import { vncWheelMasks } from "./input.js";
9
- const DIALOG = {
10
- usernameLabel: "Username",
11
- usernamePlaceholder: "macOS username (optional)",
12
- usernameHelp: "Use your macOS account name. Leave it empty when the VNC server only asks for a password.",
13
- passwordLabel: "Password",
14
- passwordPlaceholder: "Screen sharing password",
15
- passwordHelp: "Use the macOS account password, or the password configured for VNC screen sharing.",
16
- // A VNC session needs some secret; RDP can sign in on the host's own screen.
17
- requiresPassword: true
18
- };
19
- function buildUrl(origin, settings) {
20
- const query = new URLSearchParams({ host: settings.host, port: String(settings.port) });
21
- if (settings.username) query.set("vnc_user", settings.username);
22
- if (settings.password) query.set("vnc_pass", settings.password);
23
- return `${origin}/ws?${query}`;
24
- }
25
- const BUTTON_MASK = { 0: 1, 1: 2, 2: 4 };
26
- function eventDetail(event) {
27
- if (!("detail" in event)) return {};
28
- const detail = event.detail;
29
- if (!detail || typeof detail !== "object") return {};
30
- const copy = {};
31
- for (const [key, value] of Object.entries(detail)) copy[key] = value;
32
- return copy;
33
- }
34
- class VncSession extends EventTarget {
35
- protocol = "vnc";
36
- #rfb;
37
- #credentials;
38
- #transport = null;
39
- #keepalive = null;
40
- #readySent = false;
41
- #close = null;
42
- constructor(container, url, options) {
43
- super();
44
- this.#credentials = options.credentials;
45
- this.#rfb = new RFB(container, url, {
46
- credentials: {
47
- username: options.credentials.username,
48
- password: options.credentials.password
49
- },
50
- shared: true,
51
- wsProtocols: ["binary"]
52
- });
53
- this.#rfb._cursor?._canvas?.classList.add("remote-cursor-overlay");
54
- this.#rfb.showDotCursor = true;
55
- this.#rfb.resizeSession = options.allowRemoteResize;
56
- this.#rfb.clipViewport = true;
57
- this.#rfb.scaleViewport = options.scaleViewport;
58
- this.#rfb.viewOnly = options.viewOnly;
59
- this.#rfb.qualityLevel = options.quality;
60
- this.#rfb.compressionLevel = options.compression;
61
- this.#bindTransport();
62
- for (const name of ["connect", "disconnect", "credentialsrequired", "desktopname", "clipboard", "bell"]) {
63
- this.#rfb.addEventListener(name, (event) => {
64
- if (name === "credentialsrequired") {
65
- this.#rfb.sendCredentials({
66
- username: this.#credentials.username,
67
- password: this.#credentials.password
68
- });
69
- }
70
- const detail = eventDetail(event);
71
- if (name === "disconnect" && this.#close) {
72
- detail.code = this.#close.code;
73
- detail.reason = this.#close.reason;
74
- }
75
- this.dispatchEvent(new CustomEvent(name, { detail }));
76
- });
77
- }
78
- }
79
- /**
80
- * Attach to noVNC's WebSocket once it exists. Construction usually has it
81
- * already; a retry covers the tick where `_sock._websocket` is still null.
82
- */
83
- #bindTransport() {
84
- const attach = () => {
85
- const ws = this.#rfb._sock?._websocket ?? null;
86
- if (!ws || this.#transport === ws) return Boolean(ws);
87
- this.#transport = ws;
88
- const signalReady = () => {
89
- if (this.#readySent || ws.readyState !== WebSocket.OPEN) return;
90
- this.#readySent = true;
91
- ws.send(GATEWAY_READY_SIGNAL);
92
- };
93
- ws.addEventListener("open", signalReady);
94
- ws.addEventListener("close", (event) => {
95
- this.#close = { code: event.code, reason: event.reason ?? "" };
96
- this.#stopKeepalive();
97
- }, true);
98
- this.#keepalive = setInterval(() => {
99
- if (ws.readyState !== WebSocket.OPEN) return;
100
- try {
101
- ws.send(GATEWAY_KEEPALIVE_SIGNAL);
102
- const sock = this.#rfb._sock;
103
- if (sock && this.#rfb._rfbConnectionState === "connected") {
104
- RFB.messages.fbUpdateRequest(sock, true, 0, 0, 1, 1);
105
- }
106
- } catch {
107
- }
108
- }, KEEPALIVE_INTERVAL_MS);
109
- signalReady();
110
- return true;
111
- };
112
- if (attach()) return;
113
- let tries = 0;
114
- const timer = setInterval(() => {
115
- if (attach() || ++tries > 20) clearInterval(timer);
116
- }, 25);
117
- }
118
- #stopKeepalive() {
119
- if (this.#keepalive) clearInterval(this.#keepalive);
120
- this.#keepalive = null;
121
- }
122
- get connected() {
123
- return this.#rfb._rfbConnectionState === "connected";
124
- }
125
- get socket() {
126
- return this.#rfb._sock?._websocket ?? null;
127
- }
128
- get canvas() {
129
- return this.#rfb._canvas;
130
- }
131
- get scaleViewport() {
132
- return this.#rfb.scaleViewport;
133
- }
134
- set scaleViewport(enabled) {
135
- this.#rfb.scaleViewport = enabled;
136
- }
137
- get viewOnly() {
138
- return this.#rfb.viewOnly;
139
- }
140
- set viewOnly(enabled) {
141
- this.#rfb.viewOnly = enabled;
142
- }
143
- get qualityLevel() {
144
- return this.#rfb.qualityLevel;
145
- }
146
- set qualityLevel(level) {
147
- this.#rfb.qualityLevel = level;
148
- }
149
- get compressionLevel() {
150
- return this.#rfb.compressionLevel;
151
- }
152
- set compressionLevel(level) {
153
- this.#rfb.compressionLevel = level;
154
- }
155
- get screenGeometry() {
156
- const width = this.#rfb._fbWidth || 0;
157
- const height = this.#rfb._fbHeight || 0;
158
- return { width, height, viewport: { x: 0, y: 0, width, height } };
159
- }
160
- /** Internal state worth logging once the first server frame has been parsed. */
161
- describeState() {
162
- const socket = this.#rfb._sock;
163
- return {
164
- connectionState: this.#rfb._rfbConnectionState,
165
- initializationState: this.#rfb._rfbInitState,
166
- queuedReceiveBytes: Math.max(0, (socket?._rQlen ?? 0) - (socket?._rQi ?? 0))
167
- };
168
- }
169
- disconnect() {
170
- this.#stopKeepalive();
171
- this.#rfb.disconnect();
172
- }
173
- sendKey(keysym, code, down) {
174
- this.#rfb.sendKey(keysym, code, down);
175
- }
176
- sendClipboard(text) {
177
- this.#rfb.clipboardPasteFrom(text);
178
- }
179
- releaseKeyboard() {
180
- this.#rfb.blur();
181
- }
182
- /**
183
- * noVNC keeps pointer input private -- its own handlers translate browser
184
- * events into framebuffer coordinates first -- so this reaches for the same
185
- * wire helper `_sendMouse` ends at, with coordinates already in pixels.
186
- */
187
- #pointer(x, y, mask) {
188
- const socket = this.#rfb._sock;
189
- if (!socket || this.#rfb._rfbConnectionState !== "connected" || this.#rfb.viewOnly) return false;
190
- const width = this.#rfb._fbWidth || 1;
191
- const height = this.#rfb._fbHeight || 1;
192
- RFB.messages.pointerEvent(
193
- socket,
194
- Math.max(0, Math.min(width - 1, Math.round(x))),
195
- Math.max(0, Math.min(height - 1, Math.round(y))),
196
- mask
197
- );
198
- return true;
199
- }
200
- movePointer(x, y) {
201
- return this.#pointer(x, y, 0);
202
- }
203
- clickPointer(x, y, button = 0) {
204
- if (!this.#pointer(x, y, 0)) return false;
205
- this.#pointer(x, y, BUTTON_MASK[button]);
206
- this.#pointer(x, y, 0);
207
- return true;
208
- }
209
- pointerButton(x, y, button = 0, pressed = true) {
210
- return this.#pointer(x, y, pressed ? BUTTON_MASK[button] : 0);
211
- }
212
- scrollPointer(x, y, delta) {
213
- for (const mask of vncWheelMasks(delta)) {
214
- if (!this.#pointer(x, y, mask)) return false;
215
- if (!this.#pointer(x, y, 0)) return false;
216
- }
217
- return true;
218
- }
219
- /**
220
- * VNC has no Unicode input message, so text is typed as keysyms. For every
221
- * character in the Basic Multilingual Plane the keysym is the code point.
222
- */
223
- typeText(text) {
224
- if (this.#rfb._rfbConnectionState !== "connected" || this.#rfb.viewOnly || !text) return false;
225
- for (const character of text) {
226
- const point = character.codePointAt(0) ?? 0;
227
- const keysym = character === "\n" ? 65293 : character === " " ? 65289 : point;
228
- this.#rfb.sendKey(keysym, null, true);
229
- this.#rfb.sendKey(keysym, null, false);
230
- }
231
- return true;
232
- }
233
- }
234
- function describeDisconnect({ code, reason, suffix }) {
235
- if (code === 4001 || /auth|password|credential/i.test(reason)) {
236
- return `VNC authentication failed${suffix}. Verify the macOS account username and its password.`;
237
- }
238
- if (code === 4002 || /apple dh/i.test(reason)) {
239
- return `macOS VNC authentication handshake failed${suffix}.`;
240
- }
241
- if (code === 1011 || /cannot connect|unreachable|tcp error|tcp write/i.test(reason)) {
242
- return `The Worker could not keep a TCP connection to the VNC host${suffix}. Check that Screen Sharing is running and listening on this address.`;
243
- }
244
- return null;
245
- }
246
- const vncDriver = {
247
- protocol: "vnc",
248
- label: "VNC Client",
249
- defaultPort: DEFAULT_PORTS.vnc,
250
- dialog: DIALOG,
251
- buildUrl,
252
- createSession: (container, url, options) => new VncSession(container, url, options),
253
- describeState: (session) => session instanceof VncSession ? session.describeState() : {},
254
- describeDisconnect
255
- };
256
- export {
257
- vncDriver
258
- };