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.
- package/dist/index.js +1 -6
- package/dist/protocols/index.js +3 -34
- package/dist/protocols/ssh/session.js +17 -608
- package/dist/protocols/ssh/terminal.js +6 -190
- package/dist/shared/connection.js +1 -113
- package/dist/shared/hosts.js +1 -132
- package/dist/shared/icons.js +1 -41
- package/dist/shared/protocol.js +1 -39
- package/dist/shared/signals.js +1 -41
- package/package.json +1 -1
- package/dist/protocols/rdp/buffer.d.ts +0 -38
- package/dist/protocols/rdp/buffer.js +0 -169
- package/dist/protocols/rdp/caps.d.ts +0 -49
- package/dist/protocols/rdp/caps.js +0 -259
- package/dist/protocols/rdp/cert.d.ts +0 -18
- package/dist/protocols/rdp/cert.js +0 -134
- package/dist/protocols/rdp/client.d.ts +0 -56
- package/dist/protocols/rdp/client.js +0 -1069
- package/dist/protocols/rdp/cliprdr.d.ts +0 -27
- package/dist/protocols/rdp/cliprdr.js +0 -239
- package/dist/protocols/rdp/credssp.d.ts +0 -34
- package/dist/protocols/rdp/credssp.js +0 -253
- package/dist/protocols/rdp/crypto.d.ts +0 -63
- package/dist/protocols/rdp/crypto.js +0 -368
- package/dist/protocols/rdp/display.d.ts +0 -38
- package/dist/protocols/rdp/display.js +0 -588
- package/dist/protocols/rdp/gcc.d.ts +0 -23
- package/dist/protocols/rdp/gcc.js +0 -131
- package/dist/protocols/rdp/keymap.d.ts +0 -9
- package/dist/protocols/rdp/keymap.js +0 -131
- package/dist/protocols/rdp/mcs.d.ts +0 -40
- package/dist/protocols/rdp/mcs.js +0 -222
- package/dist/protocols/rdp/ntlm.d.ts +0 -61
- package/dist/protocols/rdp/ntlm.js +0 -304
- package/dist/protocols/rdp/pdu.d.ts +0 -155
- package/dist/protocols/rdp/pdu.js +0 -443
- package/dist/protocols/rdp/rail.d.ts +0 -119
- package/dist/protocols/rdp/rail.js +0 -382
- package/dist/protocols/rdp/rle.d.ts +0 -13
- package/dist/protocols/rdp/rle.js +0 -275
- package/dist/protocols/rdp/sec.d.ts +0 -59
- package/dist/protocols/rdp/sec.js +0 -155
- package/dist/protocols/rdp/session.d.ts +0 -62
- package/dist/protocols/rdp/session.js +0 -306
- package/dist/protocols/rdp/tls.d.ts +0 -18
- package/dist/protocols/rdp/tls.js +0 -353
- package/dist/protocols/rdp/vchannel.d.ts +0 -28
- package/dist/protocols/rdp/vchannel.js +0 -58
- package/dist/protocols/rdp/x224.d.ts +0 -40
- package/dist/protocols/rdp/x224.js +0 -109
- package/dist/protocols/ssh/kex.d.ts +0 -97
- package/dist/protocols/ssh/kex.js +0 -176
- package/dist/protocols/ssh/messages.d.ts +0 -50
- package/dist/protocols/ssh/messages.js +0 -54
- package/dist/protocols/ssh/transport.d.ts +0 -62
- package/dist/protocols/ssh/transport.js +0 -612
- package/dist/protocols/ssh/wire.d.ts +0 -52
- package/dist/protocols/ssh/wire.js +0 -188
- package/dist/protocols/vnc/input.d.ts +0 -5
- package/dist/protocols/vnc/input.js +0 -15
- package/dist/protocols/vnc/session.d.ts +0 -12
- 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,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
|
-
};
|