replicas-computer 0.3.33 → 0.3.34
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 +80 -7
- package/dist/stream-viewer.js +36 -10
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3325,7 +3325,7 @@ async function computerStatusCommand() {
|
|
|
3325
3325
|
}
|
|
3326
3326
|
|
|
3327
3327
|
// src/version.ts
|
|
3328
|
-
var COMPUTER_USE_VERSION = "0.3.
|
|
3328
|
+
var COMPUTER_USE_VERSION = "0.3.34";
|
|
3329
3329
|
var COMPUTER_USE_PROTOCOL_VERSION = 1;
|
|
3330
3330
|
|
|
3331
3331
|
// src/program.ts
|
|
@@ -3568,7 +3568,7 @@ async function runComputerClient(argv) {
|
|
|
3568
3568
|
}
|
|
3569
3569
|
|
|
3570
3570
|
// src/media-server.ts
|
|
3571
|
-
import { spawn as spawn7 } from "child_process";
|
|
3571
|
+
import { execFile, spawn as spawn7 } from "child_process";
|
|
3572
3572
|
import { createHash as createHash3 } from "crypto";
|
|
3573
3573
|
import { createReadStream, existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync2 } from "fs";
|
|
3574
3574
|
import { createServer as createServer3 } from "http";
|
|
@@ -4166,6 +4166,11 @@ import { availableParallelism } from "os";
|
|
|
4166
4166
|
import { dirname as dirname6, resolve as resolve2 } from "path";
|
|
4167
4167
|
import { fileURLToPath } from "url";
|
|
4168
4168
|
|
|
4169
|
+
// src/media-protocol.ts
|
|
4170
|
+
var STREAM_PROTOCOL_VERSION = 3;
|
|
4171
|
+
var MEDIA_PACKET_HEADER_BYTES = 12;
|
|
4172
|
+
var CLIPBOARD_MAX_CHARS = 1e6;
|
|
4173
|
+
|
|
4169
4174
|
// src/input-validation.ts
|
|
4170
4175
|
var finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
4171
4176
|
var optionalFinite = (value) => value === void 0 || finite(value);
|
|
@@ -4178,7 +4183,10 @@ function validInput(value) {
|
|
|
4178
4183
|
case "keyframe":
|
|
4179
4184
|
case "retry-webrtc":
|
|
4180
4185
|
case "webrtc-ready":
|
|
4186
|
+
case "copy":
|
|
4181
4187
|
return true;
|
|
4188
|
+
case "paste":
|
|
4189
|
+
return typeof value.text === "string" && value.text.length <= CLIPBOARD_MAX_CHARS;
|
|
4182
4190
|
case "theme":
|
|
4183
4191
|
return value.theme === "light" || value.theme === "dark";
|
|
4184
4192
|
case "standby":
|
|
@@ -4216,10 +4224,6 @@ function parseInput(raw) {
|
|
|
4216
4224
|
return validInput(value) ? value : void 0;
|
|
4217
4225
|
}
|
|
4218
4226
|
|
|
4219
|
-
// src/media-protocol.ts
|
|
4220
|
-
var STREAM_PROTOCOL_VERSION = 3;
|
|
4221
|
-
var MEDIA_PACKET_HEADER_BYTES = 12;
|
|
4222
|
-
|
|
4223
4227
|
// src/pacer.ts
|
|
4224
4228
|
import { performance } from "perf_hooks";
|
|
4225
4229
|
var ringSize = 4096;
|
|
@@ -4850,9 +4854,14 @@ var InputBridge = class {
|
|
|
4850
4854
|
latestPosition;
|
|
4851
4855
|
pressedButtons = /* @__PURE__ */ new Set();
|
|
4852
4856
|
pressedKeys = /* @__PURE__ */ new Set();
|
|
4857
|
+
/** Input that arrived while xclip was taking the clipboard, held so it lands after the paste. */
|
|
4858
|
+
queue;
|
|
4853
4859
|
close() {
|
|
4854
4860
|
if (this.closed) return;
|
|
4855
4861
|
this.closed = true;
|
|
4862
|
+
const queue = this.queue;
|
|
4863
|
+
this.queue = void 0;
|
|
4864
|
+
if (queue) this.drain(queue);
|
|
4856
4865
|
this.release();
|
|
4857
4866
|
const child = this.process;
|
|
4858
4867
|
this.process = void 0;
|
|
@@ -4937,8 +4946,53 @@ var InputBridge = class {
|
|
|
4937
4946
|
}
|
|
4938
4947
|
return;
|
|
4939
4948
|
}
|
|
4949
|
+
if (message.type === "paste") {
|
|
4950
|
+
if (this.queue) this.queue.push(() => this.paste(message.text));
|
|
4951
|
+
else this.paste(message.text);
|
|
4952
|
+
return;
|
|
4953
|
+
}
|
|
4940
4954
|
if (message.type === "release") this.release();
|
|
4941
4955
|
}
|
|
4956
|
+
paste(text) {
|
|
4957
|
+
if (this.closed) return;
|
|
4958
|
+
if (!text) {
|
|
4959
|
+
this.write("keydown v");
|
|
4960
|
+
this.write("keyup v");
|
|
4961
|
+
return;
|
|
4962
|
+
}
|
|
4963
|
+
const queue = [];
|
|
4964
|
+
this.queue = queue;
|
|
4965
|
+
const done = () => {
|
|
4966
|
+
if (this.queue !== queue) return;
|
|
4967
|
+
this.queue = void 0;
|
|
4968
|
+
this.write("keydown v");
|
|
4969
|
+
this.write("keyup v");
|
|
4970
|
+
this.queue = queue;
|
|
4971
|
+
setTimeout(() => {
|
|
4972
|
+
if (this.queue !== queue) return;
|
|
4973
|
+
this.queue = void 0;
|
|
4974
|
+
this.drain(queue);
|
|
4975
|
+
}, 200);
|
|
4976
|
+
};
|
|
4977
|
+
const child = spawn6("xclip", ["-selection", "clipboard", "-i"], {
|
|
4978
|
+
env: { ...process.env, DISPLAY: this.display },
|
|
4979
|
+
stdio: ["pipe", "ignore", "ignore"]
|
|
4980
|
+
});
|
|
4981
|
+
child.stdin.on("error", () => {
|
|
4982
|
+
});
|
|
4983
|
+
child.once("error", done);
|
|
4984
|
+
child.once("exit", done);
|
|
4985
|
+
child.stdin.end(text);
|
|
4986
|
+
}
|
|
4987
|
+
drain(queue) {
|
|
4988
|
+
for (let run = queue.shift(); run; run = queue.shift()) {
|
|
4989
|
+
run();
|
|
4990
|
+
if (this.queue) {
|
|
4991
|
+
this.queue.push(...queue);
|
|
4992
|
+
return;
|
|
4993
|
+
}
|
|
4994
|
+
}
|
|
4995
|
+
}
|
|
4942
4996
|
release() {
|
|
4943
4997
|
if (this.pointerFlush) clearImmediate(this.pointerFlush);
|
|
4944
4998
|
this.pointerFlush = void 0;
|
|
@@ -4964,6 +5018,10 @@ var InputBridge = class {
|
|
|
4964
5018
|
this.pointer = void 0;
|
|
4965
5019
|
}
|
|
4966
5020
|
write(command) {
|
|
5021
|
+
if (this.queue) {
|
|
5022
|
+
this.queue.push(() => this.write(command));
|
|
5023
|
+
return;
|
|
5024
|
+
}
|
|
4967
5025
|
if (!this.process || this.process.exitCode !== null || this.process.stdin.destroyed) {
|
|
4968
5026
|
const child = spawn6("xte", [], {
|
|
4969
5027
|
env: { ...process.env, DISPLAY: this.display },
|
|
@@ -5080,6 +5138,9 @@ var standbyRung = 5;
|
|
|
5080
5138
|
var port = Number.parseInt(process.env.REPLICAS_DESKTOP_STREAM_PORT || String(DESKTOP_STREAM_PORT), 10);
|
|
5081
5139
|
var display = process.env.REPLICAS_DESKTOP_DISPLAY?.replace(/^([^:])/, ":$1") || ":99";
|
|
5082
5140
|
var webrtcEnabled = process.env.REPLICAS_DESKTOP_STREAM_WEBRTC !== "0";
|
|
5141
|
+
var readClipboard = () => new Promise((done) => {
|
|
5142
|
+
execFile("xclip", ["-selection", "clipboard", "-o"], { env: { ...process.env, DISPLAY: display }, timeout: 1e3, maxBuffer: CLIPBOARD_MAX_CHARS * 4 }, (error, stdout) => done(error ? "" : stdout));
|
|
5143
|
+
});
|
|
5083
5144
|
var colorMode = process.env.REPLICAS_DESKTOP_STREAM_COLOR === "legacy" ? "legacy" : "bt709";
|
|
5084
5145
|
var defaultRequest = parseStreamRequest(new URLSearchParams({
|
|
5085
5146
|
transport: process.env.REPLICAS_DESKTOP_STREAM_TRANSPORT ?? "",
|
|
@@ -5865,6 +5926,17 @@ async function runMediaServer() {
|
|
|
5865
5926
|
case "theme":
|
|
5866
5927
|
applyDesktopTheme(message.theme);
|
|
5867
5928
|
return;
|
|
5929
|
+
case "copy":
|
|
5930
|
+
void (async () => {
|
|
5931
|
+
const before = await readClipboard();
|
|
5932
|
+
let text = before;
|
|
5933
|
+
for (let attempt = 0; attempt < 10 && text === before; attempt += 1) {
|
|
5934
|
+
await new Promise((wait) => setTimeout(wait, 50));
|
|
5935
|
+
text = await readClipboard();
|
|
5936
|
+
}
|
|
5937
|
+
if (text) reply({ type: "clipboard", text });
|
|
5938
|
+
})();
|
|
5939
|
+
return;
|
|
5868
5940
|
case "standby":
|
|
5869
5941
|
if (viewer.standby === message.standby) return;
|
|
5870
5942
|
viewer.standby = message.standby;
|
|
@@ -6011,7 +6083,7 @@ async function runMediaServer() {
|
|
|
6011
6083
|
response.setHeader("cache-control", "private, max-age=31536000, immutable");
|
|
6012
6084
|
createReadStream(path).pipe(response);
|
|
6013
6085
|
});
|
|
6014
|
-
const webSocketServer = new WebSocketServer({ noServer: true, maxPayload:
|
|
6086
|
+
const webSocketServer = new WebSocketServer({ noServer: true, maxPayload: CLIPBOARD_MAX_CHARS * 8 });
|
|
6015
6087
|
httpServer.on("upgrade", (request2, socket, head) => {
|
|
6016
6088
|
const url = new URL(request2.url || "/", "http://localhost");
|
|
6017
6089
|
if (url.pathname !== "/stream") {
|
|
@@ -6136,6 +6208,7 @@ var dependencies = [
|
|
|
6136
6208
|
["PulseAudio", "pulseaudio"],
|
|
6137
6209
|
["PulseAudio control", "pactl"],
|
|
6138
6210
|
["X automation", "xte"],
|
|
6211
|
+
["xclip", "xclip"],
|
|
6139
6212
|
["x11vnc", "x11vnc"],
|
|
6140
6213
|
["websockify", "websockify"],
|
|
6141
6214
|
["Google Chrome", "google-chrome-stable"],
|
package/dist/stream-viewer.js
CHANGED
|
@@ -100,6 +100,11 @@ function isDesktopElement(value) {
|
|
|
100
100
|
return isRecord(value) && isDesktopRect(value.rect) && ["url", "tag", "selector"].every((key) => typeof value[key] === "string") && ["text", "label", "source"].every((key) => value[key] === void 0 || typeof value[key] === "string") && (value.components === void 0 || Array.isArray(value.components) && value.components.every((name) => typeof name === "string"));
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
// src/media-protocol.ts
|
|
104
|
+
var STREAM_PROTOCOL_VERSION = 3;
|
|
105
|
+
var MEDIA_PACKET_HEADER_BYTES = 12;
|
|
106
|
+
var CLIPBOARD_MAX_CHARS = 1e6;
|
|
107
|
+
|
|
103
108
|
// src/input-validation.ts
|
|
104
109
|
var finite = (value) => typeof value === "number" && Number.isFinite(value);
|
|
105
110
|
function validServerMessage(value) {
|
|
@@ -110,6 +115,8 @@ function validServerMessage(value) {
|
|
|
110
115
|
return finite(value.sentAt);
|
|
111
116
|
case "webrtc-failed":
|
|
112
117
|
return true;
|
|
118
|
+
case "clipboard":
|
|
119
|
+
return typeof value.text === "string";
|
|
113
120
|
case "offer":
|
|
114
121
|
return typeof value.sdp === "string" && value.sdp.length < 65536;
|
|
115
122
|
case "candidate":
|
|
@@ -129,10 +136,6 @@ function wheelDeltaPixels(event, pageHeight) {
|
|
|
129
136
|
return { deltaX: event.deltaX * scale, deltaY: event.deltaY * scale };
|
|
130
137
|
}
|
|
131
138
|
|
|
132
|
-
// src/media-protocol.ts
|
|
133
|
-
var STREAM_PROTOCOL_VERSION = 3;
|
|
134
|
-
var MEDIA_PACKET_HEADER_BYTES = 12;
|
|
135
|
-
|
|
136
139
|
// src/stream-viewer.ts
|
|
137
140
|
var screen = document.querySelector("#screen");
|
|
138
141
|
var status = document.querySelector("#status");
|
|
@@ -285,7 +288,7 @@ var sendInput = (message, unreliable = false) => {
|
|
|
285
288
|
pendingPointer = void 0;
|
|
286
289
|
}
|
|
287
290
|
}
|
|
288
|
-
if (config?.inputRace && transport && (message.type === "button" || message.type === "wheel" || message.type === "key" || message.type === "release")) {
|
|
291
|
+
if (config?.inputRace && transport && (message.type === "button" || message.type === "wheel" || message.type === "key" || message.type === "release" || message.type === "paste")) {
|
|
289
292
|
const ordered = socket?.readyState === WebSocket.OPEN ? socket : void 0;
|
|
290
293
|
const reliable = transport === "webrtc" && inputChannel?.readyState === "open" ? inputChannel : void 0;
|
|
291
294
|
const nextAction = actionId + 1;
|
|
@@ -302,7 +305,7 @@ var sendInput = (message, unreliable = false) => {
|
|
|
302
305
|
const viaSocket = deliver(ordered);
|
|
303
306
|
if (!viaSocket && !deliver(reliable)) return;
|
|
304
307
|
actionId = nextAction;
|
|
305
|
-
if (viaSocket && transport === "webrtc" && pointerChannel?.readyState === "open" && pointerChannel.bufferedAmount < 16384) {
|
|
308
|
+
if (viaSocket && message.type !== "paste" && transport === "webrtc" && pointerChannel?.readyState === "open" && pointerChannel.bufferedAmount < 16384) {
|
|
306
309
|
deliver(pointerChannel);
|
|
307
310
|
}
|
|
308
311
|
return;
|
|
@@ -315,7 +318,7 @@ var sendInput = (message, unreliable = false) => {
|
|
|
315
318
|
pointerChannel.send(JSON.stringify(message));
|
|
316
319
|
return;
|
|
317
320
|
}
|
|
318
|
-
if (!unreliable && ["pointer", "button", "wheel", "key", "release"].includes(message.type) && socket?.readyState === WebSocket.OPEN) {
|
|
321
|
+
if (!unreliable && ["pointer", "button", "wheel", "key", "release", "paste"].includes(message.type) && socket?.readyState === WebSocket.OPEN) {
|
|
319
322
|
sendSocket(message);
|
|
320
323
|
return;
|
|
321
324
|
}
|
|
@@ -801,7 +804,15 @@ var completeRetry = (attempt) => {
|
|
|
801
804
|
announceTransport();
|
|
802
805
|
unlockAudio();
|
|
803
806
|
};
|
|
807
|
+
var syncedClipboard;
|
|
804
808
|
var handleServerMessage = (message, viaSocket) => {
|
|
809
|
+
if (message.type === "clipboard") {
|
|
810
|
+
void navigator.clipboard?.writeText(message.text).then(() => {
|
|
811
|
+
syncedClipboard = message.text;
|
|
812
|
+
}, () => {
|
|
813
|
+
});
|
|
814
|
+
return;
|
|
815
|
+
}
|
|
805
816
|
if (message.type === "pong") {
|
|
806
817
|
const elapsed = performance.now() - message.sentAt;
|
|
807
818
|
if ((!viaSocket || transport !== "webrtc") && message.sentAt > rttSentAt) {
|
|
@@ -1009,17 +1020,32 @@ screen.addEventListener("wheel", (event) => {
|
|
|
1009
1020
|
send({ type: "wheel", ...normalizedPointer(event), ...wheelDeltaPixels(event, screen.clientHeight) });
|
|
1010
1021
|
}, { passive: false });
|
|
1011
1022
|
screen.addEventListener("contextmenu", (event) => event.preventDefault());
|
|
1023
|
+
var isMac = /Mac|iPhone|iPad/.test(navigator.platform);
|
|
1024
|
+
var desktopKey = (code) => isMac && code.startsWith("Meta") ? code.replace("Meta", "Control") : code;
|
|
1012
1025
|
window.addEventListener("keydown", (event) => {
|
|
1013
1026
|
if (!screen.matches(":focus")) return;
|
|
1014
|
-
event.preventDefault();
|
|
1015
1027
|
if (screen.muted || audioContext?.state !== "running") unlockAudio();
|
|
1016
|
-
|
|
1028
|
+
const shortcut = (event.ctrlKey || event.metaKey) && !event.altKey;
|
|
1029
|
+
if (shortcut && event.code === "KeyV") return;
|
|
1030
|
+
event.preventDefault();
|
|
1031
|
+
send({ type: "key", code: desktopKey(event.code), down: true, repeat: event.repeat });
|
|
1032
|
+
if (isMac && event.metaKey && !["Meta", "Control", "Shift", "Alt", "CapsLock"].includes(event.key)) send({ type: "key", code: event.code, down: false });
|
|
1033
|
+
if (shortcut && !event.repeat && (event.code === "KeyC" || event.code === "KeyX")) sendSocket({ type: "copy" });
|
|
1017
1034
|
}, { capture: true });
|
|
1018
1035
|
window.addEventListener("keyup", (event) => {
|
|
1019
1036
|
if (!screen.matches(":focus")) return;
|
|
1020
1037
|
event.preventDefault();
|
|
1021
|
-
send({ type: "key", code: event.code, down: false });
|
|
1038
|
+
send({ type: "key", code: desktopKey(event.code), down: false });
|
|
1022
1039
|
}, { capture: true });
|
|
1040
|
+
window.addEventListener("paste", (event) => {
|
|
1041
|
+
if (!screen.matches(":focus")) return;
|
|
1042
|
+
event.preventDefault();
|
|
1043
|
+
const text = event.clipboardData?.getData("text/plain") ?? "";
|
|
1044
|
+
if (text.length > CLIPBOARD_MAX_CHARS) return;
|
|
1045
|
+
const fresh = text !== syncedClipboard;
|
|
1046
|
+
if (fresh) syncedClipboard = text;
|
|
1047
|
+
send({ type: "paste", text: fresh ? text : "" });
|
|
1048
|
+
});
|
|
1023
1049
|
window.addEventListener("blur", () => send({ type: "release" }));
|
|
1024
1050
|
window.addEventListener("resize", fitScreen);
|
|
1025
1051
|
var setStandby = (next) => {
|