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.
- package/README.md +50 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +7 -0
- package/dist/protocols/index.js +35 -0
- package/dist/protocols/index.js.map +7 -0
- package/dist/protocols/rdp/buffer.js +170 -0
- package/dist/protocols/rdp/buffer.js.map +7 -0
- package/dist/protocols/rdp/caps.js +260 -0
- package/dist/protocols/rdp/caps.js.map +7 -0
- package/dist/protocols/rdp/cert.js +135 -0
- package/dist/protocols/rdp/cert.js.map +7 -0
- package/dist/protocols/rdp/client.js +1068 -0
- package/dist/protocols/rdp/client.js.map +7 -0
- package/dist/protocols/rdp/cliprdr.js +240 -0
- package/dist/protocols/rdp/cliprdr.js.map +7 -0
- package/dist/protocols/rdp/credssp.js +254 -0
- package/dist/protocols/rdp/credssp.js.map +7 -0
- package/dist/protocols/rdp/crypto.js +369 -0
- package/dist/protocols/rdp/crypto.js.map +7 -0
- package/dist/protocols/rdp/display.js +589 -0
- package/dist/protocols/rdp/display.js.map +7 -0
- package/dist/protocols/rdp/gcc.js +132 -0
- package/dist/protocols/rdp/gcc.js.map +7 -0
- package/dist/protocols/rdp/keymap.js +132 -0
- package/dist/protocols/rdp/keymap.js.map +7 -0
- package/dist/protocols/rdp/mcs.js +223 -0
- package/dist/protocols/rdp/mcs.js.map +7 -0
- package/dist/protocols/rdp/ntlm.js +305 -0
- package/dist/protocols/rdp/ntlm.js.map +7 -0
- package/dist/protocols/rdp/pdu.js +444 -0
- package/dist/protocols/rdp/pdu.js.map +7 -0
- package/dist/protocols/rdp/rail.js +383 -0
- package/dist/protocols/rdp/rail.js.map +7 -0
- package/dist/protocols/rdp/rle.js +276 -0
- package/dist/protocols/rdp/rle.js.map +7 -0
- package/dist/protocols/rdp/sec.js +156 -0
- package/dist/protocols/rdp/sec.js.map +7 -0
- package/dist/protocols/rdp/session.js +307 -0
- package/dist/protocols/rdp/session.js.map +7 -0
- package/dist/protocols/rdp/tls.js +354 -0
- package/dist/protocols/rdp/tls.js.map +7 -0
- package/dist/protocols/rdp/vchannel.js +59 -0
- package/dist/protocols/rdp/vchannel.js.map +7 -0
- package/dist/protocols/rdp/x224.js +110 -0
- package/dist/protocols/rdp/x224.js.map +7 -0
- package/dist/protocols/ssh/kex.js +177 -0
- package/dist/protocols/ssh/kex.js.map +7 -0
- package/dist/protocols/ssh/messages.js +55 -0
- package/dist/protocols/ssh/messages.js.map +7 -0
- package/dist/protocols/ssh/session.js +608 -0
- package/dist/protocols/ssh/session.js.map +7 -0
- package/dist/protocols/ssh/terminal.js +191 -0
- package/dist/protocols/ssh/terminal.js.map +7 -0
- package/dist/protocols/ssh/transport.js +611 -0
- package/dist/protocols/ssh/transport.js.map +7 -0
- package/dist/protocols/ssh/wire.js +189 -0
- package/dist/protocols/ssh/wire.js.map +7 -0
- package/dist/protocols/types.js +1 -0
- package/dist/protocols/types.js.map +7 -0
- package/dist/protocols/vnc/input.js +16 -0
- package/dist/protocols/vnc/input.js.map +7 -0
- package/dist/protocols/vnc/session.js +259 -0
- package/dist/protocols/vnc/session.js.map +7 -0
- package/dist/shared/connection.js +114 -0
- package/dist/shared/connection.js.map +7 -0
- package/dist/shared/device.js +1 -0
- package/dist/shared/device.js.map +7 -0
- package/dist/shared/hosts.js +133 -0
- package/dist/shared/hosts.js.map +7 -0
- package/dist/shared/icons.js +42 -0
- package/dist/shared/icons.js.map +7 -0
- package/dist/shared/protocol.js +40 -0
- package/dist/shared/protocol.js.map +7 -0
- package/dist/shared/signals.js +42 -0
- package/dist/shared/signals.js.map +7 -0
- package/package.json +82 -0
- package/src/index.ts +14 -0
- package/src/protocols/index.ts +72 -0
- package/src/protocols/rdp/buffer.ts +204 -0
- package/src/protocols/rdp/caps.ts +304 -0
- package/src/protocols/rdp/cert.ts +190 -0
- package/src/protocols/rdp/client.ts +1363 -0
- package/src/protocols/rdp/cliprdr.ts +344 -0
- package/src/protocols/rdp/credssp.ts +319 -0
- package/src/protocols/rdp/crypto.ts +416 -0
- package/src/protocols/rdp/display.ts +750 -0
- package/src/protocols/rdp/gcc.ts +162 -0
- package/src/protocols/rdp/keymap.ts +68 -0
- package/src/protocols/rdp/mcs.ts +283 -0
- package/src/protocols/rdp/ntlm.ts +380 -0
- package/src/protocols/rdp/pdu.ts +497 -0
- package/src/protocols/rdp/rail.ts +480 -0
- package/src/protocols/rdp/rle.ts +343 -0
- package/src/protocols/rdp/sec.ts +184 -0
- package/src/protocols/rdp/session.ts +393 -0
- package/src/protocols/rdp/tls.ts +452 -0
- package/src/protocols/rdp/vchannel.ts +89 -0
- package/src/protocols/rdp/x224.ts +139 -0
- package/src/protocols/ssh/kex.ts +227 -0
- package/src/protocols/ssh/messages.ts +54 -0
- package/src/protocols/ssh/session.ts +878 -0
- package/src/protocols/ssh/terminal.ts +242 -0
- package/src/protocols/ssh/transport.ts +732 -0
- package/src/protocols/ssh/wire.ts +220 -0
- package/src/protocols/types.ts +146 -0
- package/src/protocols/vnc/input.ts +13 -0
- package/src/protocols/vnc/novnc.d.ts +75 -0
- package/src/protocols/vnc/session.ts +367 -0
- package/src/shared/connection.ts +206 -0
- package/src/shared/device.ts +28 -0
- package/src/shared/hosts.ts +240 -0
- package/src/shared/icons.ts +49 -0
- package/src/shared/protocol.ts +55 -0
- package/src/shared/signals.ts +67 -0
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { RdpClient } from "./client";
|
|
2
|
+
import { RdpDisplay } from "./display";
|
|
3
|
+
import { DEFAULT_PORTS } from "../../shared/protocol";
|
|
4
|
+
const DIALOG = {
|
|
5
|
+
usernameLabel: "Username",
|
|
6
|
+
usernamePlaceholder: "Windows username",
|
|
7
|
+
usernameHelp: "Use the Windows account name for this computer.",
|
|
8
|
+
passwordLabel: "Password",
|
|
9
|
+
passwordPlaceholder: "Windows password",
|
|
10
|
+
passwordHelp: "Use the Windows account password. It may be left empty when the host presents its own sign-in screen.",
|
|
11
|
+
// Credentials may be left empty and typed on the remote sign-in screen.
|
|
12
|
+
requiresPassword: false
|
|
13
|
+
};
|
|
14
|
+
function buildUrl(origin, settings) {
|
|
15
|
+
const query = new URLSearchParams({
|
|
16
|
+
host: settings.host,
|
|
17
|
+
port: String(settings.port),
|
|
18
|
+
security: settings.security
|
|
19
|
+
});
|
|
20
|
+
return `${origin}/rdp-ws?${query}`;
|
|
21
|
+
}
|
|
22
|
+
function describeDisconnect({ code, reason, suffix }) {
|
|
23
|
+
if (/hybrid_required|requires network level|authenticates before/i.test(reason)) {
|
|
24
|
+
return `The RDP host signs in before showing a desktop${suffix}. Enter the Windows username and password and connect again.`;
|
|
25
|
+
}
|
|
26
|
+
if (code === 1011 || /cannot connect|unreachable|tcp error|tcp write/i.test(reason)) {
|
|
27
|
+
return `The Worker could not keep a TCP connection to the RDP host${suffix}. Check that Remote Desktop is enabled and listening on this address and port.`;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
class RdpSession extends EventTarget {
|
|
32
|
+
protocol = "rdp";
|
|
33
|
+
constructor(container, url, options = {}) {
|
|
34
|
+
super();
|
|
35
|
+
this.url = url;
|
|
36
|
+
this.options = options;
|
|
37
|
+
this.display = new RdpDisplay(container);
|
|
38
|
+
this.display.viewOnly = Boolean(options.viewOnly);
|
|
39
|
+
this._rfbConnectionState = "connecting";
|
|
40
|
+
this._scaleViewport = options.scaleViewport !== false;
|
|
41
|
+
this.display.scaleViewport = this._scaleViewport;
|
|
42
|
+
this.lastErrorMessage = "";
|
|
43
|
+
this.hostLabel = options.hostLabel || "Remote Desktop";
|
|
44
|
+
this.#createClient(options.security || "auto");
|
|
45
|
+
this.remoteAppLayoutObserver = options.remoteApp?.program && typeof ResizeObserver === "function" ? new ResizeObserver(() => this.fitToViewport()) : null;
|
|
46
|
+
this.remoteAppLayoutObserver?.observe(this.display.scrollHost);
|
|
47
|
+
}
|
|
48
|
+
#createClient(security) {
|
|
49
|
+
this.security = security;
|
|
50
|
+
this.client = new RdpClient(this.url, {
|
|
51
|
+
width: this.options.width,
|
|
52
|
+
height: this.options.height,
|
|
53
|
+
username: this.options.credentials?.username || "",
|
|
54
|
+
password: this.options.credentials?.password || "",
|
|
55
|
+
domain: this.options.domain || "",
|
|
56
|
+
security,
|
|
57
|
+
keyboardLayout: this.options.keyboardLayout || 1033,
|
|
58
|
+
clientName: this.options.clientName || "cf-gateway",
|
|
59
|
+
remoteApp: this.options.remoteApp || null
|
|
60
|
+
});
|
|
61
|
+
this.client.addEventListener("ready", (event) => {
|
|
62
|
+
const { width, height } = event.detail;
|
|
63
|
+
this.display.resize(width, height);
|
|
64
|
+
this.display.attachInput(this.client);
|
|
65
|
+
this.display.focus();
|
|
66
|
+
this.fitToViewport();
|
|
67
|
+
this._rfbConnectionState = "connected";
|
|
68
|
+
this.dispatchEvent(new CustomEvent("connect"));
|
|
69
|
+
this.dispatchEvent(new CustomEvent("desktopname", {
|
|
70
|
+
detail: { name: `${this.hostLabel} (${width}x${height})` }
|
|
71
|
+
}));
|
|
72
|
+
});
|
|
73
|
+
this.client.addEventListener("resize", (event) => {
|
|
74
|
+
const { width, height } = event.detail;
|
|
75
|
+
this.display.resize(width, height);
|
|
76
|
+
this.dispatchEvent(new CustomEvent("desktopname", {
|
|
77
|
+
detail: { name: `${this.hostLabel} (${width}x${height})` }
|
|
78
|
+
}));
|
|
79
|
+
this.fitToViewport();
|
|
80
|
+
});
|
|
81
|
+
this.client.addEventListener("remoteappviewport", (event) => {
|
|
82
|
+
this.display.setViewport(event.detail);
|
|
83
|
+
});
|
|
84
|
+
this.client.addEventListener("bitmap", (event) => {
|
|
85
|
+
this.display.drawRects(event.detail.rects, event.detail.palette);
|
|
86
|
+
this.dispatchEvent(new CustomEvent("frame", {
|
|
87
|
+
detail: { rects: event.detail.rects }
|
|
88
|
+
}));
|
|
89
|
+
});
|
|
90
|
+
this.client.addEventListener("palette", (event) => {
|
|
91
|
+
this.display.palette = event.detail;
|
|
92
|
+
});
|
|
93
|
+
this.client.addEventListener("pointer", (event) => {
|
|
94
|
+
this.display.applyPointer(event.detail);
|
|
95
|
+
});
|
|
96
|
+
this.client.addEventListener("clipboard", (event) => {
|
|
97
|
+
this.dispatchEvent(new CustomEvent("clipboard", { detail: { text: event.detail.text } }));
|
|
98
|
+
});
|
|
99
|
+
this.client.addEventListener("remoteappresult", (event) => {
|
|
100
|
+
this.dispatchEvent(new CustomEvent("remoteappresult", { detail: event.detail }));
|
|
101
|
+
});
|
|
102
|
+
this.client.addEventListener("remoteappwindow", (event) => {
|
|
103
|
+
this.dispatchEvent(new CustomEvent("remoteappwindow", { detail: event.detail }));
|
|
104
|
+
});
|
|
105
|
+
this.client.addEventListener("remoteappclosed", (event) => {
|
|
106
|
+
this.dispatchEvent(new CustomEvent("remoteappclosed", { detail: event.detail }));
|
|
107
|
+
});
|
|
108
|
+
this.client.addEventListener("error", (event) => {
|
|
109
|
+
this.lastErrorMessage = event.detail.message;
|
|
110
|
+
});
|
|
111
|
+
this.client.addEventListener("close", (event) => {
|
|
112
|
+
this._rfbConnectionState = "disconnected";
|
|
113
|
+
this.remoteAppLayoutObserver?.disconnect();
|
|
114
|
+
this.display.destroy();
|
|
115
|
+
this.dispatchEvent(new CustomEvent("disconnect", {
|
|
116
|
+
detail: {
|
|
117
|
+
clean: Boolean(event.detail.clean),
|
|
118
|
+
message: this.lastErrorMessage || event.detail.message || "",
|
|
119
|
+
code: event.detail.code,
|
|
120
|
+
phase: event.detail.phase
|
|
121
|
+
}
|
|
122
|
+
}));
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Ask the host to size the remote application's window to the room this
|
|
127
|
+
* session has on the page. Full-desktop sessions have nothing to do here:
|
|
128
|
+
* their size is fixed when the connection is negotiated.
|
|
129
|
+
*/
|
|
130
|
+
fitToViewport() {
|
|
131
|
+
if (!this.options.remoteApp?.program) return false;
|
|
132
|
+
const bounds = this.display.container.getBoundingClientRect();
|
|
133
|
+
const width = Math.floor(bounds.width || this.options.width || 0);
|
|
134
|
+
const height = Math.floor(bounds.height || this.options.height || 0);
|
|
135
|
+
if (width <= 0 || height <= 0) return false;
|
|
136
|
+
return this.client.fitRemoteApp(width, height);
|
|
137
|
+
}
|
|
138
|
+
get connected() {
|
|
139
|
+
return this._rfbConnectionState === "connected";
|
|
140
|
+
}
|
|
141
|
+
get canvas() {
|
|
142
|
+
return this.display?.canvas ?? null;
|
|
143
|
+
}
|
|
144
|
+
/** The workspace instruments this socket for byte counters and keepalives. */
|
|
145
|
+
get socket() {
|
|
146
|
+
return this.client.socket ?? null;
|
|
147
|
+
}
|
|
148
|
+
get _sock() {
|
|
149
|
+
return { _websocket: this.client.socket };
|
|
150
|
+
}
|
|
151
|
+
get scaleViewport() {
|
|
152
|
+
return this._scaleViewport;
|
|
153
|
+
}
|
|
154
|
+
set scaleViewport(enabled) {
|
|
155
|
+
this._scaleViewport = Boolean(enabled);
|
|
156
|
+
this.display.setScaleViewport(this._scaleViewport);
|
|
157
|
+
}
|
|
158
|
+
get viewOnly() {
|
|
159
|
+
return this.display.viewOnly;
|
|
160
|
+
}
|
|
161
|
+
set viewOnly(enabled) {
|
|
162
|
+
this.display.viewOnly = Boolean(enabled);
|
|
163
|
+
if (enabled) this.display.releaseAllKeys();
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* The on-screen keyboard and the special-keys dialog call this with an X11
|
|
167
|
+
* keysym and a DOM code. RDP wants a scancode, so the code decides; the
|
|
168
|
+
* keysym is only a fallback for characters with no key of their own.
|
|
169
|
+
*/
|
|
170
|
+
sendKey(keysym, code, pressed) {
|
|
171
|
+
if (code && this.client.sendKeyByCode(code, pressed)) return;
|
|
172
|
+
if (!pressed || !keysym) return;
|
|
173
|
+
if (keysym >= 32 && keysym <= 1114111) {
|
|
174
|
+
this.client.sendUnicode(String.fromCodePoint(keysym));
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
sendCtrlAltDel() {
|
|
178
|
+
this.client.sendCtrlAltDel();
|
|
179
|
+
}
|
|
180
|
+
// --- pointer, for the AI pane's screen-control tool ----------------------
|
|
181
|
+
//
|
|
182
|
+
// The display already turns browser events into these calls; exposing them
|
|
183
|
+
// lets something other than a mouse drive the same path.
|
|
184
|
+
/** Desktop pixels. Returns false when the session cannot accept input. */
|
|
185
|
+
movePointer(x, y) {
|
|
186
|
+
if (!this.client?.connected || this.display.viewOnly) return false;
|
|
187
|
+
this.client.sendMouseMove(...this.#clampToDesktop(x, y));
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
clickPointer(x, y, button = 0) {
|
|
191
|
+
if (!this.client?.connected || this.display.viewOnly) return false;
|
|
192
|
+
const [px, py] = this.#clampToDesktop(x, y);
|
|
193
|
+
this.client.sendMouseMove(px, py);
|
|
194
|
+
this.pointerButton(px, py, button, true);
|
|
195
|
+
this.pointerButton(px, py, button, false);
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
pointerButton(x, y, button = 0, pressed = true) {
|
|
199
|
+
if (!this.client?.connected || this.display.viewOnly) return false;
|
|
200
|
+
const [px, py] = this.#clampToDesktop(x, y);
|
|
201
|
+
this.client.sendMouseButton(button, pressed, px, py);
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
scrollPointer(x, y, delta) {
|
|
205
|
+
if (!this.client?.connected || this.display.viewOnly) return false;
|
|
206
|
+
const [px, py] = this.#clampToDesktop(x, y);
|
|
207
|
+
for (let i = 0; i < Math.min(20, Math.abs(delta.y)); i++) {
|
|
208
|
+
this.client.sendWheel(Math.sign(delta.y), px, py, false);
|
|
209
|
+
}
|
|
210
|
+
for (let i = 0; i < Math.min(20, Math.abs(delta.x)); i++) {
|
|
211
|
+
this.client.sendWheel(Math.sign(delta.x), px, py, true);
|
|
212
|
+
}
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
215
|
+
/** Type a run of characters, which RDP carries as Unicode rather than keys. */
|
|
216
|
+
typeText(text) {
|
|
217
|
+
if (!this.client?.connected || this.display.viewOnly || !text) return false;
|
|
218
|
+
let printable = "";
|
|
219
|
+
const flushPrintable = () => {
|
|
220
|
+
if (!printable) return;
|
|
221
|
+
this.client.sendUnicode(printable);
|
|
222
|
+
printable = "";
|
|
223
|
+
};
|
|
224
|
+
for (const character of text) {
|
|
225
|
+
if (character === "\n" || character === "\r") {
|
|
226
|
+
flushPrintable();
|
|
227
|
+
this.client.sendKeyByCode("Enter", true);
|
|
228
|
+
this.client.sendKeyByCode("Enter", false);
|
|
229
|
+
} else if (character === " ") {
|
|
230
|
+
flushPrintable();
|
|
231
|
+
this.client.sendKeyByCode("Tab", true);
|
|
232
|
+
this.client.sendKeyByCode("Tab", false);
|
|
233
|
+
} else {
|
|
234
|
+
printable += character;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
flushPrintable();
|
|
238
|
+
return true;
|
|
239
|
+
}
|
|
240
|
+
#clampToDesktop(x, y) {
|
|
241
|
+
return [
|
|
242
|
+
Math.max(0, Math.min((this.display.width || 1) - 1, Math.round(x))),
|
|
243
|
+
Math.max(0, Math.min((this.display.height || 1) - 1, Math.round(y)))
|
|
244
|
+
];
|
|
245
|
+
}
|
|
246
|
+
/** What the canvas is showing, for a screen capture or a coordinate check. */
|
|
247
|
+
get screenGeometry() {
|
|
248
|
+
return {
|
|
249
|
+
width: this.display.width,
|
|
250
|
+
height: this.display.height,
|
|
251
|
+
viewport: { ...this.display.viewport }
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Hand text to the host's clipboard, falling back to typing it when the host
|
|
256
|
+
* declined the clipboard channel -- xrdp builds often do.
|
|
257
|
+
*/
|
|
258
|
+
clipboardPasteFrom(text) {
|
|
259
|
+
if (!text) return;
|
|
260
|
+
const shared = this.client.setClipboardText(text);
|
|
261
|
+
if (!shared || typeof shared.then !== "function") {
|
|
262
|
+
this.client.sendUnicode(text);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
shared.then((acknowledged) => {
|
|
266
|
+
if (!acknowledged) this.client.sendUnicode(text);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Deliver text into the session, which is what the clipboard drawer's button
|
|
271
|
+
* has always visibly done. `clipboardPasteFrom` only loads the clipboard, the
|
|
272
|
+
* way noVNC's method of that name does.
|
|
273
|
+
*/
|
|
274
|
+
pasteText(text) {
|
|
275
|
+
return text ? this.client.pasteToHost(text) : false;
|
|
276
|
+
}
|
|
277
|
+
releaseKeyboard() {
|
|
278
|
+
this.display.releaseAllKeys();
|
|
279
|
+
this.display.blur();
|
|
280
|
+
}
|
|
281
|
+
/** True once the host has agreed to share its clipboard. */
|
|
282
|
+
get clipboardShared() {
|
|
283
|
+
return this.client.clipboardShared;
|
|
284
|
+
}
|
|
285
|
+
disconnect() {
|
|
286
|
+
this.remoteAppLayoutObserver?.disconnect();
|
|
287
|
+
this.client.disconnect();
|
|
288
|
+
this.display.destroy();
|
|
289
|
+
this._rfbConnectionState = "disconnected";
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const rdpDriver = {
|
|
293
|
+
protocol: "rdp",
|
|
294
|
+
label: "RDP Client",
|
|
295
|
+
defaultPort: DEFAULT_PORTS.rdp,
|
|
296
|
+
dialog: DIALOG,
|
|
297
|
+
buildUrl,
|
|
298
|
+
createSession: (container, url, options) => new RdpSession(container, url, options),
|
|
299
|
+
// The RDP client logs its own progress, so there is nothing to add here.
|
|
300
|
+
describeState: () => ({}),
|
|
301
|
+
describeDisconnect
|
|
302
|
+
};
|
|
303
|
+
export {
|
|
304
|
+
RdpSession,
|
|
305
|
+
rdpDriver
|
|
306
|
+
};
|
|
307
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../src/protocols/rdp/session.ts"],
|
|
4
|
+
"sourcesContent": ["// An RDP session wearing the same shape as noVNC's RFB object.\n//\n// The toolbar, the on-screen keyboard, the byte counters and the stats rail in\n// app.js were all written against that interface. Presenting RDP through the\n// same surface keeps one UI for both protocols instead of two of everything.\n\nimport { RdpClient } from './client';\nimport { RdpDisplay } from './display';\nimport type { DialogCopy, DisconnectContext, ProtocolDriver } from '../types';\nimport type { ConnectionSettings } from '../../shared/connection';\nimport { DEFAULT_PORTS } from '../../shared/protocol';\n\n/** Wording for the one connect dialog, which is shared markup with VNC. */\nconst DIALOG: DialogCopy = {\n usernameLabel: 'Username',\n usernamePlaceholder: 'Windows username',\n usernameHelp: 'Use the Windows account name for this computer.',\n passwordLabel: 'Password',\n passwordPlaceholder: 'Windows password',\n passwordHelp: 'Use the Windows account password. It may be left empty when the host presents its own sign-in screen.',\n // Credentials may be left empty and typed on the remote sign-in screen.\n requiresPassword: false,\n};\n\n/**\n * RDP credentials never appear in the URL: the browser-side client puts them\n * straight into the protocol's own Client Info PDU, or into CredSSP for NLA.\n */\nfunction buildUrl(origin: string, settings: ConnectionSettings): string {\n const query = new URLSearchParams({\n host: settings.host,\n port: String(settings.port),\n security: settings.security,\n });\n return `${origin}/rdp-ws?${query}`;\n}\n\n/**\n * Returns null when nothing RDP-specific applies. The client's own message,\n * when there is one, is always more specific and wins before this is consulted.\n */\nfunction describeDisconnect({ code, reason, suffix }: DisconnectContext): string | null {\n if (/hybrid_required|requires network level|authenticates before/i.test(reason)) {\n return `The RDP host signs in before showing a desktop${suffix}. `\n + 'Enter the Windows username and password and connect again.';\n }\n if (code === 1011 || /cannot connect|unreachable|tcp error|tcp write/i.test(reason)) {\n return `The Worker could not keep a TCP connection to the RDP host${suffix}. `\n + 'Check that Remote Desktop is enabled and listening on this address and port.';\n }\n return null;\n}\n\nexport class RdpSession extends EventTarget {\n readonly protocol = 'rdp' as const;\n\n constructor(container, url: string, options = {}) {\n super();\n\n this.url = url;\n this.options = options;\n this.display = new RdpDisplay(container);\n this.display.viewOnly = Boolean(options.viewOnly);\n\n this._rfbConnectionState = 'connecting';\n this._scaleViewport = options.scaleViewport !== false;\n this.display.scaleViewport = this._scaleViewport;\n this.lastErrorMessage = '';\n this.hostLabel = options.hostLabel || 'Remote Desktop';\n\n this.#createClient(options.security || 'auto');\n\n // RemoteApp windows are sized inside a fixed negotiated RDP desktop. The\n // browser pane can still change when AI/SSH docks, closes, or the device\n // rotates, so publish the new work area and refit the application whenever\n // the pane itself changes size.\n this.remoteAppLayoutObserver = options.remoteApp?.program\n && typeof ResizeObserver === 'function'\n ? new ResizeObserver(() => this.fitToViewport())\n : null;\n this.remoteAppLayoutObserver?.observe(this.display.scrollHost);\n }\n\n #createClient(security) {\n this.security = security;\n this.client = new RdpClient(this.url, {\n width: this.options.width,\n height: this.options.height,\n username: this.options.credentials?.username || '',\n password: this.options.credentials?.password || '',\n domain: this.options.domain || '',\n security,\n keyboardLayout: this.options.keyboardLayout || 0x409,\n clientName: this.options.clientName || 'cf-gateway',\n remoteApp: this.options.remoteApp || null,\n });\n\n this.client.addEventListener('ready', (event) => {\n const { width, height } = event.detail;\n this.display.resize(width, height);\n this.display.attachInput(this.client);\n this.display.focus();\n this.fitToViewport();\n\n this._rfbConnectionState = 'connected';\n this.dispatchEvent(new CustomEvent('connect'));\n this.dispatchEvent(new CustomEvent('desktopname', {\n detail: { name: `${this.hostLabel} (${width}x${height})` },\n }));\n });\n\n // A host that re-runs capability exchange with a different desktop size.\n this.client.addEventListener('resize', (event) => {\n const { width, height } = event.detail;\n this.display.resize(width, height);\n this.dispatchEvent(new CustomEvent('desktopname', {\n detail: { name: `${this.hostLabel} (${width}x${height})` },\n }));\n this.fitToViewport();\n });\n\n // In RemoteApp mode the canvas follows the application's own window, so\n // the desktop around it -- which the host never paints -- stays off screen.\n this.client.addEventListener('remoteappviewport', (event) => {\n this.display.setViewport(event.detail);\n });\n\n this.client.addEventListener('bitmap', (event) => {\n this.display.drawRects(event.detail.rects, event.detail.palette);\n // The workspace keeps a pre-close RemoteApp thumbnail in memory. Emit\n // only after the rectangles have reached the canvas so its capture is a\n // real application frame rather than the black framebuffer allocated at\n // connection time.\n this.dispatchEvent(new CustomEvent('frame', {\n detail: { rects: event.detail.rects },\n }));\n });\n\n this.client.addEventListener('palette', (event) => {\n this.display.palette = event.detail;\n });\n\n this.client.addEventListener('pointer', (event) => {\n this.display.applyPointer(event.detail);\n });\n\n // Same event name and shape noVNC uses, so app.js needs no protocol branch.\n this.client.addEventListener('clipboard', (event) => {\n this.dispatchEvent(new CustomEvent('clipboard', { detail: { text: event.detail.text } }));\n });\n\n this.client.addEventListener('remoteappresult', (event) => {\n this.dispatchEvent(new CustomEvent('remoteappresult', { detail: event.detail }));\n });\n\n this.client.addEventListener('remoteappwindow', (event) => {\n this.dispatchEvent(new CustomEvent('remoteappwindow', { detail: event.detail }));\n });\n\n this.client.addEventListener('remoteappclosed', (event) => {\n this.dispatchEvent(new CustomEvent('remoteappclosed', { detail: event.detail }));\n });\n\n this.client.addEventListener('error', (event) => {\n this.lastErrorMessage = event.detail.message;\n });\n\n this.client.addEventListener('close', (event) => {\n this._rfbConnectionState = 'disconnected';\n this.remoteAppLayoutObserver?.disconnect();\n this.display.destroy();\n this.dispatchEvent(new CustomEvent('disconnect', {\n detail: {\n clean: Boolean(event.detail.clean),\n message: this.lastErrorMessage || event.detail.message || '',\n code: event.detail.code,\n phase: event.detail.phase,\n },\n }));\n });\n }\n\n /**\n * Ask the host to size the remote application's window to the room this\n * session has on the page. Full-desktop sessions have nothing to do here:\n * their size is fixed when the connection is negotiated.\n */\n fitToViewport() {\n if (!this.options.remoteApp?.program) return false;\n\n const bounds = this.display.container.getBoundingClientRect();\n const width = Math.floor(bounds.width || this.options.width || 0);\n const height = Math.floor(bounds.height || this.options.height || 0);\n if (width <= 0 || height <= 0) return false;\n\n return this.client.fitRemoteApp(width, height);\n }\n\n get connected(): boolean {\n return this._rfbConnectionState === 'connected';\n }\n\n get canvas(): HTMLCanvasElement | null {\n return this.display?.canvas ?? null;\n }\n\n /** The workspace instruments this socket for byte counters and keepalives. */\n get socket(): WebSocket | null {\n return this.client.socket ?? null;\n }\n\n get _sock() {\n return { _websocket: this.client.socket };\n }\n\n get scaleViewport() {\n return this._scaleViewport;\n }\n\n set scaleViewport(enabled) {\n this._scaleViewport = Boolean(enabled);\n this.display.setScaleViewport(this._scaleViewport);\n }\n\n get viewOnly() {\n return this.display.viewOnly;\n }\n\n set viewOnly(enabled) {\n this.display.viewOnly = Boolean(enabled);\n if (enabled) this.display.releaseAllKeys();\n }\n\n /**\n * The on-screen keyboard and the special-keys dialog call this with an X11\n * keysym and a DOM code. RDP wants a scancode, so the code decides; the\n * keysym is only a fallback for characters with no key of their own.\n */\n sendKey(keysym, code: number, pressed: boolean) {\n if (code && this.client.sendKeyByCode(code, pressed)) return;\n\n // Unicode input has no press/release pair to mirror, so send it once.\n if (!pressed || !keysym) return;\n if (keysym >= 0x20 && keysym <= 0x10ffff) {\n this.client.sendUnicode(String.fromCodePoint(keysym));\n }\n }\n\n sendCtrlAltDel() {\n this.client.sendCtrlAltDel();\n }\n\n // --- pointer, for the AI pane's screen-control tool ----------------------\n //\n // The display already turns browser events into these calls; exposing them\n // lets something other than a mouse drive the same path.\n\n /** Desktop pixels. Returns false when the session cannot accept input. */\n movePointer(x: number, y: number) {\n if (!this.client?.connected || this.display.viewOnly) return false;\n this.client.sendMouseMove(...this.#clampToDesktop(x, y));\n return true;\n }\n\n clickPointer(x: number, y: number, button = 0) {\n if (!this.client?.connected || this.display.viewOnly) return false;\n const [px, py] = this.#clampToDesktop(x, y);\n this.client.sendMouseMove(px, py);\n this.pointerButton(px, py, button, true);\n this.pointerButton(px, py, button, false);\n return true;\n }\n\n pointerButton(x: number, y: number, button = 0, pressed = true) {\n if (!this.client?.connected || this.display.viewOnly) return false;\n const [px, py] = this.#clampToDesktop(x, y);\n this.client.sendMouseButton(button, pressed, px, py);\n return true;\n }\n\n scrollPointer(x: number, y: number, delta: { x: number; y: number }) {\n if (!this.client?.connected || this.display.viewOnly) return false;\n const [px, py] = this.#clampToDesktop(x, y);\n for (let i = 0; i < Math.min(20, Math.abs(delta.y)); i++) {\n this.client.sendWheel(Math.sign(delta.y), px, py, false);\n }\n for (let i = 0; i < Math.min(20, Math.abs(delta.x)); i++) {\n this.client.sendWheel(Math.sign(delta.x), px, py, true);\n }\n return true;\n }\n\n /** Type a run of characters, which RDP carries as Unicode rather than keys. */\n typeText(text: string) {\n if (!this.client?.connected || this.display.viewOnly || !text) return false;\n let printable = '';\n const flushPrintable = () => {\n if (!printable) return;\n this.client.sendUnicode(printable);\n printable = '';\n };\n\n for (const character of text) {\n if (character === '\\n' || character === '\\r') {\n flushPrintable();\n this.client.sendKeyByCode('Enter', true);\n this.client.sendKeyByCode('Enter', false);\n } else if (character === '\\t') {\n flushPrintable();\n this.client.sendKeyByCode('Tab', true);\n this.client.sendKeyByCode('Tab', false);\n } else {\n printable += character;\n }\n }\n flushPrintable();\n return true;\n }\n\n #clampToDesktop(x: number, y: number) {\n return [\n Math.max(0, Math.min((this.display.width || 1) - 1, Math.round(x))),\n Math.max(0, Math.min((this.display.height || 1) - 1, Math.round(y))),\n ];\n }\n\n /** What the canvas is showing, for a screen capture or a coordinate check. */\n get screenGeometry() {\n return {\n width: this.display.width,\n height: this.display.height,\n viewport: { ...this.display.viewport },\n };\n }\n\n /**\n * Hand text to the host's clipboard, falling back to typing it when the host\n * declined the clipboard channel -- xrdp builds often do.\n */\n clipboardPasteFrom(text: string) {\n if (!text) return;\n\n const shared = this.client.setClipboardText(text);\n if (!shared || typeof shared.then !== 'function') {\n this.client.sendUnicode(text);\n return;\n }\n\n shared.then((acknowledged: boolean) => {\n if (!acknowledged) this.client.sendUnicode(text);\n });\n }\n\n /**\n * Deliver text into the session, which is what the clipboard drawer's button\n * has always visibly done. `clipboardPasteFrom` only loads the clipboard, the\n * way noVNC's method of that name does.\n */\n pasteText(text: string) {\n return text ? this.client.pasteToHost(text) : false;\n }\n\n releaseKeyboard(): void {\n // Anything held down has to go up on the host's side too, or the far\n // machine spends the rest of the session thinking Command is pressed.\n this.display.releaseAllKeys();\n this.display.blur();\n }\n\n /** True once the host has agreed to share its clipboard. */\n get clipboardShared() {\n return this.client.clipboardShared;\n }\n\n disconnect() {\n this.remoteAppLayoutObserver?.disconnect();\n this.client.disconnect();\n this.display.destroy();\n this._rfbConnectionState = 'disconnected';\n }\n}\n\nexport const rdpDriver: ProtocolDriver = {\n protocol: 'rdp',\n label: 'RDP Client',\n defaultPort: DEFAULT_PORTS.rdp,\n dialog: DIALOG,\n buildUrl,\n createSession: (container, url, options) => new RdpSession(container, url, options),\n // The RDP client logs its own progress, so there is nothing to add here.\n describeState: () => ({}),\n describeDisconnect,\n};\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|