replicas-cli 0.2.349 → 0.2.351
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.mjs +76 -37
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -9601,7 +9601,7 @@ var HOOK_EXEC_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
|
9601
9601
|
var REPLICAS_CONFIG_FILENAMES = ["replicas.json", "replicas.yaml", "replicas.yml"];
|
|
9602
9602
|
|
|
9603
9603
|
// ../shared/src/cli-version.ts
|
|
9604
|
-
var CLI_VERSION = "0.2.
|
|
9604
|
+
var CLI_VERSION = "0.2.351";
|
|
9605
9605
|
|
|
9606
9606
|
// ../shared/src/version.ts
|
|
9607
9607
|
function compareVersions(v1, v2) {
|
|
@@ -9661,6 +9661,7 @@ var WORKSPACE_FILE_CONTENT_MAX_SIZE_BYTES = 1 * 1024 * 1024;
|
|
|
9661
9661
|
var WORKSPACE_SIDEBAR_VIEWS = ["owned", "shared", "team", "automated"];
|
|
9662
9662
|
var WORKSPACE_STATUS_FILTERS = WORKSPACE_STATUSES.map((status) => status === "error" ? "failed" : status);
|
|
9663
9663
|
var WORKSPACE_SORT_CHOICES = [
|
|
9664
|
+
["manual", "Custom"],
|
|
9664
9665
|
["activity", "Last activity"],
|
|
9665
9666
|
["created", "Created date"],
|
|
9666
9667
|
["status", "Status"]
|
|
@@ -14796,21 +14797,23 @@ async function learningsDeleteCommand(id) {
|
|
|
14796
14797
|
// src/commands/computer/index.ts
|
|
14797
14798
|
import { spawn as spawn4, spawnSync as spawnSync3 } from "child_process";
|
|
14798
14799
|
import { createHash as createHash2 } from "crypto";
|
|
14799
|
-
import { closeSync, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as
|
|
14800
|
-
import { dirname as
|
|
14800
|
+
import { closeSync, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, openSync, readFileSync as readFileSync3, readSync, rmSync as rmSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
14801
|
+
import { dirname as dirname3 } from "path";
|
|
14801
14802
|
import chalk21 from "chalk";
|
|
14802
14803
|
|
|
14803
14804
|
// src/commands/computer/desktop.ts
|
|
14804
14805
|
import { spawnSync } from "child_process";
|
|
14805
|
-
import { existsSync } from "fs";
|
|
14806
|
-
import { isAbsolute, resolve } from "path";
|
|
14806
|
+
import { existsSync, mkdirSync, readFileSync } from "fs";
|
|
14807
|
+
import { dirname, isAbsolute, resolve } from "path";
|
|
14807
14808
|
var STATE_DIR = process.env.REPLICAS_DESKTOP_STATE_DIR || "/tmp/replicas-computer";
|
|
14808
14809
|
var DEFAULT_DISPLAY = process.env.REPLICAS_DESKTOP_DISPLAY || ":99";
|
|
14809
14810
|
var NOVNC_PORT = process.env.REPLICAS_DESKTOP_NOVNC_PORT ? parseInt(process.env.REPLICAS_DESKTOP_NOVNC_PORT, 10) : DESKTOP_NOVNC_PORT;
|
|
14810
14811
|
var SERVICES_SCRIPT = "/usr/local/bin/replicas-start-desktop-services";
|
|
14812
|
+
var INPUT_LOCK_FILE = process.env.REPLICAS_DESKTOP_INPUT_LOCK_FILE || `${STATE_DIR}/input.lock`;
|
|
14811
14813
|
var CHROME_WRAPPER = "/usr/local/bin/replicas-chrome";
|
|
14812
14814
|
var INFO_WAIT_TIMEOUT_MS = 1e4;
|
|
14813
14815
|
var INFO_WAIT_INTERVAL_MS = 500;
|
|
14816
|
+
var lastDesktopHealthAt = 0;
|
|
14814
14817
|
var CHROME_DEBUG_PORT = (() => {
|
|
14815
14818
|
const value = process.env.REPLICAS_DESKTOP_CHROME_DEBUG_PORT;
|
|
14816
14819
|
if (!value) return 9222;
|
|
@@ -14823,16 +14826,49 @@ var CHROME_DEBUG_PORT = (() => {
|
|
|
14823
14826
|
function fail(msg) {
|
|
14824
14827
|
throw new Error(msg);
|
|
14825
14828
|
}
|
|
14829
|
+
function getDesktopBridgeStatus() {
|
|
14830
|
+
const r = spawnSync("bash", [SERVICES_SCRIPT, "--status-json"], { stdio: "pipe" });
|
|
14831
|
+
if (r.status !== 0) return null;
|
|
14832
|
+
try {
|
|
14833
|
+
return JSON.parse(r.stdout.toString());
|
|
14834
|
+
} catch {
|
|
14835
|
+
return null;
|
|
14836
|
+
}
|
|
14837
|
+
}
|
|
14838
|
+
function desktopStackHealthy() {
|
|
14839
|
+
const pids = {};
|
|
14840
|
+
for (const name of ["openbox", "tint2", "x11vnc", "novnc"]) {
|
|
14841
|
+
try {
|
|
14842
|
+
const pid = Number.parseInt(readFileSync(`${STATE_DIR}/${name}.pid`, "utf8").trim(), 10);
|
|
14843
|
+
if (!Number.isFinite(pid)) return false;
|
|
14844
|
+
process.kill(pid, 0);
|
|
14845
|
+
pids[name] = pid;
|
|
14846
|
+
} catch {
|
|
14847
|
+
return false;
|
|
14848
|
+
}
|
|
14849
|
+
}
|
|
14850
|
+
if (spawnSync("xdpyinfo", [], { env: withDisplay(), stdio: "ignore" }).status !== 0) return false;
|
|
14851
|
+
const wm = spawnSync("xprop", ["-root", "_NET_SUPPORTING_WM_CHECK"], { env: withDisplay(), stdio: "pipe" });
|
|
14852
|
+
if (wm.status !== 0 || !wm.stdout?.toString().includes("window id")) return false;
|
|
14853
|
+
const bridge = getDesktopBridgeStatus();
|
|
14854
|
+
return !!bridge?.x11vnc.listenerPids?.includes(pids.x11vnc) && !!bridge.websockify.listenerPids?.includes(pids.novnc);
|
|
14855
|
+
}
|
|
14826
14856
|
function ensureServicesRunning() {
|
|
14827
14857
|
if (!existsSync(SERVICES_SCRIPT)) {
|
|
14828
14858
|
fail(
|
|
14829
14859
|
`Desktop services script missing at ${SERVICES_SCRIPT}. The workspace image is out of date \u2014 Xvfb / openbox / x11vnc / websockify must be installed and \`replicas-start-desktop-services\` baked in.`
|
|
14830
14860
|
);
|
|
14831
14861
|
}
|
|
14862
|
+
if (Date.now() - lastDesktopHealthAt < 1e3) return;
|
|
14863
|
+
if (desktopStackHealthy()) {
|
|
14864
|
+
lastDesktopHealthAt = Date.now();
|
|
14865
|
+
return;
|
|
14866
|
+
}
|
|
14832
14867
|
const r = spawnSync("bash", [SERVICES_SCRIPT], { stdio: "pipe" });
|
|
14833
14868
|
if (r.status !== 0) {
|
|
14834
14869
|
fail(`Failed to start desktop services: ${r.stderr?.toString() || "unknown error"}`);
|
|
14835
14870
|
}
|
|
14871
|
+
lastDesktopHealthAt = Date.now();
|
|
14836
14872
|
}
|
|
14837
14873
|
function withDisplay(env = process.env) {
|
|
14838
14874
|
return { ...env, DISPLAY: DEFAULT_DISPLAY };
|
|
@@ -14845,6 +14881,17 @@ function runDisplayCmd(bin, args) {
|
|
|
14845
14881
|
}
|
|
14846
14882
|
return r.stdout?.toString() ?? "";
|
|
14847
14883
|
}
|
|
14884
|
+
function runDesktopInputCmd(args) {
|
|
14885
|
+
mkdirSync(dirname(INPUT_LOCK_FILE), { recursive: true });
|
|
14886
|
+
return runDisplayCmd("flock", [
|
|
14887
|
+
"--exclusive",
|
|
14888
|
+
"--wait",
|
|
14889
|
+
process.env.REPLICAS_DESKTOP_INPUT_LOCK_WAIT_SECONDS || "60",
|
|
14890
|
+
INPUT_LOCK_FILE,
|
|
14891
|
+
"xdotool",
|
|
14892
|
+
...args
|
|
14893
|
+
]);
|
|
14894
|
+
}
|
|
14848
14895
|
function tryDisplayCmd(bin, args) {
|
|
14849
14896
|
ensureServicesRunning();
|
|
14850
14897
|
const r = spawnSync(bin, args, { env: withDisplay(), stdio: "pipe" });
|
|
@@ -14910,8 +14957,8 @@ var sleep = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
|
14910
14957
|
|
|
14911
14958
|
// src/commands/computer/recording.ts
|
|
14912
14959
|
import { spawn as spawn3 } from "child_process";
|
|
14913
|
-
import { appendFileSync, existsSync as existsSync2, mkdirSync, readFileSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
14914
|
-
import { dirname } from "path";
|
|
14960
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
14961
|
+
import { dirname as dirname2 } from "path";
|
|
14915
14962
|
|
|
14916
14963
|
// src/commands/computer/recording/render.ts
|
|
14917
14964
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
@@ -15393,7 +15440,7 @@ var RECORD_DIMENSIONS_FILE = `${STATE_DIR}/recording-dimensions.json`;
|
|
|
15393
15440
|
var RECORD_ACTIONS_FILE = `${STATE_DIR}/recording-actions.jsonl`;
|
|
15394
15441
|
function recordingStartedAt() {
|
|
15395
15442
|
if (!existsSync2(RECORD_STARTED_AT_FILE)) return null;
|
|
15396
|
-
const startedAt = Number.parseInt(
|
|
15443
|
+
const startedAt = Number.parseInt(readFileSync2(RECORD_STARTED_AT_FILE, "utf8").trim(), 10);
|
|
15397
15444
|
return Number.isFinite(startedAt) ? startedAt : null;
|
|
15398
15445
|
}
|
|
15399
15446
|
function logRecordingAction(action) {
|
|
@@ -15406,7 +15453,7 @@ function logRecordingAction(action) {
|
|
|
15406
15453
|
function readRecordingDimensions() {
|
|
15407
15454
|
if (!existsSync2(RECORD_DIMENSIONS_FILE)) return configuredDesktopDimensions();
|
|
15408
15455
|
try {
|
|
15409
|
-
const dimensions = JSON.parse(
|
|
15456
|
+
const dimensions = JSON.parse(readFileSync2(RECORD_DIMENSIONS_FILE, "utf8"));
|
|
15410
15457
|
const width = dimensions?.width;
|
|
15411
15458
|
const height = dimensions?.height;
|
|
15412
15459
|
if (typeof width === "number" && Number.isFinite(width) && width > 0 && typeof height === "number" && Number.isFinite(height) && height > 0) {
|
|
@@ -15424,7 +15471,7 @@ function isOptionalNumber(value) {
|
|
|
15424
15471
|
}
|
|
15425
15472
|
function readRecordingActions() {
|
|
15426
15473
|
if (!existsSync2(RECORD_ACTIONS_FILE)) return [];
|
|
15427
|
-
return
|
|
15474
|
+
return readFileSync2(RECORD_ACTIONS_FILE, "utf8").split("\n").filter(Boolean).flatMap((line) => {
|
|
15428
15475
|
try {
|
|
15429
15476
|
const value = JSON.parse(line);
|
|
15430
15477
|
if (typeof value !== "object" || value === null) return [];
|
|
@@ -15446,7 +15493,7 @@ function readRecordingActions() {
|
|
|
15446
15493
|
async function computerRecordStartCommand(path6, options) {
|
|
15447
15494
|
ensureServicesRunning();
|
|
15448
15495
|
if (existsSync2(RECORD_PID_FILE)) {
|
|
15449
|
-
const pid = parseInt(
|
|
15496
|
+
const pid = parseInt(readFileSync2(RECORD_PID_FILE, "utf8").trim(), 10);
|
|
15450
15497
|
if (Number.isFinite(pid)) {
|
|
15451
15498
|
let alive = false;
|
|
15452
15499
|
try {
|
|
@@ -15458,10 +15505,10 @@ async function computerRecordStartCommand(path6, options) {
|
|
|
15458
15505
|
}
|
|
15459
15506
|
}
|
|
15460
15507
|
const target = resolvePath(path6);
|
|
15461
|
-
|
|
15508
|
+
mkdirSync2(dirname2(target), { recursive: true });
|
|
15462
15509
|
const fps = options.fps ? parseCoord(options.fps, "--fps") : 60;
|
|
15463
15510
|
const { width, height } = configuredDesktopDimensions();
|
|
15464
|
-
|
|
15511
|
+
mkdirSync2(STATE_DIR, { recursive: true });
|
|
15465
15512
|
const rawTarget = `${target}.raw-${Date.now()}.mp4`;
|
|
15466
15513
|
rmSync2(RECORD_ACTIONS_FILE, { force: true });
|
|
15467
15514
|
const child = spawn3("ffmpeg", [
|
|
@@ -15506,7 +15553,7 @@ async function computerRecordStartCommand(path6, options) {
|
|
|
15506
15553
|
async function computerRecordStopCommand() {
|
|
15507
15554
|
if (!existsSync2(RECORD_PID_FILE) && !existsSync2(RECORD_PATH_FILE)) fail("no recording in progress");
|
|
15508
15555
|
if (existsSync2(RECORD_PID_FILE)) {
|
|
15509
|
-
const pid = parseInt(
|
|
15556
|
+
const pid = parseInt(readFileSync2(RECORD_PID_FILE, "utf8").trim(), 10);
|
|
15510
15557
|
if (!Number.isFinite(pid)) fail("invalid recording pidfile");
|
|
15511
15558
|
try {
|
|
15512
15559
|
process.kill(pid, "SIGINT");
|
|
@@ -15523,9 +15570,9 @@ async function computerRecordStopCommand() {
|
|
|
15523
15570
|
rmSync2(RECORD_PID_FILE, { force: true });
|
|
15524
15571
|
}
|
|
15525
15572
|
if (existsSync2(RECORD_PATH_FILE)) {
|
|
15526
|
-
const target =
|
|
15527
|
-
const rawPath = existsSync2(RECORD_RAW_PATH_FILE) ?
|
|
15528
|
-
const fps = existsSync2(RECORD_FPS_FILE) ? parseInt(
|
|
15573
|
+
const target = readFileSync2(RECORD_PATH_FILE, "utf8").trim();
|
|
15574
|
+
const rawPath = existsSync2(RECORD_RAW_PATH_FILE) ? readFileSync2(RECORD_RAW_PATH_FILE, "utf8").trim() : target;
|
|
15575
|
+
const fps = existsSync2(RECORD_FPS_FILE) ? parseInt(readFileSync2(RECORD_FPS_FILE, "utf8").trim(), 10) : 60;
|
|
15529
15576
|
const size = readRecordingDimensions();
|
|
15530
15577
|
const actions = readRecordingActions();
|
|
15531
15578
|
if (rawPath !== target) {
|
|
@@ -15543,15 +15590,6 @@ async function computerRecordStopCommand() {
|
|
|
15543
15590
|
}
|
|
15544
15591
|
|
|
15545
15592
|
// src/commands/computer/index.ts
|
|
15546
|
-
function desktopBridgeStatus() {
|
|
15547
|
-
const r = spawnSync3("bash", [SERVICES_SCRIPT, "--status-json"], { stdio: "pipe" });
|
|
15548
|
-
if (r.status !== 0) return null;
|
|
15549
|
-
try {
|
|
15550
|
-
return JSON.parse(r.stdout.toString());
|
|
15551
|
-
} catch {
|
|
15552
|
-
return null;
|
|
15553
|
-
}
|
|
15554
|
-
}
|
|
15555
15593
|
function bridgeStatus(details, includeBacklog = false, rootsOnly = false) {
|
|
15556
15594
|
const pids = rootsOnly ? details.rootPids ?? [] : details.listenerPids ?? [];
|
|
15557
15595
|
const listenerCount = rootsOnly ? details.rootListeners ?? pids.length : details.listeners ?? pids.length;
|
|
@@ -15591,7 +15629,7 @@ async function computerInfoCommand() {
|
|
|
15591
15629
|
}
|
|
15592
15630
|
async function computerStatusCommand() {
|
|
15593
15631
|
ensureServicesRunning();
|
|
15594
|
-
const bridge =
|
|
15632
|
+
const bridge = getDesktopBridgeStatus();
|
|
15595
15633
|
const procs = ["Xvfb", "openbox", "tint2", "x11vnc", "websockify"];
|
|
15596
15634
|
for (const p of procs) {
|
|
15597
15635
|
const r = spawnSync3("pgrep", ["-af", p], { stdio: "pipe" });
|
|
@@ -15631,7 +15669,7 @@ function loadBrandSvg(canvasW, canvasH) {
|
|
|
15631
15669
|
`Brand wallpaper SVG missing at ${path6}. The workspace image is out of date \u2014 \`desktop/brand-wallpaper.svg\` must be installed at $REPLICAS_DESKTOP_TEMPLATES.`
|
|
15632
15670
|
);
|
|
15633
15671
|
}
|
|
15634
|
-
return
|
|
15672
|
+
return readFileSync3(path6, "utf8").replace(/(<svg[^>]*\s)width="\d+"/, `$1width="${canvasW}"`).replace(/(<svg[^>]*\s)height="\d+"/, `$1height="${canvasH}"`);
|
|
15635
15673
|
}
|
|
15636
15674
|
var BRAND_PAD_FRACTION = 0.06;
|
|
15637
15675
|
var SCREENSHOT_CORNER_FRACTION = 0.022;
|
|
@@ -15694,7 +15732,7 @@ function overlayGrid(rawPath, target, width, height, gridSize, gridPath) {
|
|
|
15694
15732
|
}
|
|
15695
15733
|
async function computerScreenshotCommand(path6, options = {}) {
|
|
15696
15734
|
const target = resolvePath(path6);
|
|
15697
|
-
|
|
15735
|
+
mkdirSync3(dirname3(target), { recursive: true });
|
|
15698
15736
|
const stamp = `${process.pid}-${Date.now()}`;
|
|
15699
15737
|
const rawPath = `/tmp/replicas-screenshot-${stamp}.raw.png`;
|
|
15700
15738
|
const svgPath = `/tmp/replicas-screenshot-${stamp}.brand.svg`;
|
|
@@ -15773,7 +15811,7 @@ async function computerScreenshotCommand(path6, options = {}) {
|
|
|
15773
15811
|
console.log(target);
|
|
15774
15812
|
}
|
|
15775
15813
|
function hashFile(path6) {
|
|
15776
|
-
return createHash2("sha256").update(
|
|
15814
|
+
return createHash2("sha256").update(readFileSync3(path6)).digest("hex");
|
|
15777
15815
|
}
|
|
15778
15816
|
async function captureStableRawScreenshot(target, options) {
|
|
15779
15817
|
const start = Date.now();
|
|
@@ -15824,7 +15862,7 @@ function recordingMousePosition() {
|
|
|
15824
15862
|
}
|
|
15825
15863
|
async function computerObserveCommand(path6, options = {}) {
|
|
15826
15864
|
const target = resolvePath(path6);
|
|
15827
|
-
|
|
15865
|
+
mkdirSync3(dirname3(target), { recursive: true });
|
|
15828
15866
|
const timeoutMs = options.timeout ? parseCoord(options.timeout, "--timeout") : 3e3;
|
|
15829
15867
|
const stableMs = options.stableMs ? parseCoord(options.stableMs, "--stable-ms") : 600;
|
|
15830
15868
|
const pollMs = options.pollMs ? parseCoord(options.pollMs, "--poll-ms") : 200;
|
|
@@ -15835,6 +15873,7 @@ async function computerObserveCommand(path6, options = {}) {
|
|
|
15835
15873
|
const rawPath = `/tmp/replicas-observe-${stamp}.raw.png`;
|
|
15836
15874
|
const gridPath = `/tmp/replicas-observe-${stamp}.grid.svg`;
|
|
15837
15875
|
try {
|
|
15876
|
+
ensureServicesRunning();
|
|
15838
15877
|
const capture = await captureStableRawScreenshot(rawPath, { timeoutMs, stableMs, pollMs });
|
|
15839
15878
|
const gridSize = options.raw ? null : parseGridSize(options.grid ?? true);
|
|
15840
15879
|
if (gridSize === null) {
|
|
@@ -16340,7 +16379,7 @@ async function computerClickCommand(xStr, yStr, options) {
|
|
|
16340
16379
|
args.push("keyup", mod);
|
|
16341
16380
|
}
|
|
16342
16381
|
}
|
|
16343
|
-
|
|
16382
|
+
runDesktopInputCmd(args);
|
|
16344
16383
|
logRecordingAction({ type: "click", x, y });
|
|
16345
16384
|
console.log(`clicked ${button === "1" ? "left" : button === "2" ? "middle" : button === "3" ? "right" : `button ${button}`} at (${x},${y})${options.double ? " x2" : ""}`);
|
|
16346
16385
|
}
|
|
@@ -16348,19 +16387,19 @@ async function computerMoveCommand(xStr, yStr) {
|
|
|
16348
16387
|
const dimensions = getDisplayDimensions();
|
|
16349
16388
|
const x = parseScreenCoord(xStr, "x", dimensions.width);
|
|
16350
16389
|
const y = parseScreenCoord(yStr, "y", dimensions.height);
|
|
16351
|
-
|
|
16390
|
+
runDesktopInputCmd(["mousemove", "--sync", String(x), String(y)]);
|
|
16352
16391
|
logRecordingAction({ type: "move", x, y });
|
|
16353
16392
|
console.log(`moved to (${x},${y})`);
|
|
16354
16393
|
}
|
|
16355
16394
|
async function computerTypeCommand(text, options) {
|
|
16356
16395
|
const delay = options.delay ? parseCoord(options.delay, "--delay") : 12;
|
|
16357
|
-
|
|
16396
|
+
runDesktopInputCmd(["type", "--delay", String(delay), "--", text]);
|
|
16358
16397
|
const position = recordingMousePosition();
|
|
16359
16398
|
logRecordingAction({ type: "type", ...position ?? {} });
|
|
16360
16399
|
console.log(`typed ${text.length} char${text.length === 1 ? "" : "s"}`);
|
|
16361
16400
|
}
|
|
16362
16401
|
async function computerKeyCommand(combo) {
|
|
16363
|
-
|
|
16402
|
+
runDesktopInputCmd(["key", "--", combo]);
|
|
16364
16403
|
const position = recordingMousePosition();
|
|
16365
16404
|
logRecordingAction({ type: "key", ...position ?? {} });
|
|
16366
16405
|
console.log(`pressed ${combo}`);
|
|
@@ -16387,7 +16426,7 @@ async function computerScrollCommand(direction, options) {
|
|
|
16387
16426
|
);
|
|
16388
16427
|
}
|
|
16389
16428
|
args.push("click", "--repeat", String(amount), "--delay", "30", button);
|
|
16390
|
-
|
|
16429
|
+
runDesktopInputCmd(args);
|
|
16391
16430
|
const position = hoverPosition ?? recordingMousePosition();
|
|
16392
16431
|
logRecordingAction({ type: "scroll", ...position ?? {} });
|
|
16393
16432
|
console.log(`scrolled ${dir} x${amount}`);
|
|
@@ -16414,7 +16453,7 @@ async function computerDragCommand(fx, fy, tx, ty) {
|
|
|
16414
16453
|
args.push("mousemove", "--sync", String(x), String(y), "sleep", "0.018");
|
|
16415
16454
|
}
|
|
16416
16455
|
args.push("mouseup", "1");
|
|
16417
|
-
|
|
16456
|
+
runDesktopInputCmd(args);
|
|
16418
16457
|
logRecordingAction({ type: "drag", x: fromX, y: fromY, toX, toY });
|
|
16419
16458
|
console.log(`dragged (${fromX},${fromY}) -> (${toX},${toY})`);
|
|
16420
16459
|
}
|