ppio-sandbox 2.0.4-a2 → 2.1.0-a2
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/{chunk-TKMNGUIC.mjs → chunk-46KYZWWN.mjs} +3253 -2366
- package/dist/chunk-46KYZWWN.mjs.map +1 -0
- package/dist/chunk-T5IKSUZH.mjs +468 -0
- package/dist/chunk-T5IKSUZH.mjs.map +1 -0
- package/dist/chunk-X3M4KXDB.mjs +533 -0
- package/dist/chunk-X3M4KXDB.mjs.map +1 -0
- package/dist/code-interpreter.d.mts +4 -202
- package/dist/code-interpreter.d.ts +4 -202
- package/dist/code-interpreter.js +7355 -5580
- package/dist/code-interpreter.js.map +1 -1
- package/dist/code-interpreter.mjs +18 -460
- package/dist/code-interpreter.mjs.map +1 -1
- package/dist/desktop.d.mts +2 -91
- package/dist/desktop.d.ts +2 -91
- package/dist/desktop.js +7394 -5687
- package/dist/desktop.js.map +1 -1
- package/dist/desktop.mjs +17 -525
- package/dist/desktop.mjs.map +1 -1
- package/dist/{index-CBdou_ua.d.mts → index-CRwaZUdl.d.mts} +2363 -1886
- package/dist/{index-CBdou_ua.d.ts → index-CRwaZUdl.d.ts} +2363 -1886
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +7410 -5188
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +13 -1
- package/dist/sandbox-JYRR7FR5.mjs +8 -0
- package/dist/sandbox-JYRR7FR5.mjs.map +1 -0
- package/dist/sandbox-V324H2RC.mjs +8 -0
- package/dist/sandbox-V324H2RC.mjs.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-TKMNGUIC.mjs.map +0 -1
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CommandExitError,
|
|
3
|
+
ConnectionConfig,
|
|
4
|
+
Sandbox,
|
|
5
|
+
TimeoutError,
|
|
6
|
+
__spreadProps,
|
|
7
|
+
__spreadValues
|
|
8
|
+
} from "./chunk-46KYZWWN.mjs";
|
|
9
|
+
|
|
10
|
+
// src/desktop/utils.ts
|
|
11
|
+
import { randomBytes } from "crypto";
|
|
12
|
+
function generateRandomString(length = 16) {
|
|
13
|
+
const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
14
|
+
const bytes = randomBytes(length);
|
|
15
|
+
let result = "";
|
|
16
|
+
for (let i = 0; i < length; i++) {
|
|
17
|
+
result += characters[bytes[i] % characters.length];
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/desktop/sandbox.ts
|
|
23
|
+
var MOUSE_BUTTONS = {
|
|
24
|
+
left: 1,
|
|
25
|
+
right: 3,
|
|
26
|
+
middle: 2
|
|
27
|
+
};
|
|
28
|
+
var KEYS = {
|
|
29
|
+
alt: "Alt_L",
|
|
30
|
+
alt_left: "Alt_L",
|
|
31
|
+
alt_right: "Alt_R",
|
|
32
|
+
backspace: "BackSpace",
|
|
33
|
+
break: "Pause",
|
|
34
|
+
caps_lock: "Caps_Lock",
|
|
35
|
+
cmd: "Super_L",
|
|
36
|
+
command: "Super_L",
|
|
37
|
+
control: "Control_L",
|
|
38
|
+
control_left: "Control_L",
|
|
39
|
+
control_right: "Control_R",
|
|
40
|
+
ctrl: "Control_L",
|
|
41
|
+
del: "Delete",
|
|
42
|
+
delete: "Delete",
|
|
43
|
+
down: "Down",
|
|
44
|
+
end: "End",
|
|
45
|
+
enter: "Return",
|
|
46
|
+
esc: "Escape",
|
|
47
|
+
escape: "Escape",
|
|
48
|
+
f1: "F1",
|
|
49
|
+
f2: "F2",
|
|
50
|
+
f3: "F3",
|
|
51
|
+
f4: "F4",
|
|
52
|
+
f5: "F5",
|
|
53
|
+
f6: "F6",
|
|
54
|
+
f7: "F7",
|
|
55
|
+
f8: "F8",
|
|
56
|
+
f9: "F9",
|
|
57
|
+
f10: "F10",
|
|
58
|
+
f11: "F11",
|
|
59
|
+
f12: "F12",
|
|
60
|
+
home: "Home",
|
|
61
|
+
insert: "Insert",
|
|
62
|
+
left: "Left",
|
|
63
|
+
menu: "Menu",
|
|
64
|
+
meta: "Meta_L",
|
|
65
|
+
num_lock: "Num_Lock",
|
|
66
|
+
page_down: "Page_Down",
|
|
67
|
+
page_up: "Page_Up",
|
|
68
|
+
pause: "Pause",
|
|
69
|
+
print: "Print",
|
|
70
|
+
right: "Right",
|
|
71
|
+
scroll_lock: "Scroll_Lock",
|
|
72
|
+
shift: "Shift_L",
|
|
73
|
+
shift_left: "Shift_L",
|
|
74
|
+
shift_right: "Shift_R",
|
|
75
|
+
space: "space",
|
|
76
|
+
super: "Super_L",
|
|
77
|
+
super_left: "Super_L",
|
|
78
|
+
super_right: "Super_R",
|
|
79
|
+
tab: "Tab",
|
|
80
|
+
up: "Up",
|
|
81
|
+
win: "Super_L",
|
|
82
|
+
windows: "Super_L"
|
|
83
|
+
};
|
|
84
|
+
function mapKey(key) {
|
|
85
|
+
const lowerKey = key.toLowerCase();
|
|
86
|
+
if (lowerKey in KEYS) {
|
|
87
|
+
return KEYS[lowerKey];
|
|
88
|
+
}
|
|
89
|
+
return lowerKey;
|
|
90
|
+
}
|
|
91
|
+
var Sandbox2 = class extends Sandbox {
|
|
92
|
+
/**
|
|
93
|
+
* Use {@link Sandbox.create} to create a new Sandbox instead.
|
|
94
|
+
*
|
|
95
|
+
* @hidden
|
|
96
|
+
* @hide
|
|
97
|
+
* @internal
|
|
98
|
+
* @access protected
|
|
99
|
+
*/
|
|
100
|
+
constructor(opts) {
|
|
101
|
+
var _a;
|
|
102
|
+
super(__spreadProps(__spreadValues({}, opts), { envdVersion: (_a = opts.envdVersion) != null ? _a : "0.0.0" }));
|
|
103
|
+
this.lastXfce4Pid = null;
|
|
104
|
+
this.display = ":0";
|
|
105
|
+
this.stream = new VNCServer(this);
|
|
106
|
+
}
|
|
107
|
+
static async create(templateOrOpts, opts) {
|
|
108
|
+
var _a, _b, _c;
|
|
109
|
+
const { template, sandboxOpts } = typeof templateOrOpts === "string" ? { template: templateOrOpts, sandboxOpts: opts } : { template: this.defaultTemplate, sandboxOpts: templateOrOpts };
|
|
110
|
+
const config = new ConnectionConfig(sandboxOpts);
|
|
111
|
+
const display = (opts == null ? void 0 : opts.display) || ":0";
|
|
112
|
+
const sandboxOptsWithDisplay = __spreadProps(__spreadValues({}, sandboxOpts), {
|
|
113
|
+
envs: __spreadProps(__spreadValues({}, sandboxOpts == null ? void 0 : sandboxOpts.envs), {
|
|
114
|
+
DISPLAY: display
|
|
115
|
+
})
|
|
116
|
+
});
|
|
117
|
+
let sbx;
|
|
118
|
+
if (config.debug) {
|
|
119
|
+
sbx = new this(__spreadValues(__spreadValues({
|
|
120
|
+
sandboxId: "desktop"
|
|
121
|
+
}, sandboxOptsWithDisplay), config));
|
|
122
|
+
} else {
|
|
123
|
+
const sandbox = await this.createSandbox(
|
|
124
|
+
template,
|
|
125
|
+
(_a = sandboxOptsWithDisplay == null ? void 0 : sandboxOptsWithDisplay.timeoutMs) != null ? _a : this.defaultSandboxTimeoutMs,
|
|
126
|
+
sandboxOptsWithDisplay
|
|
127
|
+
);
|
|
128
|
+
sbx = new this(__spreadValues(__spreadValues(__spreadValues({}, sandbox), sandboxOptsWithDisplay), config));
|
|
129
|
+
}
|
|
130
|
+
sbx.display = display;
|
|
131
|
+
sbx.lastXfce4Pid = null;
|
|
132
|
+
sbx.stream = new VNCServer(sbx);
|
|
133
|
+
const [width, height] = (_b = sandboxOpts == null ? void 0 : sandboxOpts.resolution) != null ? _b : [1024, 768];
|
|
134
|
+
await sbx.commands.run(
|
|
135
|
+
`Xvfb ${sbx.display} -ac -screen 0 ${width}x${height}x24 -retro -dpi ${(_c = sandboxOpts == null ? void 0 : sandboxOpts.dpi) != null ? _c : 96} -nolisten tcp -nolisten unix`,
|
|
136
|
+
{ background: true, timeoutMs: 0 }
|
|
137
|
+
);
|
|
138
|
+
let hasStarted = await sbx.waitAndVerify(
|
|
139
|
+
`xdpyinfo -display ${sbx.display}`,
|
|
140
|
+
(r) => r.exitCode === 0
|
|
141
|
+
);
|
|
142
|
+
if (!hasStarted) {
|
|
143
|
+
throw new TimeoutError("Could not start Xvfb");
|
|
144
|
+
}
|
|
145
|
+
await sbx.startXfce4();
|
|
146
|
+
return sbx;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Wait for a command to return a specific result.
|
|
150
|
+
* @param cmd - The command to run.
|
|
151
|
+
* @param onResult - The function to check the result of the command.
|
|
152
|
+
* @param timeout - The maximum time to wait for the command to return the result.
|
|
153
|
+
* @param interval - The interval to wait between checks.
|
|
154
|
+
* @returns `true` if the command returned the result within the timeout, otherwise `false`.
|
|
155
|
+
*/
|
|
156
|
+
async waitAndVerify(cmd, onResult, timeout = 10, interval = 0.5) {
|
|
157
|
+
let elapsed = 0;
|
|
158
|
+
while (elapsed < timeout) {
|
|
159
|
+
try {
|
|
160
|
+
if (onResult(await this.commands.run(cmd))) {
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
} catch (e) {
|
|
164
|
+
if (e instanceof CommandExitError) {
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
throw e;
|
|
168
|
+
}
|
|
169
|
+
await new Promise((resolve) => setTimeout(resolve, interval * 1e3));
|
|
170
|
+
elapsed += interval;
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Start xfce4 session if logged out or not running.
|
|
176
|
+
*/
|
|
177
|
+
async startXfce4() {
|
|
178
|
+
if (this.lastXfce4Pid === null || (await this.commands.run(
|
|
179
|
+
`ps aux | grep ${this.lastXfce4Pid} | grep -v grep | head -n 1`
|
|
180
|
+
)).stdout.trim().includes("[xfce4-session] <defunct>")) {
|
|
181
|
+
const result = await this.commands.run("startxfce4", {
|
|
182
|
+
background: true,
|
|
183
|
+
timeoutMs: 0
|
|
184
|
+
});
|
|
185
|
+
this.lastXfce4Pid = result.pid;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
async screenshot(format = "bytes") {
|
|
189
|
+
const path = `/tmp/screenshot-${generateRandomString()}.png`;
|
|
190
|
+
await this.commands.run(`scrot --pointer ${path}`);
|
|
191
|
+
const file = await this.files.read(path, { format });
|
|
192
|
+
this.files.remove(path);
|
|
193
|
+
return file;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Left click on the mouse position.
|
|
197
|
+
*/
|
|
198
|
+
async leftClick(x, y) {
|
|
199
|
+
if (x && y) {
|
|
200
|
+
await this.moveMouse(x, y);
|
|
201
|
+
}
|
|
202
|
+
await this.commands.run("xdotool click 1");
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Double left click on the mouse position.
|
|
206
|
+
*/
|
|
207
|
+
async doubleClick(x, y) {
|
|
208
|
+
if (x && y) {
|
|
209
|
+
await this.moveMouse(x, y);
|
|
210
|
+
}
|
|
211
|
+
await this.commands.run("xdotool click --repeat 2 1");
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Right click on the mouse position.
|
|
215
|
+
*/
|
|
216
|
+
async rightClick(x, y) {
|
|
217
|
+
if (x && y) {
|
|
218
|
+
await this.moveMouse(x, y);
|
|
219
|
+
}
|
|
220
|
+
await this.commands.run("xdotool click 3");
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Middle click on the mouse position.
|
|
224
|
+
*/
|
|
225
|
+
async middleClick(x, y) {
|
|
226
|
+
if (x && y) {
|
|
227
|
+
await this.moveMouse(x, y);
|
|
228
|
+
}
|
|
229
|
+
await this.commands.run("xdotool click 2");
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Scroll the mouse wheel by the given amount.
|
|
233
|
+
* @param direction - The direction to scroll. Can be "up" or "down".
|
|
234
|
+
* @param amount - The amount to scroll.
|
|
235
|
+
*/
|
|
236
|
+
async scroll(direction = "down", amount = 1) {
|
|
237
|
+
const button = direction === "up" ? "4" : "5";
|
|
238
|
+
await this.commands.run(`xdotool click --repeat ${amount} ${button}`);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Move the mouse to the given coordinates.
|
|
242
|
+
* @param x - The x coordinate.
|
|
243
|
+
* @param y - The y coordinate.
|
|
244
|
+
*/
|
|
245
|
+
async moveMouse(x, y) {
|
|
246
|
+
await this.commands.run(`xdotool mousemove --sync ${x} ${y}`);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Press the mouse button.
|
|
250
|
+
*/
|
|
251
|
+
async mousePress(button = "left") {
|
|
252
|
+
await this.commands.run(`xdotool mousedown ${MOUSE_BUTTONS[button]}`);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Release the mouse button.
|
|
256
|
+
*/
|
|
257
|
+
async mouseRelease(button = "left") {
|
|
258
|
+
await this.commands.run(`xdotool mouseup ${MOUSE_BUTTONS[button]}`);
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Get the current cursor position.
|
|
262
|
+
* @returns A object with the x and y coordinates
|
|
263
|
+
* @throws Error if cursor position cannot be determined
|
|
264
|
+
*/
|
|
265
|
+
async getCursorPosition() {
|
|
266
|
+
const result = await this.commands.run("xdotool getmouselocation");
|
|
267
|
+
const match = result.stdout.match(/x:(\d+)\s+y:(\d+)/);
|
|
268
|
+
if (!match) {
|
|
269
|
+
throw new Error(
|
|
270
|
+
`Failed to parse cursor position from output: ${result.stdout}`
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
const [, x, y] = match;
|
|
274
|
+
if (!x || !y) {
|
|
275
|
+
throw new Error(`Invalid cursor position values: x=${x}, y=${y}`);
|
|
276
|
+
}
|
|
277
|
+
return { x: parseInt(x), y: parseInt(y) };
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Get the current screen size.
|
|
281
|
+
* @returns An {@link ScreenSize} object
|
|
282
|
+
* @throws Error if screen size cannot be determined
|
|
283
|
+
*/
|
|
284
|
+
async getScreenSize() {
|
|
285
|
+
const result = await this.commands.run("xrandr");
|
|
286
|
+
const match = result.stdout.match(/(\d+x\d+)/);
|
|
287
|
+
if (!match) {
|
|
288
|
+
throw new Error(
|
|
289
|
+
`Failed to parse screen size from output: ${result.stdout}`
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
try {
|
|
293
|
+
const [width, height] = match[1].split("x").map((val) => parseInt(val));
|
|
294
|
+
return { width, height };
|
|
295
|
+
} catch (error) {
|
|
296
|
+
throw new Error(`Invalid screen size format: ${match[1]}`);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
*breakIntoChunks(text, n) {
|
|
300
|
+
for (let i = 0; i < text.length; i += n) {
|
|
301
|
+
yield text.slice(i, i + n);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
quoteString(s) {
|
|
305
|
+
if (!s) {
|
|
306
|
+
return "''";
|
|
307
|
+
}
|
|
308
|
+
if (!/[^\w@%+=:,./-]/.test(s)) {
|
|
309
|
+
return s;
|
|
310
|
+
}
|
|
311
|
+
return "'" + s.replace(/'/g, `'"'"'`) + "'";
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Write the given text at the current cursor position.
|
|
315
|
+
* @param text - The text to write.
|
|
316
|
+
* @param options - An object containing the chunk size and delay between each chunk of text.
|
|
317
|
+
* @param options.chunkSize - The size of each chunk of text to write. Default is 25 characters.
|
|
318
|
+
* @param options.delayInMs - The delay between each chunk of text. Default is 75 ms.
|
|
319
|
+
*/
|
|
320
|
+
async write(text, options = {
|
|
321
|
+
chunkSize: 25,
|
|
322
|
+
delayInMs: 75
|
|
323
|
+
}) {
|
|
324
|
+
const chunks = this.breakIntoChunks(text, options.chunkSize);
|
|
325
|
+
for (const chunk of chunks) {
|
|
326
|
+
await this.commands.run(
|
|
327
|
+
`xdotool type --delay ${options.delayInMs} ${this.quoteString(chunk)}`
|
|
328
|
+
);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Press a key.
|
|
333
|
+
* @param key - The key to press (e.g. "enter", "space", "backspace", etc.). Can be a single key or an array of keys.
|
|
334
|
+
*/
|
|
335
|
+
async press(key) {
|
|
336
|
+
if (Array.isArray(key)) {
|
|
337
|
+
key = key.map(mapKey).join("+");
|
|
338
|
+
} else {
|
|
339
|
+
key = mapKey(key);
|
|
340
|
+
}
|
|
341
|
+
await this.commands.run(`xdotool key ${key}`);
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Drag the mouse from the given position to the given position.
|
|
345
|
+
* @param from - The starting position.
|
|
346
|
+
* @param to - The ending position.
|
|
347
|
+
*/
|
|
348
|
+
async drag([x1, y1], [x2, y2]) {
|
|
349
|
+
await this.moveMouse(x1, y1);
|
|
350
|
+
await this.mousePress();
|
|
351
|
+
await this.moveMouse(x2, y2);
|
|
352
|
+
await this.mouseRelease();
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Wait for the given amount of time.
|
|
356
|
+
* @param ms - The amount of time to wait in milliseconds.
|
|
357
|
+
*/
|
|
358
|
+
async wait(ms) {
|
|
359
|
+
await this.commands.run(`sleep ${ms / 1e3}`);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Open a file or a URL in the default application.
|
|
363
|
+
* @param fileOrUrl - The file or URL to open.
|
|
364
|
+
*/
|
|
365
|
+
async open(fileOrUrl) {
|
|
366
|
+
await this.commands.run(`xdg-open ${fileOrUrl}`, {
|
|
367
|
+
background: true
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
371
|
+
* Get the current window ID.
|
|
372
|
+
* @returns The ID of the current window.
|
|
373
|
+
*/
|
|
374
|
+
async getCurrentWindowId() {
|
|
375
|
+
const result = await this.commands.run("xdotool getwindowfocus");
|
|
376
|
+
return result.stdout.trim();
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Get the window ID of the window with the given title.
|
|
380
|
+
* @param title - The title of the window.
|
|
381
|
+
* @returns The ID of the window.
|
|
382
|
+
*/
|
|
383
|
+
async getApplicationWindows(application) {
|
|
384
|
+
const result = await this.commands.run(
|
|
385
|
+
`xdotool search --onlyvisible --class ${application}`
|
|
386
|
+
);
|
|
387
|
+
return result.stdout.trim().split("\n");
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Get the title of the window with the given ID.
|
|
391
|
+
* @param windowId - The ID of the window.
|
|
392
|
+
* @returns The title of the window.
|
|
393
|
+
*/
|
|
394
|
+
async getWindowTitle(windowId) {
|
|
395
|
+
const result = await this.commands.run(`xdotool getwindowname ${windowId}`);
|
|
396
|
+
return result.stdout.trim();
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Launch an application.
|
|
400
|
+
* @param application - The application to launch.
|
|
401
|
+
* @param uri - The URI to open in the application.
|
|
402
|
+
*/
|
|
403
|
+
async launch(application, uri) {
|
|
404
|
+
await this.commands.run(`gtk-launch ${application} ${uri != null ? uri : ""}`, {
|
|
405
|
+
background: true,
|
|
406
|
+
timeoutMs: 0
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
Sandbox2.defaultTemplate = "desktop";
|
|
411
|
+
var VNCServer = class {
|
|
412
|
+
constructor(desktop) {
|
|
413
|
+
this.vncPort = 5900;
|
|
414
|
+
this.port = 6080;
|
|
415
|
+
this.novncAuthEnabled = false;
|
|
416
|
+
this.url = null;
|
|
417
|
+
this.novncHandle = null;
|
|
418
|
+
this.desktop = desktop;
|
|
419
|
+
this.novncCommand = `cd /opt/noVNC/utils && ./novnc_proxy --vnc localhost:${this.vncPort} --listen ${this.port} --web /opt/noVNC > /tmp/novnc.log 2>&1`;
|
|
420
|
+
}
|
|
421
|
+
getAuthKey() {
|
|
422
|
+
if (!this.password) {
|
|
423
|
+
throw new Error(
|
|
424
|
+
"Unable to retrieve stream auth key, check if requireAuth is enabled"
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
return this.password;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Set the VNC command to start the VNC server.
|
|
431
|
+
*/
|
|
432
|
+
async getVNCCommand(windowId) {
|
|
433
|
+
let pwdFlag = "-nopw";
|
|
434
|
+
if (this.novncAuthEnabled) {
|
|
435
|
+
await this.desktop.commands.run("mkdir -p ~/.vnc");
|
|
436
|
+
await this.desktop.commands.run(
|
|
437
|
+
`x11vnc -storepasswd ${this.password} ~/.vnc/passwd`
|
|
438
|
+
);
|
|
439
|
+
pwdFlag = "-usepw";
|
|
440
|
+
}
|
|
441
|
+
return `x11vnc -bg -display ${this.desktop.display} -forever -wait 50 -shared -rfbport ${this.vncPort} ${pwdFlag} 2>/tmp/x11vnc_stderr.log` + (windowId ? ` -id ${windowId}` : "");
|
|
442
|
+
}
|
|
443
|
+
async waitForPort(port) {
|
|
444
|
+
return await this.desktop.waitAndVerify(
|
|
445
|
+
`netstat -tuln | grep ":${port} "`,
|
|
446
|
+
(r) => r.stdout.trim() !== ""
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
/**
|
|
450
|
+
* Check if the VNC server is running.
|
|
451
|
+
* @returns Whether the VNC server is running.
|
|
452
|
+
*/
|
|
453
|
+
async checkVNCRunning() {
|
|
454
|
+
try {
|
|
455
|
+
const result = await this.desktop.commands.run("pgrep -x x11vnc");
|
|
456
|
+
return result.stdout.trim() !== "";
|
|
457
|
+
} catch (error) {
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* Get the URL to a web page with a stream of the desktop sandbox.
|
|
463
|
+
* @param autoConnect - Whether to automatically connect to the server after opening the URL.
|
|
464
|
+
* @param viewOnly - Whether to prevent user interaction through the client.
|
|
465
|
+
* @param resize - Whether to resize the view when the window resizes.
|
|
466
|
+
* @param authKey - The password to use to connect to the server.
|
|
467
|
+
* @returns The URL to connect to the VNC server.
|
|
468
|
+
*/
|
|
469
|
+
getUrl({
|
|
470
|
+
autoConnect = true,
|
|
471
|
+
viewOnly = false,
|
|
472
|
+
resize = "scale",
|
|
473
|
+
authKey
|
|
474
|
+
} = {}) {
|
|
475
|
+
if (this.url === null) {
|
|
476
|
+
throw new Error("Server is not running");
|
|
477
|
+
}
|
|
478
|
+
let url = new URL(this.url);
|
|
479
|
+
if (autoConnect) {
|
|
480
|
+
url.searchParams.set("autoconnect", "true");
|
|
481
|
+
}
|
|
482
|
+
if (viewOnly) {
|
|
483
|
+
url.searchParams.set("view_only", "true");
|
|
484
|
+
}
|
|
485
|
+
if (resize) {
|
|
486
|
+
url.searchParams.set("resize", resize);
|
|
487
|
+
}
|
|
488
|
+
if (authKey) {
|
|
489
|
+
url.searchParams.set("password", authKey);
|
|
490
|
+
}
|
|
491
|
+
return url.toString();
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Start the VNC server.
|
|
495
|
+
*/
|
|
496
|
+
async start(opts = {}) {
|
|
497
|
+
var _a, _b, _c;
|
|
498
|
+
if (await this.checkVNCRunning()) {
|
|
499
|
+
throw new Error("Stream is already running");
|
|
500
|
+
}
|
|
501
|
+
this.vncPort = (_a = opts.vncPort) != null ? _a : this.vncPort;
|
|
502
|
+
this.port = (_b = opts.port) != null ? _b : this.port;
|
|
503
|
+
this.novncAuthEnabled = (_c = opts.requireAuth) != null ? _c : this.novncAuthEnabled;
|
|
504
|
+
this.password = this.novncAuthEnabled ? generateRandomString() : void 0;
|
|
505
|
+
this.url = new URL(`https://${this.desktop.getHost(this.port)}/vnc.html`);
|
|
506
|
+
const vncCommand = await this.getVNCCommand(opts.windowId);
|
|
507
|
+
await this.desktop.commands.run(vncCommand);
|
|
508
|
+
this.novncHandle = await this.desktop.commands.run(this.novncCommand, {
|
|
509
|
+
background: true,
|
|
510
|
+
timeoutMs: 0
|
|
511
|
+
});
|
|
512
|
+
if (!await this.waitForPort(this.port)) {
|
|
513
|
+
throw new Error("Could not start noVNC server");
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Stop the VNC server.
|
|
518
|
+
*/
|
|
519
|
+
async stop() {
|
|
520
|
+
if (await this.checkVNCRunning()) {
|
|
521
|
+
await this.desktop.commands.run("pkill x11vnc");
|
|
522
|
+
}
|
|
523
|
+
if (this.novncHandle) {
|
|
524
|
+
await this.novncHandle.kill();
|
|
525
|
+
this.novncHandle = null;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
export {
|
|
531
|
+
Sandbox2 as Sandbox
|
|
532
|
+
};
|
|
533
|
+
//# sourceMappingURL=chunk-X3M4KXDB.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/desktop/utils.ts","../src/desktop/sandbox.ts"],"sourcesContent":["import { randomBytes } from 'crypto';\n\nexport function generateRandomString(length: number = 16): string {\n const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n const bytes = randomBytes(length);\n let result = '';\n\n for (let i = 0; i < length; i++) {\n result += characters[bytes[i] % characters.length];\n }\n\n return result;\n}","import {\n Sandbox as SandboxBase,\n SandboxOpts as SandboxOptsBase,\n CommandHandle,\n CommandResult,\n CommandExitError,\n ConnectionConfig,\n TimeoutError,\n} from '../core'\n\nimport { generateRandomString } from './utils'\n\ninterface CursorPosition {\n x: number\n y: number\n}\n\ninterface ScreenSize {\n width: number\n height: number\n}\n\nconst MOUSE_BUTTONS = {\n left: 1,\n right: 3,\n middle: 2,\n}\n\nconst KEYS = {\n alt: 'Alt_L',\n alt_left: 'Alt_L',\n alt_right: 'Alt_R',\n backspace: 'BackSpace',\n break: 'Pause',\n caps_lock: 'Caps_Lock',\n cmd: 'Super_L',\n command: 'Super_L',\n control: 'Control_L',\n control_left: 'Control_L',\n control_right: 'Control_R',\n ctrl: 'Control_L',\n del: 'Delete',\n delete: 'Delete',\n down: 'Down',\n end: 'End',\n enter: 'Return',\n esc: 'Escape',\n escape: 'Escape',\n f1: 'F1',\n f2: 'F2',\n f3: 'F3',\n f4: 'F4',\n f5: 'F5',\n f6: 'F6',\n f7: 'F7',\n f8: 'F8',\n f9: 'F9',\n f10: 'F10',\n f11: 'F11',\n f12: 'F12',\n home: 'Home',\n insert: 'Insert',\n left: 'Left',\n menu: 'Menu',\n meta: 'Meta_L',\n num_lock: 'Num_Lock',\n page_down: 'Page_Down',\n page_up: 'Page_Up',\n pause: 'Pause',\n print: 'Print',\n right: 'Right',\n scroll_lock: 'Scroll_Lock',\n shift: 'Shift_L',\n shift_left: 'Shift_L',\n shift_right: 'Shift_R',\n space: 'space',\n super: 'Super_L',\n super_left: 'Super_L',\n super_right: 'Super_R',\n tab: 'Tab',\n up: 'Up',\n win: 'Super_L',\n windows: 'Super_L',\n}\n\nfunction mapKey(key: string): string {\n const lowerKey = key.toLowerCase()\n if (lowerKey in KEYS) {\n return KEYS[lowerKey as keyof typeof KEYS]\n }\n return lowerKey\n}\n\n/**\n * Configuration options for the Sandbox environment.\n * @interface SandboxOpts\n * @extends {SandboxOptsBase}\n */\nexport interface SandboxOpts extends SandboxOptsBase {\n /**\n * The screen resolution in pixels, specified as [width, height].\n * @type {[number, number]}\n */\n resolution?: [number, number]\n\n /**\n * Dots per inch (DPI) setting for the display.\n * @type {number}\n */\n dpi?: number\n\n /**\n * Display identifier.\n * @type {string}\n */\n display?: string\n}\n\nexport class Sandbox extends SandboxBase {\n protected static override readonly defaultTemplate: string = 'desktop'\n private lastXfce4Pid: number | null = null\n public display: string = ':0'\n public stream: VNCServer = new VNCServer(this)\n\n /**\n * Use {@link Sandbox.create} to create a new Sandbox instead.\n *\n * @hidden\n * @hide\n * @internal\n * @access protected\n */\n constructor(\n opts: Omit<SandboxOpts, 'timeoutMs' | 'metadata'> & {\n sandboxId: string\n envdVersion?: string\n }\n ) {\n super({ ...opts, envdVersion: opts.envdVersion ?? '0.0.0' })\n }\n /**\n * Create a new sandbox from the default `desktop` sandbox template.\n *\n * @param opts connection options.\n *\n * @returns sandbox instance for the new sandbox.\n *\n * @example\n * ```ts\n * const sandbox = await Sandbox.create()\n * ```\n * @constructs Sandbox\n */\n static async create<S extends typeof Sandbox>(\n this: S,\n opts?: SandboxOpts\n ): Promise<InstanceType<S>>\n /**\n * Create a new sandbox from the specified sandbox template.\n *\n * @param template sandbox template name or ID.\n * @param opts connection options.\n *\n * @returns sandbox instance for the new sandbox.\n *\n * @example\n * ```ts\n * const sandbox = await Sandbox.create('<template-name-or-id>')\n * ```\n * @constructs Sandbox\n */\n static async create<S extends typeof Sandbox>(\n this: S,\n template: string,\n opts?: SandboxOpts\n ): Promise<InstanceType<S>>\n static async create<S extends typeof Sandbox>(\n this: S,\n templateOrOpts?: SandboxOpts | string,\n opts?: SandboxOpts\n ): Promise<InstanceType<S>> {\n const { template, sandboxOpts } =\n typeof templateOrOpts === 'string'\n ? { template: templateOrOpts, sandboxOpts: opts }\n : { template: this.defaultTemplate, sandboxOpts: templateOrOpts }\n\n const config = new ConnectionConfig(sandboxOpts)\n\n // Add DISPLAY environment variable if not already set\n const display = opts?.display || ':0'\n const sandboxOptsWithDisplay = {\n ...sandboxOpts,\n envs: {\n ...sandboxOpts?.envs,\n DISPLAY: display,\n },\n }\n\n let sbx\n if (config.debug) {\n sbx = new this({\n sandboxId: 'desktop',\n ...sandboxOptsWithDisplay,\n ...config,\n }) as InstanceType<S>\n } else {\n const sandbox = await this.createSandbox(\n template,\n sandboxOptsWithDisplay?.timeoutMs ?? this.defaultSandboxTimeoutMs,\n sandboxOptsWithDisplay\n )\n sbx = new this({\n ...sandbox,\n ...sandboxOptsWithDisplay,\n ...config,\n }) as InstanceType<S>\n }\n\n sbx.display = display\n sbx.lastXfce4Pid = null\n sbx.stream = new VNCServer(sbx)\n\n const [width, height] = sandboxOpts?.resolution ?? [1024, 768]\n await sbx.commands.run(\n `Xvfb ${sbx.display} -ac -screen 0 ${width}x${height}x24 ` +\n `-retro -dpi ${sandboxOpts?.dpi ?? 96} -nolisten tcp -nolisten unix`,\n { background: true, timeoutMs: 0 }\n )\n\n let hasStarted = await sbx.waitAndVerify(\n `xdpyinfo -display ${sbx.display}`,\n (r: CommandResult) => r.exitCode === 0\n )\n if (!hasStarted) {\n throw new TimeoutError('Could not start Xvfb')\n }\n\n await sbx.startXfce4()\n\n return sbx\n }\n\n /**\n * Wait for a command to return a specific result.\n * @param cmd - The command to run.\n * @param onResult - The function to check the result of the command.\n * @param timeout - The maximum time to wait for the command to return the result.\n * @param interval - The interval to wait between checks.\n * @returns `true` if the command returned the result within the timeout, otherwise `false`.\n */\n async waitAndVerify(\n cmd: string,\n onResult: (result: CommandResult) => boolean,\n timeout: number = 10,\n interval: number = 0.5\n ): Promise<boolean> {\n let elapsed = 0\n\n while (elapsed < timeout) {\n try {\n if (onResult(await this.commands.run(cmd))) {\n return true\n }\n } catch (e) {\n if (e instanceof CommandExitError) {\n continue\n }\n throw e\n }\n\n await new Promise((resolve) => setTimeout(resolve, interval * 1000))\n elapsed += interval\n }\n\n return false\n }\n\n /**\n * Start xfce4 session if logged out or not running.\n */\n private async startXfce4(): Promise<void> {\n if (\n this.lastXfce4Pid === null ||\n (\n await this.commands.run(\n `ps aux | grep ${this.lastXfce4Pid} | grep -v grep | head -n 1`\n )\n ).stdout\n .trim()\n .includes('[xfce4-session] <defunct>')\n ) {\n const result = await this.commands.run('startxfce4', {\n background: true,\n timeoutMs: 0,\n })\n this.lastXfce4Pid = result.pid\n }\n }\n\n /**\n * Take a screenshot and save it to the given name.\n * @param format - The format of the screenshot.\n * @returns A Uint8Array bytes representation of the screenshot.\n */\n async screenshot(): Promise<Uint8Array>\n /**\n * Take a screenshot and save it to the given name.\n * @param format - The format of the screenshot.\n * @returns A Uint8Array bytes representation of the screenshot.\n */\n async screenshot(format: 'bytes'): Promise<Uint8Array>\n /**\n * Take a screenshot and save it to the given name.\n * @returns A Blob representation of the screenshot.\n */\n async screenshot(format: 'blob'): Promise<Blob>\n /**\n * Take a screenshot and save it to the given name.\n * @returns A ReadableStream of bytes representation of the screenshot.\n */\n async screenshot(format: 'stream'): Promise<ReadableStream<Uint8Array>>\n async screenshot(format: 'bytes' | 'blob' | 'stream' = 'bytes') {\n const path = `/tmp/screenshot-${generateRandomString()}.png`\n await this.commands.run(`scrot --pointer ${path}`)\n\n // @ts-expect-error\n const file = await this.files.read(path, { format })\n this.files.remove(path)\n return file\n }\n\n /**\n * Left click on the mouse position.\n */\n async leftClick(x?: number, y?: number): Promise<void> {\n if (x && y) {\n await this.moveMouse(x, y)\n }\n\n await this.commands.run('xdotool click 1')\n }\n\n /**\n * Double left click on the mouse position.\n */\n async doubleClick(x?: number, y?: number): Promise<void> {\n if (x && y) {\n await this.moveMouse(x, y)\n }\n\n await this.commands.run('xdotool click --repeat 2 1')\n }\n\n /**\n * Right click on the mouse position.\n */\n async rightClick(x?: number, y?: number): Promise<void> {\n if (x && y) {\n await this.moveMouse(x, y)\n }\n\n await this.commands.run('xdotool click 3')\n }\n\n /**\n * Middle click on the mouse position.\n */\n async middleClick(x?: number, y?: number): Promise<void> {\n if (x && y) {\n await this.moveMouse(x, y)\n }\n\n await this.commands.run('xdotool click 2')\n }\n\n /**\n * Scroll the mouse wheel by the given amount.\n * @param direction - The direction to scroll. Can be \"up\" or \"down\".\n * @param amount - The amount to scroll.\n */\n async scroll(\n direction: 'up' | 'down' = 'down',\n amount: number = 1\n ): Promise<void> {\n const button = direction === 'up' ? '4' : '5'\n await this.commands.run(`xdotool click --repeat ${amount} ${button}`)\n }\n\n /**\n * Move the mouse to the given coordinates.\n * @param x - The x coordinate.\n * @param y - The y coordinate.\n */\n async moveMouse(x: number, y: number): Promise<void> {\n await this.commands.run(`xdotool mousemove --sync ${x} ${y}`)\n }\n\n /**\n * Press the mouse button.\n */\n async mousePress(\n button: 'left' | 'right' | 'middle' = 'left'\n ): Promise<void> {\n await this.commands.run(`xdotool mousedown ${MOUSE_BUTTONS[button]}`)\n }\n\n /**\n * Release the mouse button.\n */\n async mouseRelease(\n button: 'left' | 'right' | 'middle' = 'left'\n ): Promise<void> {\n await this.commands.run(`xdotool mouseup ${MOUSE_BUTTONS[button]}`)\n }\n\n /**\n * Get the current cursor position.\n * @returns A object with the x and y coordinates\n * @throws Error if cursor position cannot be determined\n */\n async getCursorPosition(): Promise<CursorPosition> {\n const result = await this.commands.run('xdotool getmouselocation')\n\n const match = result.stdout.match(/x:(\\d+)\\s+y:(\\d+)/)\n if (!match) {\n throw new Error(\n `Failed to parse cursor position from output: ${result.stdout}`\n )\n }\n\n const [, x, y] = match\n if (!x || !y) {\n throw new Error(`Invalid cursor position values: x=${x}, y=${y}`)\n }\n\n return { x: parseInt(x), y: parseInt(y) }\n }\n\n /**\n * Get the current screen size.\n * @returns An {@link ScreenSize} object\n * @throws Error if screen size cannot be determined\n */\n async getScreenSize(): Promise<ScreenSize> {\n const result = await this.commands.run('xrandr')\n\n const match = result.stdout.match(/(\\d+x\\d+)/)\n if (!match) {\n throw new Error(\n `Failed to parse screen size from output: ${result.stdout}`\n )\n }\n\n try {\n const [width, height] = match[1].split('x').map((val) => parseInt(val))\n return { width, height }\n } catch (error) {\n throw new Error(`Invalid screen size format: ${match[1]}`)\n }\n }\n\n private *breakIntoChunks(text: string, n: number): Generator<string> {\n for (let i = 0; i < text.length; i += n) {\n yield text.slice(i, i + n)\n }\n }\n\n private quoteString(s: string): string {\n if (!s) {\n return \"''\"\n }\n\n if (!/[^\\w@%+=:,./-]/.test(s)) {\n return s\n }\n\n // use single quotes, and put single quotes into double quotes\n // the string $'b is then quoted as '$'\"'\"'b'\n return \"'\" + s.replace(/'/g, \"'\\\"'\\\"'\") + \"'\"\n }\n\n /**\n * Write the given text at the current cursor position.\n * @param text - The text to write.\n * @param options - An object containing the chunk size and delay between each chunk of text.\n * @param options.chunkSize - The size of each chunk of text to write. Default is 25 characters.\n * @param options.delayInMs - The delay between each chunk of text. Default is 75 ms.\n */\n async write(\n text: string,\n options: { chunkSize: number; delayInMs: number } = {\n chunkSize: 25,\n delayInMs: 75,\n }\n ): Promise<void> {\n const chunks = this.breakIntoChunks(text, options.chunkSize)\n\n for (const chunk of chunks) {\n await this.commands.run(\n `xdotool type --delay ${options.delayInMs} ${this.quoteString(chunk)}`\n )\n }\n }\n\n /**\n * Press a key.\n * @param key - The key to press (e.g. \"enter\", \"space\", \"backspace\", etc.). Can be a single key or an array of keys.\n */\n async press(key: string | string[]): Promise<void> {\n if (Array.isArray(key)) {\n key = key.map(mapKey).join('+')\n } else {\n key = mapKey(key)\n }\n\n await this.commands.run(`xdotool key ${key}`)\n }\n\n /**\n * Drag the mouse from the given position to the given position.\n * @param from - The starting position.\n * @param to - The ending position.\n */\n async drag(\n [x1, y1]: [number, number],\n [x2, y2]: [number, number]\n ): Promise<void> {\n await this.moveMouse(x1, y1)\n await this.mousePress()\n await this.moveMouse(x2, y2)\n await this.mouseRelease()\n }\n\n /**\n * Wait for the given amount of time.\n * @param ms - The amount of time to wait in milliseconds.\n */\n async wait(ms: number): Promise<void> {\n await this.commands.run(`sleep ${ms / 1000}`)\n }\n\n /**\n * Open a file or a URL in the default application.\n * @param fileOrUrl - The file or URL to open.\n */\n async open(fileOrUrl: string): Promise<void> {\n await this.commands.run(`xdg-open ${fileOrUrl}`, {\n background: true,\n })\n }\n\n /**\n * Get the current window ID.\n * @returns The ID of the current window.\n */\n async getCurrentWindowId(): Promise<string> {\n const result = await this.commands.run('xdotool getwindowfocus')\n return result.stdout.trim()\n }\n\n /**\n * Get the window ID of the window with the given title.\n * @param title - The title of the window.\n * @returns The ID of the window.\n */\n async getApplicationWindows(application: string): Promise<string[]> {\n const result = await this.commands.run(\n `xdotool search --onlyvisible --class ${application}`\n )\n\n return result.stdout.trim().split('\\n')\n }\n\n /**\n * Get the title of the window with the given ID.\n * @param windowId - The ID of the window.\n * @returns The title of the window.\n */\n async getWindowTitle(windowId: string): Promise<string> {\n const result = await this.commands.run(`xdotool getwindowname ${windowId}`)\n\n return result.stdout.trim()\n }\n\n /**\n * Launch an application.\n * @param application - The application to launch.\n * @param uri - The URI to open in the application.\n */\n async launch(application: string, uri?: string): Promise<void> {\n await this.commands.run(`gtk-launch ${application} ${uri ?? ''}`, {\n background: true,\n timeoutMs: 0,\n })\n }\n}\n\ninterface VNCServerOptions {\n vncPort?: number\n port?: number\n requireAuth?: boolean\n windowId?: string\n}\n\ninterface UrlOptions {\n autoConnect?: boolean\n viewOnly?: boolean\n resize?: 'off' | 'scale' | 'remote'\n authKey?: string\n}\n\n// Modified VNCServer class\nclass VNCServer {\n private vncPort: number = 5900\n private port: number = 6080\n private novncAuthEnabled: boolean = false\n private url: URL | null = null\n private novncHandle: CommandHandle | null = null\n private password: string | undefined\n private readonly novncCommand: string\n private readonly desktop: Sandbox\n\n constructor(desktop: Sandbox) {\n this.desktop = desktop\n this.novncCommand =\n `cd /opt/noVNC/utils && ./novnc_proxy --vnc localhost:${this.vncPort} ` +\n `--listen ${this.port} --web /opt/noVNC > /tmp/novnc.log 2>&1`\n }\n\n public getAuthKey(): string {\n if (!this.password) {\n throw new Error(\n 'Unable to retrieve stream auth key, check if requireAuth is enabled'\n )\n }\n\n return this.password\n }\n\n /**\n * Set the VNC command to start the VNC server.\n */\n private async getVNCCommand(windowId?: string): Promise<string> {\n let pwdFlag = '-nopw'\n if (this.novncAuthEnabled) {\n // Create .vnc directory if it doesn't exist\n await this.desktop.commands.run('mkdir -p ~/.vnc')\n await this.desktop.commands.run(\n `x11vnc -storepasswd ${this.password} ~/.vnc/passwd`\n )\n pwdFlag = '-usepw'\n }\n\n return (\n `x11vnc -bg -display ${this.desktop.display} -forever -wait 50 -shared ` +\n `-rfbport ${this.vncPort} ${pwdFlag} 2>/tmp/x11vnc_stderr.log` +\n (windowId ? ` -id ${windowId}` : '')\n )\n }\n\n private async waitForPort(port: number): Promise<boolean> {\n return await this.desktop.waitAndVerify(\n `netstat -tuln | grep \":${port} \"`,\n (r: CommandResult) => r.stdout.trim() !== ''\n )\n }\n\n /**\n * Check if the VNC server is running.\n * @returns Whether the VNC server is running.\n */\n private async checkVNCRunning(): Promise<boolean> {\n try {\n const result = await this.desktop.commands.run('pgrep -x x11vnc')\n return result.stdout.trim() !== ''\n } catch (error) {\n return false\n }\n }\n\n /**\n * Get the URL to a web page with a stream of the desktop sandbox.\n * @param autoConnect - Whether to automatically connect to the server after opening the URL.\n * @param viewOnly - Whether to prevent user interaction through the client.\n * @param resize - Whether to resize the view when the window resizes.\n * @param authKey - The password to use to connect to the server.\n * @returns The URL to connect to the VNC server.\n */\n public getUrl({\n autoConnect = true,\n viewOnly = false,\n resize = 'scale',\n authKey,\n }: UrlOptions = {}): string {\n if (this.url === null) {\n throw new Error('Server is not running')\n }\n\n let url = new URL(this.url)\n if (autoConnect) {\n url.searchParams.set('autoconnect', 'true')\n }\n if (viewOnly) {\n url.searchParams.set('view_only', 'true')\n }\n if (resize) {\n url.searchParams.set('resize', resize)\n }\n if (authKey) {\n url.searchParams.set('password', authKey)\n }\n return url.toString()\n }\n\n /**\n * Start the VNC server.\n */\n public async start(opts: VNCServerOptions = {}): Promise<void> {\n // If stream is already running, throw an error.\n if (await this.checkVNCRunning()) {\n throw new Error('Stream is already running')\n }\n\n this.vncPort = opts.vncPort ?? this.vncPort\n this.port = opts.port ?? this.port\n this.novncAuthEnabled = opts.requireAuth ?? this.novncAuthEnabled\n this.password = this.novncAuthEnabled ? generateRandomString() : undefined\n this.url = new URL(`https://${this.desktop.getHost(this.port)}/vnc.html`)\n\n const vncCommand = await this.getVNCCommand(opts.windowId)\n await this.desktop.commands.run(vncCommand)\n\n this.novncHandle = await this.desktop.commands.run(this.novncCommand, {\n background: true,\n timeoutMs: 0,\n })\n if (!(await this.waitForPort(this.port))) {\n throw new Error('Could not start noVNC server')\n }\n }\n\n /**\n * Stop the VNC server.\n */\n public async stop(): Promise<void> {\n if (await this.checkVNCRunning()) {\n await this.desktop.commands.run('pkill x11vnc')\n }\n\n if (this.novncHandle) {\n await this.novncHandle.kill()\n this.novncHandle = null\n }\n }\n}\n"],"mappings":"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","names":["Sandbox"]}
|