tesseract-agent 0.11.22 → 0.11.23
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-bundle/cli.js +2001 -1026
- package/dist-bundle/mesh-core/package.json +1 -1
- package/package.json +1 -1
package/dist-bundle/cli.js
CHANGED
|
@@ -20016,7 +20016,7 @@ var init_screen = __esm({
|
|
|
20016
20016
|
vars: [],
|
|
20017
20017
|
sensitive: true,
|
|
20018
20018
|
content: `
|
|
20019
|
-
YOU CAN SEE AND CONTROL THIS
|
|
20019
|
+
YOU CAN SEE AND CONTROL THIS COMPUTER. You control the developer's ACTUAL screen directly through the \`tesseract-computer\` tools, and you ALSO have your full Claude Code toolset (bash, files, editors, web, etc.). Use whichever fits: drive the screen for on-screen tasks, use your coding/file/shell tools for the rest, and combine them freely.
|
|
20020
20020
|
|
|
20021
20021
|
THE LOOP IS SEE \u2192 ACT \u2192 VERIFY:
|
|
20022
20022
|
- Call \`see_screen\` to LOOK \u2014 it returns the current screen as an image. YOU read the image and decide where to act. Call it before acting on anything new, and AGAIN after acting to confirm what happened.
|
|
@@ -20028,7 +20028,7 @@ VERIFY & SELF-CORRECT AS YOU GO: after EVERY action, \`see_screen\` and check it
|
|
|
20028
20028
|
|
|
20029
20029
|
READING TEXT ALOUD: when the developer asks you to READ something on the screen (an article, a message, an error, code, a field's value) \u2014 do NOT transcribe it from the picture, you will misread it. Call \`read_text\` with a short \`what\`, and when you can see the block, the region rectangle (x1,y1,x2,y2) around it \u2014 it pulls the EXACT text via accessibility. Then read that back. When it's spoken, keep it to the relevant part; if it's long, give the gist or ask how much of it to read.
|
|
20030
20030
|
|
|
20031
|
-
MORE HELPERS (prefer these over guessing or blind waits): \`read_ui\` lists the on-screen elements with their exact click coordinates \u2014 use it to act precisely on a busy/unfamiliar window. \`wait_for\` waits until something appears or disappears (page loaded, spinner gone) instead of a blind pause. \`get_clipboard\` / \`get_selection\` read the clipboard or whatever the developer has highlighted (use \`get_selection\` when they say "this"). \`open_url\` / \`open_path\` open a link or a file/folder directly (more reliable than navigating by hand). \`list_windows\` shows what's open and \`focus_window\` brings a specific one to the front. \`notify\` posts a
|
|
20031
|
+
MORE HELPERS (prefer these over guessing or blind waits): \`read_ui\` lists the on-screen elements with their exact click coordinates \u2014 use it to act precisely on a busy/unfamiliar window. \`wait_for\` waits until something appears or disappears (page loaded, spinner gone) instead of a blind pause. \`get_clipboard\` / \`get_selection\` read the clipboard or whatever the developer has highlighted (use \`get_selection\` when they say "this"). \`open_url\` / \`open_path\` open a link or a file/folder directly (more reliable than navigating by hand). \`list_windows\` shows what's open and \`focus_window\` brings a specific one to the front. \`notify\` posts a system notification.
|
|
20032
20032
|
|
|
20033
20033
|
BE TRUTHFUL: never say something worked, opened, or is showing unless you JUST confirmed it with a fresh \`see_screen\`. A tool returning "done" only means the keystroke/click was sent \u2014 NOT that it had the effect you wanted.
|
|
20034
20034
|
|
|
@@ -20046,7 +20046,7 @@ WHEN YOUR REPLIES ARE READ ALOUD (voice): keep them SHORT and conversational \u2
|
|
|
20046
20046
|
vars: [],
|
|
20047
20047
|
content: `
|
|
20048
20048
|
|
|
20049
|
-
YOU ARE THE VOICE LAYER. You talk with the developer, understand what they want, and hand the actual DOING to a powerful agent (Claude Code) that fully controls this
|
|
20049
|
+
YOU ARE THE VOICE LAYER. You talk with the developer, understand what they want, and hand the actual DOING to a powerful agent (Claude Code) that fully controls this computer \u2014 it takes high-res screenshots, uses the mouse/keyboard, the accessibility tree, processes, files, and every other tool. You do NOT operate the screen yourself.
|
|
20050
20050
|
|
|
20051
20051
|
You also see a LOW-RESOLUTION ~1 fps view of the screen, so you can follow along, describe what's happening, and confirm results in your own words \u2014 but it's just for awareness; never try to act on it.
|
|
20052
20052
|
|
|
@@ -27125,6 +27125,11 @@ function engineSearchDirs(opts) {
|
|
|
27125
27125
|
const programFiles = env.ProgramFiles ?? "C:\\Program Files";
|
|
27126
27126
|
const choco = env.ChocolateyInstall ?? "C:\\ProgramData\\chocolatey";
|
|
27127
27127
|
return dedupe([
|
|
27128
|
+
// The native installer's launcher dir (Claude Code's `install.ps1` writes %USERPROFILE%\.local\bin
|
|
27129
|
+
// there, mirroring POSIX ~/.local/bin). Omitting it read a working native install as "not
|
|
27130
|
+
// installed" in the picker/Settings while chat worked — and left it off the probe PATH preamble,
|
|
27131
|
+
// since windowsPathPreamble derives its dirs from here.
|
|
27132
|
+
j(home, ".local", "bin"),
|
|
27128
27133
|
j(appData, "npm"),
|
|
27129
27134
|
j(localAppData, "pnpm"),
|
|
27130
27135
|
j(localAppData, "Volta", "bin"),
|
|
@@ -28603,7 +28608,24 @@ function parseNSFilenames(raw) {
|
|
|
28603
28608
|
function parseUriList(raw) {
|
|
28604
28609
|
return raw.split(/[\r\n]+/).map((l) => l.trim()).filter((l) => l.startsWith("file://")).map(decodeFileUrl);
|
|
28605
28610
|
}
|
|
28606
|
-
function
|
|
28611
|
+
function parseCfHdrop(bytes3) {
|
|
28612
|
+
if (bytes3.length < 20)
|
|
28613
|
+
return [];
|
|
28614
|
+
const dv = new DataView(bytes3.buffer, bytes3.byteOffset, bytes3.byteLength);
|
|
28615
|
+
const pFiles = dv.getUint32(0, true);
|
|
28616
|
+
if (pFiles >= bytes3.length)
|
|
28617
|
+
return [];
|
|
28618
|
+
const fWide = dv.getUint32(16, true) !== 0;
|
|
28619
|
+
const body = new TextDecoder(fWide ? "utf-16le" : "latin1").decode(bytes3.subarray(pFiles));
|
|
28620
|
+
const out = [];
|
|
28621
|
+
for (const seg of body.split("\0")) {
|
|
28622
|
+
if (!seg)
|
|
28623
|
+
break;
|
|
28624
|
+
out.push(seg);
|
|
28625
|
+
}
|
|
28626
|
+
return out;
|
|
28627
|
+
}
|
|
28628
|
+
function readFilePathsWith(read, readBuffer) {
|
|
28607
28629
|
const tryRead = (format) => {
|
|
28608
28630
|
try {
|
|
28609
28631
|
return read(format) || "";
|
|
@@ -28626,6 +28648,17 @@ function readFilePathsWith(read) {
|
|
|
28626
28648
|
if (p.length)
|
|
28627
28649
|
return p;
|
|
28628
28650
|
}
|
|
28651
|
+
if (readBuffer) {
|
|
28652
|
+
try {
|
|
28653
|
+
const buf = readBuffer("CF_HDROP");
|
|
28654
|
+
if (buf && buf.length) {
|
|
28655
|
+
const p = parseCfHdrop(buf);
|
|
28656
|
+
if (p.length)
|
|
28657
|
+
return p;
|
|
28658
|
+
}
|
|
28659
|
+
} catch {
|
|
28660
|
+
}
|
|
28661
|
+
}
|
|
28629
28662
|
return [];
|
|
28630
28663
|
}
|
|
28631
28664
|
var init_clipboard_files = __esm({
|
|
@@ -38653,11 +38686,11 @@ function inflightParamsOf(p) {
|
|
|
38653
38686
|
clientRequestId: pick3("clientRequestId")
|
|
38654
38687
|
};
|
|
38655
38688
|
}
|
|
38656
|
-
function recordInflightTurn(sessionId, params,
|
|
38689
|
+
function recordInflightTurn(sessionId, params, attempts2 = 0) {
|
|
38657
38690
|
try {
|
|
38658
38691
|
const dir2 = inflightDir();
|
|
38659
38692
|
mkdirSync8(dir2, { recursive: true });
|
|
38660
|
-
const entry = { sessionId, startedAt: (/* @__PURE__ */ new Date()).toISOString(), attempts, params: inflightParamsOf({ ...params, sessionId }) };
|
|
38693
|
+
const entry = { sessionId, startedAt: (/* @__PURE__ */ new Date()).toISOString(), attempts: attempts2, params: inflightParamsOf({ ...params, sessionId }) };
|
|
38661
38694
|
const tmp = `${fileFor(sessionId)}.tmp`;
|
|
38662
38695
|
writeFileSync6(tmp, JSON.stringify(entry), { mode: 384 });
|
|
38663
38696
|
renameSync4(tmp, fileFor(sessionId));
|
|
@@ -39798,8 +39831,8 @@ async function anthropicMessage(args) {
|
|
|
39798
39831
|
}
|
|
39799
39832
|
async function anthropicCall(args) {
|
|
39800
39833
|
const { env, model, system, content, maxTokens, timeoutMs, label } = args;
|
|
39801
|
-
const
|
|
39802
|
-
const budgetMs = args.budgetMs ??
|
|
39834
|
+
const attempts2 = Math.max(1, args.attempts ?? 1);
|
|
39835
|
+
const budgetMs = args.budgetMs ?? attempts2 * timeoutMs + 4e3;
|
|
39803
39836
|
const deadline = Date.now() + budgetMs;
|
|
39804
39837
|
const base = { ...model ? { model } : {}, max_tokens: maxTokens, system, messages: [{ role: "user", content }] };
|
|
39805
39838
|
const useSchema = !!args.schema && !schemaUnsupported.has(schemaKey(anthropicBaseUrl(env), model));
|
|
@@ -39808,7 +39841,7 @@ async function anthropicCall(args) {
|
|
|
39808
39841
|
const headers = await anthropicHeaders(env);
|
|
39809
39842
|
if (!headers)
|
|
39810
39843
|
return { text: null, rateLimited: false };
|
|
39811
|
-
for (let attempt = 0; attempt <
|
|
39844
|
+
for (let attempt = 0; attempt < attempts2; attempt++) {
|
|
39812
39845
|
const remaining = deadline - Date.now();
|
|
39813
39846
|
if (remaining < 1e3) {
|
|
39814
39847
|
log(`${label}: out of budget after ${attempt} attempt(s) \u2014 giving up rather than starting a leg that cannot finish.`);
|
|
@@ -39845,7 +39878,7 @@ async function anthropicCall(args) {
|
|
|
39845
39878
|
}
|
|
39846
39879
|
const rateLimited = res.status === 429 || res.status === 529;
|
|
39847
39880
|
const modelRejected = (res.status === 400 || res.status === 403 || res.status === 404) && !!model;
|
|
39848
|
-
if (rateLimited && attempt <
|
|
39881
|
+
if (rateLimited && attempt < attempts2 - 1) {
|
|
39849
39882
|
let stated = 0;
|
|
39850
39883
|
try {
|
|
39851
39884
|
stated = Number(res.headers?.get?.("retry-after")) * 1e3;
|
|
@@ -39861,7 +39894,7 @@ async function anthropicCall(args) {
|
|
|
39861
39894
|
log(`${label}: HTTP ${res.status} (${model}) ${errBody.slice(0, 200)}`);
|
|
39862
39895
|
return { text: null, rateLimited, modelRejected };
|
|
39863
39896
|
} catch (err2) {
|
|
39864
|
-
if (args.retryOnThrow && attempt <
|
|
39897
|
+
if (args.retryOnThrow && attempt < attempts2 - 1) {
|
|
39865
39898
|
const wait = Math.max(0, Math.min(800 * (attempt + 1) * (attempt + 1), deadline - Date.now()));
|
|
39866
39899
|
log(`${label}: call failed (${err2 instanceof Error ? err2.message : String(err2)}) \u2014 retry ${attempt + 1} in ${wait}ms`);
|
|
39867
39900
|
await new Promise((r) => setTimeout(r, wait));
|
|
@@ -39995,7 +40028,7 @@ function killProcessTree(child, signal2 = "SIGKILL") {
|
|
|
39995
40028
|
const pid = child.pid;
|
|
39996
40029
|
if (process.platform === "win32") {
|
|
39997
40030
|
try {
|
|
39998
|
-
spawn("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore" });
|
|
40031
|
+
spawn("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
39999
40032
|
} catch {
|
|
40000
40033
|
}
|
|
40001
40034
|
return;
|
|
@@ -40480,7 +40513,7 @@ function findUserInstalledClaude(binName) {
|
|
|
40480
40513
|
if (existsSync6(p))
|
|
40481
40514
|
return p;
|
|
40482
40515
|
try {
|
|
40483
|
-
const out = execFileSync(process.platform === "win32" ? "where" : "which", [binName], { encoding: "utf8" }).split("\n")[0]?.trim();
|
|
40516
|
+
const out = execFileSync(process.platform === "win32" ? "where" : "which", [binName], { encoding: "utf8", windowsHide: true }).split("\n")[0]?.trim();
|
|
40484
40517
|
if (out && existsSync6(out))
|
|
40485
40518
|
return out;
|
|
40486
40519
|
} catch {
|
|
@@ -40497,17 +40530,21 @@ function resolvedClaudeCodePath() {
|
|
|
40497
40530
|
function resolveClaudeCodeExecutable() {
|
|
40498
40531
|
if (cachedClaudeExe)
|
|
40499
40532
|
return cachedClaudeExe;
|
|
40500
|
-
const
|
|
40501
|
-
const
|
|
40502
|
-
|
|
40503
|
-
|
|
40533
|
+
const binNames = process.platform === "win32" ? ["claude.exe", "claude.cmd"] : ["claude"];
|
|
40534
|
+
for (const name of binNames) {
|
|
40535
|
+
const user = findUserInstalledClaude(name);
|
|
40536
|
+
if (user)
|
|
40537
|
+
return cachedClaudeExe = user;
|
|
40538
|
+
}
|
|
40504
40539
|
const resourcesPath = process.resourcesPath;
|
|
40505
40540
|
if (resourcesPath) {
|
|
40506
40541
|
const platKey = process.platform === "darwin" ? "mac" : process.platform === "win32" ? "win" : "linux";
|
|
40507
40542
|
for (const dir2 of [`${platKey}-${process.arch}`, platKey]) {
|
|
40508
|
-
|
|
40509
|
-
|
|
40510
|
-
|
|
40543
|
+
for (const name of binNames) {
|
|
40544
|
+
const bundled = join19(resourcesPath, "claude-cli", dir2, name);
|
|
40545
|
+
if (existsSync6(bundled))
|
|
40546
|
+
return cachedClaudeExe = bundled;
|
|
40547
|
+
}
|
|
40511
40548
|
}
|
|
40512
40549
|
}
|
|
40513
40550
|
throw new Error("Claude Code isn\u2019t installed. Tesseract runs the agent framework you have installed rather than bundling one \u2014 install it with `curl -fsSL https://claude.ai/install.sh | bash` (Windows: `irm https://claude.ai/install.ps1 | iex`), or pick a different engine in Settings.");
|
|
@@ -40638,7 +40675,14 @@ function cliEffortLevels() {
|
|
|
40638
40675
|
return cachedEfforts;
|
|
40639
40676
|
cachedEfforts = null;
|
|
40640
40677
|
try {
|
|
40641
|
-
const
|
|
40678
|
+
const claudeExe = resolveClaudeCodeExecutable();
|
|
40679
|
+
const help = execFileSync(claudeExe, ["--help"], {
|
|
40680
|
+
encoding: "utf8",
|
|
40681
|
+
timeout: 5e3,
|
|
40682
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
40683
|
+
windowsHide: true,
|
|
40684
|
+
...process.platform === "win32" && /\.(cmd|bat)$/i.test(claudeExe) ? { shell: true } : {}
|
|
40685
|
+
});
|
|
40642
40686
|
const line = help.split("\n").find((l) => l.includes("--effort"));
|
|
40643
40687
|
const listed = line ? /\(([^)]*)\)/.exec(line) : null;
|
|
40644
40688
|
const levels = listed?.[1].split(",").map((x) => x.trim()).filter(Boolean) ?? [];
|
|
@@ -40849,7 +40893,7 @@ function resolveNodeExecutable() {
|
|
|
40849
40893
|
return cachedNodeExe = p;
|
|
40850
40894
|
}
|
|
40851
40895
|
try {
|
|
40852
|
-
const found = execSync(process.platform === "win32" ? "where node" : "command -v node", { encoding: "utf8", timeout: 3e3 }).split(/\r?\n/)[0].trim();
|
|
40896
|
+
const found = execSync(process.platform === "win32" ? "where node" : "command -v node", { encoding: "utf8", timeout: 3e3, windowsHide: true }).split(/\r?\n/)[0].trim();
|
|
40853
40897
|
if (found && existsSync6(found))
|
|
40854
40898
|
return cachedNodeExe = found;
|
|
40855
40899
|
} catch {
|
|
@@ -45829,11 +45873,14 @@ ${prompt("agent.system.long-running")}`;
|
|
|
45829
45873
|
};
|
|
45830
45874
|
delete spawnEnv.CLAUDE_CODE_DISABLE_BACKGROUND_TASKS;
|
|
45831
45875
|
live.endpointEnv = anthropicEnvOf(spawnEnv);
|
|
45876
|
+
const winBatch = process.platform === "win32" && /\.(cmd|bat)$/i.test(options.command);
|
|
45832
45877
|
const child = spawn2(options.command, options.args, {
|
|
45833
45878
|
cwd: options.cwd,
|
|
45834
45879
|
env: spawnEnv,
|
|
45835
45880
|
signal: options.signal,
|
|
45836
45881
|
stdio: ["pipe", "pipe", "pipe"],
|
|
45882
|
+
windowsHide: true,
|
|
45883
|
+
...winBatch ? { shell: true } : {},
|
|
45837
45884
|
// Own process group (POSIX) so Stop can SIGKILL the WHOLE tree — the CLI's
|
|
45838
45885
|
// Bash tool spawns grandchildren (builds, dev servers, sleeps) that a
|
|
45839
45886
|
// single-PID kill would orphan. Pipes keep it attached; we never unref, so
|
|
@@ -49027,7 +49074,7 @@ function nodeExe() {
|
|
|
49027
49074
|
}
|
|
49028
49075
|
if (!cachedNodeExe2) {
|
|
49029
49076
|
try {
|
|
49030
|
-
const found = execSync2(process.platform === "win32" ? "where node" : "command -v node", { encoding: "utf8" }).split(/\r?\n/)[0].trim();
|
|
49077
|
+
const found = execSync2(process.platform === "win32" ? "where node" : "command -v node", { encoding: "utf8", windowsHide: true }).split(/\r?\n/)[0].trim();
|
|
49031
49078
|
if (found && existsSync9(found))
|
|
49032
49079
|
cachedNodeExe2 = found;
|
|
49033
49080
|
} catch {
|
|
@@ -49110,7 +49157,7 @@ function writeConfig(cfg2) {
|
|
|
49110
49157
|
function ccr(args, timeout = 8e3) {
|
|
49111
49158
|
const { exe, asNode } = nodeExe();
|
|
49112
49159
|
return new Promise((resolve16) => {
|
|
49113
|
-
execFile4(exe, [ccrCliPath(), ...args], { env: ccrEnv(asNode), timeout }, (err2, stdout, stderr) => {
|
|
49160
|
+
execFile4(exe, [ccrCliPath(), ...args], { env: ccrEnv(asNode), timeout, windowsHide: true }, (err2, stdout, stderr) => {
|
|
49114
49161
|
resolve16({ ok: !err2, out: `${stdout || ""}${stderr || ""}` });
|
|
49115
49162
|
});
|
|
49116
49163
|
});
|
|
@@ -49822,7 +49869,7 @@ function rememberedPath(engineId) {
|
|
|
49822
49869
|
function which(bin, path6) {
|
|
49823
49870
|
const tool2 = process.platform === "win32" ? "where" : "which";
|
|
49824
49871
|
try {
|
|
49825
|
-
const out = execFileSync2(tool2, [bin], { env: { ...process.env, PATH: path6 }, timeout: 4e3, encoding: "utf-8" });
|
|
49872
|
+
const out = execFileSync2(tool2, [bin], { env: { ...process.env, PATH: path6 }, timeout: 4e3, encoding: "utf-8", windowsHide: true });
|
|
49826
49873
|
return out.split(/\r?\n/).map((l) => l.trim()).find(Boolean) || void 0;
|
|
49827
49874
|
} catch {
|
|
49828
49875
|
return void 0;
|
|
@@ -85795,7 +85842,7 @@ var init_mesh_ca = __esm({
|
|
|
85795
85842
|
init_base_paths();
|
|
85796
85843
|
import { existsSync as existsSync61, mkdtempSync as mkdtempSync4, readFileSync as readFileSync70, rmSync as rmSync14 } from "node:fs";
|
|
85797
85844
|
import { createHash as createHash12 } from "node:crypto";
|
|
85798
|
-
import { execFileSync as execFileSync9, spawn as
|
|
85845
|
+
import { execFileSync as execFileSync9, spawn as spawn16 } from "node:child_process";
|
|
85799
85846
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
85800
85847
|
import { tmpdir as tmpdir8 } from "node:os";
|
|
85801
85848
|
import { dirname as dirname47, join as join104, resolve as resolve15 } from "node:path";
|
|
@@ -96176,7 +96223,7 @@ function checkTrust(rootDir, trusted, relaxHardlink = false) {
|
|
|
96176
96223
|
} catch {
|
|
96177
96224
|
return "unreadable";
|
|
96178
96225
|
}
|
|
96179
|
-
if ((st.mode & 2) !== 0)
|
|
96226
|
+
if (!isWindows && (st.mode & 2) !== 0)
|
|
96180
96227
|
return "world-writable";
|
|
96181
96228
|
if (!isWindows && typeof process.getuid === "function") {
|
|
96182
96229
|
const uid = process.getuid();
|
|
@@ -97613,6 +97660,13 @@ function capture(command, args, opts = {}) {
|
|
|
97613
97660
|
}
|
|
97614
97661
|
});
|
|
97615
97662
|
}
|
|
97663
|
+
async function loadLibnutWindowing() {
|
|
97664
|
+
if (process.platform === "linux" && !process.env.DISPLAY)
|
|
97665
|
+
return null;
|
|
97666
|
+
const n = await loadLibnut();
|
|
97667
|
+
const w = n;
|
|
97668
|
+
return w && typeof w.getActiveWindow === "function" && typeof w.getWindowTitle === "function" && typeof w.focusWindow === "function" && typeof w.keyTap === "function" && typeof w.keyToggle === "function" ? w : null;
|
|
97669
|
+
}
|
|
97616
97670
|
var libnutPromise;
|
|
97617
97671
|
function loadLibnut() {
|
|
97618
97672
|
if (libnutPromise)
|
|
@@ -97784,6 +97838,10 @@ function createDaemonOsControlRunner() {
|
|
|
97784
97838
|
const p = n.getMousePos();
|
|
97785
97839
|
return { x: p.x, y: p.y };
|
|
97786
97840
|
},
|
|
97841
|
+
// KNOWN LIMITATION (Windows): libnut-win32's typeString drives the ASCII keyboard-event path and
|
|
97842
|
+
// CANNOT emit non-ASCII / Unicode characters (accents, CJK, emoji) — those come out wrong or empty.
|
|
97843
|
+
// Plain ASCII typing is fine. A dedicated Unicode helper (SendInput with KEYEVENTF_UNICODE) is a
|
|
97844
|
+
// separate rdesk task; until then typeString stays the text path. macOS/Linux type Unicode fine.
|
|
97787
97845
|
async typeText(t) {
|
|
97788
97846
|
const n = await nutOrThrow();
|
|
97789
97847
|
n.typeString(t);
|
|
@@ -97797,8 +97855,20 @@ function createDaemonOsControlRunner() {
|
|
|
97797
97855
|
n.keyTap(key2, modifiers);
|
|
97798
97856
|
log3(`key ${combo}`);
|
|
97799
97857
|
},
|
|
97800
|
-
// App launch (the one non-input capability the voice path needs —
|
|
97858
|
+
// App launch (the one non-input capability the voice path needs) — platform-correct launcher.
|
|
97801
97859
|
async appLaunch(app) {
|
|
97860
|
+
if (process.platform === "win32") {
|
|
97861
|
+
const r2 = await capture("cmd", ["/c", "start", "", app], { timeoutMs: 1e4 });
|
|
97862
|
+
if (r2.code !== 0)
|
|
97863
|
+
throw new Error(r2.stderr.trim() || `Could not launch ${app}`);
|
|
97864
|
+
return;
|
|
97865
|
+
}
|
|
97866
|
+
if (process.platform === "linux") {
|
|
97867
|
+
const r2 = await capture("xdg-open", [app], { timeoutMs: 1e4 });
|
|
97868
|
+
if (r2.code !== 0)
|
|
97869
|
+
throw new Error(r2.stderr.trim() || `Could not launch ${app}`);
|
|
97870
|
+
return;
|
|
97871
|
+
}
|
|
97802
97872
|
const r = await capture("open", [app.includes(".") ? "-b" : "-a", app], { timeoutMs: 1e4 });
|
|
97803
97873
|
if (r.code !== 0)
|
|
97804
97874
|
throw new Error(r.stderr.trim() || `Could not launch ${app}`);
|
|
@@ -102017,7 +102087,7 @@ function run2(bin, args, opts) {
|
|
|
102017
102087
|
let timedOut = false;
|
|
102018
102088
|
let child;
|
|
102019
102089
|
try {
|
|
102020
|
-
child = spawn7(bin, args, { cwd: opts?.cwd ?? mediaSpawnCwd(), stdio: ["ignore", "pipe", "pipe"] });
|
|
102090
|
+
child = spawn7(bin, args, { cwd: opts?.cwd ?? mediaSpawnCwd(), stdio: ["ignore", "pipe", "pipe"], windowsHide: true });
|
|
102021
102091
|
} catch (e) {
|
|
102022
102092
|
reject(e instanceof Error ? e : new Error(String(e)));
|
|
102023
102093
|
return;
|
|
@@ -102427,7 +102497,7 @@ async function realesrganGpuWorks() {
|
|
|
102427
102497
|
return false;
|
|
102428
102498
|
}
|
|
102429
102499
|
await new Promise((resolve16, reject) => {
|
|
102430
|
-
const c = spawn8(re.bin, ["-i", inP, "-o", outP, "-n", model, "-s", "2", "-m", re.models, "-g", "0"], { cwd: upscaleSpawnCwd(), stdio: "ignore" });
|
|
102500
|
+
const c = spawn8(re.bin, ["-i", inP, "-o", outP, "-n", model, "-s", "2", "-m", re.models, "-g", "0"], { cwd: upscaleSpawnCwd(), stdio: "ignore", windowsHide: true });
|
|
102431
102501
|
const t = setTimeout(() => {
|
|
102432
102502
|
killChild(c, "SIGKILL", "realesrgan GPU probe (timeout)");
|
|
102433
102503
|
reject(new Error("probe timeout"));
|
|
@@ -104875,6 +104945,7 @@ function readRecord(buf, base, rel) {
|
|
|
104875
104945
|
}
|
|
104876
104946
|
async function windowsFavorites() {
|
|
104877
104947
|
const script = [
|
|
104948
|
+
`[Console]::OutputEncoding=[System.Text.Encoding]::UTF8`,
|
|
104878
104949
|
`$ErrorActionPreference='SilentlyContinue'`,
|
|
104879
104950
|
`$sh = New-Object -ComObject Shell.Application`,
|
|
104880
104951
|
`$ns = $sh.Namespace('shell:::{679f85cb-0220-4080-b29b-5540cc05aab6}')`,
|
|
@@ -104882,10 +104953,10 @@ async function windowsFavorites() {
|
|
|
104882
104953
|
].join("; ");
|
|
104883
104954
|
const encoded = Buffer.from(script, "utf16le").toString("base64");
|
|
104884
104955
|
const stdout = await new Promise((resolve16) => {
|
|
104885
|
-
const child = execFile5("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded], { timeout: 4e3, windowsHide: true, maxBuffer: 1 << 20 }, (err2, out) => resolve16(err2 ? "" : out));
|
|
104956
|
+
const child = execFile5("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-EncodedCommand", encoded], { timeout: 4e3, windowsHide: true, maxBuffer: 1 << 20, encoding: "utf8" }, (err2, out) => resolve16(err2 ? "" : out));
|
|
104886
104957
|
child.on("error", () => resolve16(""));
|
|
104887
104958
|
});
|
|
104888
|
-
const items = stdout.split(/\r?\n/).map((line) => {
|
|
104959
|
+
const items = stdout.replace(/^/, "").split(/\r?\n/).map((line) => {
|
|
104889
104960
|
const tab = line.indexOf(" ");
|
|
104890
104961
|
if (tab < 0)
|
|
104891
104962
|
return null;
|
|
@@ -105397,10 +105468,19 @@ async function runRemoteFsOp(root, op2, args, quotaKey) {
|
|
|
105397
105468
|
id = statId(join56(real, e.name));
|
|
105398
105469
|
} catch {
|
|
105399
105470
|
}
|
|
105471
|
+
let isDir = e.isDirectory();
|
|
105472
|
+
if (e.isSymbolicLink()) {
|
|
105473
|
+
const resolved = safeContentPath(join56(real, e.name));
|
|
105474
|
+
try {
|
|
105475
|
+
isDir = resolved != null && statSync16(resolved).isDirectory();
|
|
105476
|
+
} catch {
|
|
105477
|
+
isDir = false;
|
|
105478
|
+
}
|
|
105479
|
+
}
|
|
105400
105480
|
return {
|
|
105401
105481
|
name: e.name,
|
|
105402
105482
|
path: join56(dir2, e.name),
|
|
105403
|
-
isDir
|
|
105483
|
+
isDir,
|
|
105404
105484
|
ignored: ignored.has(e.name),
|
|
105405
105485
|
size: id?.size,
|
|
105406
105486
|
mtimeMs: id?.mtimeMs,
|
|
@@ -105476,13 +105556,35 @@ async function runRemoteFsOp(root, op2, args, quotaKey) {
|
|
|
105476
105556
|
return OUTSIDE("write");
|
|
105477
105557
|
const off = Math.max(0, Math.floor(Number(a[1]) || 0));
|
|
105478
105558
|
const buf = Buffer.from(String(a[2] ?? ""), "base64");
|
|
105559
|
+
if (off === 0) {
|
|
105560
|
+
const tmp = join56(dirname22(real), `.${basename8(real)}.tesseract-upload-${process.pid}-${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`);
|
|
105561
|
+
let fd2 = null;
|
|
105562
|
+
try {
|
|
105563
|
+
await mkdir10(dirname22(real), { recursive: true });
|
|
105564
|
+
fd2 = openSync7(tmp, "w");
|
|
105565
|
+
if (buf.length > 0)
|
|
105566
|
+
writeSync2(fd2, buf, 0, buf.length, 0);
|
|
105567
|
+
closeSync7(fd2);
|
|
105568
|
+
fd2 = null;
|
|
105569
|
+
await renameAsync(tmp, real);
|
|
105570
|
+
return { ok: true, bytesWritten: buf.length };
|
|
105571
|
+
} catch (err2) {
|
|
105572
|
+
return { ok: false, error: String(err2.message) };
|
|
105573
|
+
} finally {
|
|
105574
|
+
if (fd2 != null)
|
|
105575
|
+
try {
|
|
105576
|
+
closeSync7(fd2);
|
|
105577
|
+
} catch {
|
|
105578
|
+
}
|
|
105579
|
+
try {
|
|
105580
|
+
rmSync6(tmp, { force: true });
|
|
105581
|
+
} catch {
|
|
105582
|
+
}
|
|
105583
|
+
}
|
|
105584
|
+
}
|
|
105479
105585
|
let fd = null;
|
|
105480
105586
|
try {
|
|
105481
|
-
|
|
105482
|
-
await mkdir10(dirname22(real), { recursive: true });
|
|
105483
|
-
fd = openSync7(real, "w");
|
|
105484
|
-
} else
|
|
105485
|
-
fd = openSync7(real, "r+");
|
|
105587
|
+
fd = openSync7(real, "r+");
|
|
105486
105588
|
if (buf.length > 0)
|
|
105487
105589
|
writeSync2(fd, buf, 0, buf.length, off);
|
|
105488
105590
|
return { ok: true, bytesWritten: buf.length };
|
|
@@ -105607,8 +105709,16 @@ async function runRemoteFsOp(root, op2, args, quotaKey) {
|
|
|
105607
105709
|
if (!insideOk(oldPath))
|
|
105608
105710
|
return OUTSIDE("rename");
|
|
105609
105711
|
const newPath = join56(dirname22(oldPath), newName);
|
|
105610
|
-
|
|
105611
|
-
|
|
105712
|
+
const caseOnlyRename = newPath.toLowerCase() === oldPath.toLowerCase() && newPath !== oldPath;
|
|
105713
|
+
if (!caseOnlyRename && existsSync24(newPath)) {
|
|
105714
|
+
let targetIsDir = false;
|
|
105715
|
+
try {
|
|
105716
|
+
targetIsDir = statSync16(newPath).isDirectory();
|
|
105717
|
+
} catch {
|
|
105718
|
+
}
|
|
105719
|
+
if (targetIsDir)
|
|
105720
|
+
return { ok: false, error: `A folder named "${newName}" already exists.` };
|
|
105721
|
+
}
|
|
105612
105722
|
try {
|
|
105613
105723
|
await renameAsync(oldPath, newPath);
|
|
105614
105724
|
return { ok: true, newPath };
|
|
@@ -105700,22 +105810,38 @@ async function runRemoteFsOp(root, op2, args, quotaKey) {
|
|
|
105700
105810
|
case "movePaths": {
|
|
105701
105811
|
const srcPaths = Array.isArray(a[0]) ? a[0] : [];
|
|
105702
105812
|
const destDir = String(a[1] ?? "");
|
|
105813
|
+
const destNamesArg = a[2];
|
|
105814
|
+
const destNames = Array.isArray(destNamesArg) ? destNamesArg.map((n) => typeof n === "string" && validName(n) ? n : void 0) : typeof destNamesArg === "string" && validName(destNamesArg) ? [destNamesArg] : [];
|
|
105703
105815
|
if (!insideOk(destDir))
|
|
105704
105816
|
return OUTSIDE(op2 === "copyPaths" ? "copy" : "move");
|
|
105705
105817
|
try {
|
|
105706
105818
|
const newPaths = [];
|
|
105707
|
-
for (
|
|
105819
|
+
for (let i = 0; i < srcPaths.length; i++) {
|
|
105820
|
+
const src = srcPaths[i];
|
|
105708
105821
|
if (typeof src !== "string" || !src)
|
|
105709
105822
|
continue;
|
|
105710
105823
|
if (!insideOk(src))
|
|
105711
105824
|
return OUTSIDE(op2 === "copyPaths" ? "copy from" : "move from");
|
|
105712
|
-
|
|
105825
|
+
const wantName = destNames[i];
|
|
105826
|
+
if (op2 === "movePaths" && !wantName && dirname22(src) === destDir) {
|
|
105713
105827
|
newPaths.push(src);
|
|
105714
105828
|
continue;
|
|
105715
105829
|
}
|
|
105716
|
-
|
|
105830
|
+
let target;
|
|
105831
|
+
if (wantName) {
|
|
105832
|
+
target = join56(destDir, wantName);
|
|
105833
|
+
let targetIsDir = false;
|
|
105834
|
+
try {
|
|
105835
|
+
targetIsDir = statSync16(target).isDirectory();
|
|
105836
|
+
} catch {
|
|
105837
|
+
}
|
|
105838
|
+
if (targetIsDir)
|
|
105839
|
+
return { ok: false, error: `A folder named "${wantName}" already exists.` };
|
|
105840
|
+
} else {
|
|
105841
|
+
target = resolveUniqueName(destDir, basename8(src));
|
|
105842
|
+
}
|
|
105717
105843
|
if (op2 === "copyPaths")
|
|
105718
|
-
await cp(src, target, { recursive: true, errorOnExist: false, force:
|
|
105844
|
+
await cp(src, target, { recursive: true, errorOnExist: false, force: !!wantName });
|
|
105719
105845
|
else
|
|
105720
105846
|
await moveItem(src, target);
|
|
105721
105847
|
newPaths.push(target);
|
|
@@ -106914,17 +107040,17 @@ var PeerLinkManager = class {
|
|
|
106914
107040
|
}
|
|
106915
107041
|
return this.awaitInbound(token4, msg.peerHostId);
|
|
106916
107042
|
}
|
|
106917
|
-
const
|
|
107043
|
+
const attempts2 = [
|
|
106918
107044
|
dialLadder({ peerHostId: msg.peerHostId, candidates: msg.peerCandidates, peerFingerprint256: msg.peerTlsFingerprint256, token: token4 })
|
|
106919
107045
|
];
|
|
106920
107046
|
if (quicOn)
|
|
106921
107047
|
for (const u of udp)
|
|
106922
|
-
|
|
106923
|
-
dbg(`[peer-link] handleGo dial: racing ${
|
|
107048
|
+
attempts2.push(this.quicDial(msg, token4, u));
|
|
107049
|
+
dbg(`[peer-link] handleGo dial: racing ${attempts2.length} rung(s) (TCP${quicOn ? " + QUIC" : ""}) to ${msg.peerHostId}`);
|
|
106924
107050
|
return new Promise((resolve16, reject) => {
|
|
106925
107051
|
let done = false, failed = 0;
|
|
106926
107052
|
const errs = [];
|
|
106927
|
-
for (const p of
|
|
107053
|
+
for (const p of attempts2) {
|
|
106928
107054
|
p.then((l) => {
|
|
106929
107055
|
if (done) {
|
|
106930
107056
|
try {
|
|
@@ -106940,7 +107066,7 @@ var PeerLinkManager = class {
|
|
|
106940
107066
|
}, (e) => {
|
|
106941
107067
|
errs.push(e);
|
|
106942
107068
|
console.warn(`[peer-link] dial rung failed: ${e.message}`);
|
|
106943
|
-
if (++failed ===
|
|
107069
|
+
if (++failed === attempts2.length && !done)
|
|
106944
107070
|
reject(new Error(`peer-link: no rung connected (${errs.map((x) => x.message).join("; ")})`));
|
|
106945
107071
|
});
|
|
106946
107072
|
}
|
|
@@ -110633,6 +110759,7 @@ function routeInboundLinkFrame(frame) {
|
|
|
110633
110759
|
init_dist5();
|
|
110634
110760
|
|
|
110635
110761
|
// ../../packages/host-core/dist/pty-manager.js
|
|
110762
|
+
import { spawn as spawn9 } from "node:child_process";
|
|
110636
110763
|
import { existsSync as existsSync31 } from "node:fs";
|
|
110637
110764
|
import { homedir as homedir11 } from "node:os";
|
|
110638
110765
|
init_safe_kill();
|
|
@@ -110668,6 +110795,24 @@ function flush(ptyId, s, sink3) {
|
|
|
110668
110795
|
s.bytes = 0;
|
|
110669
110796
|
sink3.output(ptyId, Buffer.from(joined, "utf8").toString("base64"));
|
|
110670
110797
|
}
|
|
110798
|
+
function killPtyProc(proc, signal2, label) {
|
|
110799
|
+
if (process.platform !== "win32") {
|
|
110800
|
+
killChild(proc, signal2, label);
|
|
110801
|
+
return;
|
|
110802
|
+
}
|
|
110803
|
+
const pid = proc.pid;
|
|
110804
|
+
if (typeof pid !== "number" || pid <= 0)
|
|
110805
|
+
return;
|
|
110806
|
+
try {
|
|
110807
|
+
proc.kill();
|
|
110808
|
+
} catch {
|
|
110809
|
+
}
|
|
110810
|
+
try {
|
|
110811
|
+
spawn9("taskkill", ["/pid", String(pid), "/T", "/F"], { stdio: "ignore", windowsHide: true }).on("error", () => {
|
|
110812
|
+
});
|
|
110813
|
+
} catch {
|
|
110814
|
+
}
|
|
110815
|
+
}
|
|
110671
110816
|
async function openPty(opts, sink3) {
|
|
110672
110817
|
const pty = await loadPty();
|
|
110673
110818
|
if (!pty)
|
|
@@ -110736,7 +110881,7 @@ function killPty(ptyId, signal2, clientId) {
|
|
|
110736
110881
|
clearTimeout(s.timer);
|
|
110737
110882
|
s.timer = null;
|
|
110738
110883
|
}
|
|
110739
|
-
|
|
110884
|
+
killPtyProc(s.proc, signal2, "pty session");
|
|
110740
110885
|
}
|
|
110741
110886
|
function releaseClientPtys(clientId) {
|
|
110742
110887
|
for (const [k, s] of [...sessions2]) {
|
|
@@ -110746,7 +110891,7 @@ function releaseClientPtys(clientId) {
|
|
|
110746
110891
|
clearTimeout(s.timer);
|
|
110747
110892
|
s.timer = null;
|
|
110748
110893
|
}
|
|
110749
|
-
|
|
110894
|
+
killPtyProc(s.proc, "SIGTERM", "pty session");
|
|
110750
110895
|
sessions2.delete(k);
|
|
110751
110896
|
}
|
|
110752
110897
|
}
|
|
@@ -110758,7 +110903,7 @@ function releaseRemotePtys() {
|
|
|
110758
110903
|
clearTimeout(s.timer);
|
|
110759
110904
|
s.timer = null;
|
|
110760
110905
|
}
|
|
110761
|
-
|
|
110906
|
+
killPtyProc(s.proc, "SIGTERM", "pty session");
|
|
110762
110907
|
sessions2.delete(k);
|
|
110763
110908
|
}
|
|
110764
110909
|
}
|
|
@@ -113855,12 +114000,40 @@ function displayBoundsById(monitorId) {
|
|
|
113855
114000
|
const d = el2.screen.getAllDisplays().find((x) => String(x.id) === monitorId) ?? el2.screen.getPrimaryDisplay();
|
|
113856
114001
|
return d ? { x: d.bounds.x, y: d.bounds.y, width: d.bounds.width, height: d.bounds.height } : null;
|
|
113857
114002
|
}
|
|
114003
|
+
function virtualDesktopFraction(globalDip) {
|
|
114004
|
+
const el2 = electronModule();
|
|
114005
|
+
const scr = el2?.screen;
|
|
114006
|
+
if (!scr)
|
|
114007
|
+
return { fx: 0, fy: 0 };
|
|
114008
|
+
const toPhys = (p) => typeof scr.dipToScreenPoint === "function" ? scr.dipToScreenPoint(p) : p;
|
|
114009
|
+
const displays = scr.getAllDisplays();
|
|
114010
|
+
if (!displays.length)
|
|
114011
|
+
return { fx: 0, fy: 0 };
|
|
114012
|
+
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
|
|
114013
|
+
for (const d of displays) {
|
|
114014
|
+
const tl = toPhys({ x: d.bounds.x, y: d.bounds.y });
|
|
114015
|
+
const pw = Math.round(d.bounds.width * (d.scaleFactor || 1));
|
|
114016
|
+
const ph = Math.round(d.bounds.height * (d.scaleFactor || 1));
|
|
114017
|
+
if (tl.x < minX)
|
|
114018
|
+
minX = tl.x;
|
|
114019
|
+
if (tl.y < minY)
|
|
114020
|
+
minY = tl.y;
|
|
114021
|
+
if (tl.x + pw > maxX)
|
|
114022
|
+
maxX = tl.x + pw;
|
|
114023
|
+
if (tl.y + ph > maxY)
|
|
114024
|
+
maxY = tl.y + ph;
|
|
114025
|
+
}
|
|
114026
|
+
const vw = Math.max(1, maxX - minX), vh = Math.max(1, maxY - minY);
|
|
114027
|
+
const phys = toPhys(globalDip);
|
|
114028
|
+
const clamp5 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
|
|
114029
|
+
return { fx: clamp5((phys.x - minX) / vw), fy: clamp5((phys.y - minY) / vh) };
|
|
114030
|
+
}
|
|
113858
114031
|
async function wakeDisplay(log10) {
|
|
113859
114032
|
if (process.platform !== "darwin")
|
|
113860
114033
|
return;
|
|
113861
114034
|
try {
|
|
113862
|
-
const { spawn:
|
|
113863
|
-
|
|
114035
|
+
const { spawn: spawn17 } = await import("node:child_process");
|
|
114036
|
+
spawn17("caffeinate", ["-u", "-t", "5"], { detached: true, stdio: "ignore" }).unref();
|
|
113864
114037
|
log10?.("display was asleep \u2014 declared user activity to wake it");
|
|
113865
114038
|
await new Promise((r) => setTimeout(r, 1200));
|
|
113866
114039
|
} catch {
|
|
@@ -114083,8 +114256,397 @@ var PublisherWindow = class {
|
|
|
114083
114256
|
}
|
|
114084
114257
|
};
|
|
114085
114258
|
|
|
114259
|
+
// ../../packages/host-core/dist/rdesk/win-input.js
|
|
114260
|
+
import { spawn as spawn10 } from "node:child_process";
|
|
114261
|
+
function vkForCode(code, key2) {
|
|
114262
|
+
if (code) {
|
|
114263
|
+
if (code.length === 4 && code.startsWith("Key")) {
|
|
114264
|
+
const c = code.charCodeAt(3);
|
|
114265
|
+
if (c >= 65 && c <= 90)
|
|
114266
|
+
return c;
|
|
114267
|
+
}
|
|
114268
|
+
if (code.length === 6 && code.startsWith("Digit")) {
|
|
114269
|
+
const c = code.charCodeAt(5);
|
|
114270
|
+
if (c >= 48 && c <= 57)
|
|
114271
|
+
return c;
|
|
114272
|
+
}
|
|
114273
|
+
if (code.startsWith("Numpad")) {
|
|
114274
|
+
const n = NUMPAD[code];
|
|
114275
|
+
if (n !== void 0)
|
|
114276
|
+
return n;
|
|
114277
|
+
}
|
|
114278
|
+
if (code.length >= 2 && code[0] === "F") {
|
|
114279
|
+
const n = Number(code.slice(1));
|
|
114280
|
+
if (Number.isInteger(n) && n >= 1 && n <= 24)
|
|
114281
|
+
return 112 + (n - 1);
|
|
114282
|
+
}
|
|
114283
|
+
const mapped = CODE_VK[code];
|
|
114284
|
+
if (mapped !== void 0)
|
|
114285
|
+
return mapped;
|
|
114286
|
+
}
|
|
114287
|
+
const named = KEY_VK[key2];
|
|
114288
|
+
if (named !== void 0)
|
|
114289
|
+
return named;
|
|
114290
|
+
if (key2.length === 1) {
|
|
114291
|
+
const c = key2.toUpperCase().charCodeAt(0);
|
|
114292
|
+
if (c >= 65 && c <= 90 || c >= 48 && c <= 57)
|
|
114293
|
+
return c;
|
|
114294
|
+
}
|
|
114295
|
+
return 0;
|
|
114296
|
+
}
|
|
114297
|
+
var NUMPAD = {
|
|
114298
|
+
Numpad0: 96,
|
|
114299
|
+
Numpad1: 97,
|
|
114300
|
+
Numpad2: 98,
|
|
114301
|
+
Numpad3: 99,
|
|
114302
|
+
Numpad4: 100,
|
|
114303
|
+
Numpad5: 101,
|
|
114304
|
+
Numpad6: 102,
|
|
114305
|
+
Numpad7: 103,
|
|
114306
|
+
Numpad8: 104,
|
|
114307
|
+
Numpad9: 105,
|
|
114308
|
+
NumpadMultiply: 106,
|
|
114309
|
+
NumpadAdd: 107,
|
|
114310
|
+
NumpadSubtract: 109,
|
|
114311
|
+
NumpadDecimal: 110,
|
|
114312
|
+
NumpadDivide: 111,
|
|
114313
|
+
NumpadEnter: 13
|
|
114314
|
+
};
|
|
114315
|
+
var CODE_VK = {
|
|
114316
|
+
// Modifiers (L/R specific so a chord releases the exact key it pressed).
|
|
114317
|
+
ShiftLeft: 160,
|
|
114318
|
+
ShiftRight: 161,
|
|
114319
|
+
ControlLeft: 162,
|
|
114320
|
+
ControlRight: 163,
|
|
114321
|
+
AltLeft: 164,
|
|
114322
|
+
AltRight: 165,
|
|
114323
|
+
MetaLeft: 91,
|
|
114324
|
+
MetaRight: 92,
|
|
114325
|
+
// Editing / whitespace.
|
|
114326
|
+
Enter: 13,
|
|
114327
|
+
Escape: 27,
|
|
114328
|
+
Tab: 9,
|
|
114329
|
+
Backspace: 8,
|
|
114330
|
+
Space: 32,
|
|
114331
|
+
CapsLock: 20,
|
|
114332
|
+
// Navigation (extended keys — the helper flags them EXTENDEDKEY).
|
|
114333
|
+
ArrowUp: 38,
|
|
114334
|
+
ArrowDown: 40,
|
|
114335
|
+
ArrowLeft: 37,
|
|
114336
|
+
ArrowRight: 39,
|
|
114337
|
+
Home: 36,
|
|
114338
|
+
End: 35,
|
|
114339
|
+
PageUp: 33,
|
|
114340
|
+
PageDown: 34,
|
|
114341
|
+
Insert: 45,
|
|
114342
|
+
Delete: 46,
|
|
114343
|
+
// US-position punctuation (host layout re-interprets; text still rides typeUnicode).
|
|
114344
|
+
Backquote: 192,
|
|
114345
|
+
Minus: 189,
|
|
114346
|
+
Equal: 187,
|
|
114347
|
+
BracketLeft: 219,
|
|
114348
|
+
BracketRight: 221,
|
|
114349
|
+
Backslash: 220,
|
|
114350
|
+
Semicolon: 186,
|
|
114351
|
+
Quote: 222,
|
|
114352
|
+
Comma: 188,
|
|
114353
|
+
Period: 190,
|
|
114354
|
+
Slash: 191,
|
|
114355
|
+
PrintScreen: 44,
|
|
114356
|
+
ScrollLock: 145,
|
|
114357
|
+
Pause: 19,
|
|
114358
|
+
ContextMenu: 93
|
|
114359
|
+
};
|
|
114360
|
+
var KEY_VK = {
|
|
114361
|
+
Shift: 16,
|
|
114362
|
+
Control: 17,
|
|
114363
|
+
Alt: 18,
|
|
114364
|
+
Meta: 91,
|
|
114365
|
+
Enter: 13,
|
|
114366
|
+
Escape: 27,
|
|
114367
|
+
Tab: 9,
|
|
114368
|
+
Backspace: 8,
|
|
114369
|
+
Delete: 46,
|
|
114370
|
+
" ": 32,
|
|
114371
|
+
ArrowUp: 38,
|
|
114372
|
+
ArrowDown: 40,
|
|
114373
|
+
ArrowLeft: 37,
|
|
114374
|
+
ArrowRight: 39,
|
|
114375
|
+
Home: 36,
|
|
114376
|
+
End: 35,
|
|
114377
|
+
PageUp: 33,
|
|
114378
|
+
PageDown: 34,
|
|
114379
|
+
Insert: 45
|
|
114380
|
+
};
|
|
114381
|
+
var BTN_CODE = { L: "L", R: "R", M: "M", X1: "1", X2: "2" };
|
|
114382
|
+
var CSHARP = `
|
|
114383
|
+
using System;
|
|
114384
|
+
using System.Runtime.InteropServices;
|
|
114385
|
+
using System.Globalization;
|
|
114386
|
+
namespace Tess {
|
|
114387
|
+
public static class Input {
|
|
114388
|
+
[StructLayout(LayoutKind.Sequential)] struct MOUSEINPUT { public int dx; public int dy; public uint mouseData; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; }
|
|
114389
|
+
[StructLayout(LayoutKind.Sequential)] struct KEYBDINPUT { public ushort wVk; public ushort wScan; public uint dwFlags; public uint time; public IntPtr dwExtraInfo; }
|
|
114390
|
+
[StructLayout(LayoutKind.Explicit)] struct INPUTUNION { [FieldOffset(0)] public MOUSEINPUT mi; [FieldOffset(0)] public KEYBDINPUT ki; }
|
|
114391
|
+
[StructLayout(LayoutKind.Sequential)] struct INPUT { public uint type; public INPUTUNION u; }
|
|
114392
|
+
[DllImport("user32.dll", SetLastError=true)] static extern uint SendInput(uint nInputs, INPUT[] pInputs, int cbSize);
|
|
114393
|
+
[DllImport("user32.dll")] static extern int GetSystemMetrics(int nIndex);
|
|
114394
|
+
[DllImport("user32.dll")] static extern IntPtr SetProcessDpiAwarenessContext(IntPtr value);
|
|
114395
|
+
|
|
114396
|
+
const uint INPUT_MOUSE=0, INPUT_KEYBOARD=1;
|
|
114397
|
+
const uint MOVE=0x0001, ABSOLUTE=0x8000, VIRTUALDESK=0x4000;
|
|
114398
|
+
const uint LEFTDOWN=0x0002, LEFTUP=0x0004, RIGHTDOWN=0x0008, RIGHTUP=0x0010, MIDDLEDOWN=0x0020, MIDDLEUP=0x0040, XDOWN=0x0080, XUP=0x0100, WHEEL=0x0800, HWHEEL=0x1000;
|
|
114399
|
+
const uint KEYUP=0x0002, UNICODE=0x0004, EXTENDED=0x0001;
|
|
114400
|
+
const uint XBUTTON1=0x0001, XBUTTON2=0x0002;
|
|
114401
|
+
const int SM_XVIRTUALSCREEN=76, SM_YVIRTUALSCREEN=77, SM_CXVIRTUALSCREEN=78, SM_CYVIRTUALSCREEN=79;
|
|
114402
|
+
static readonly CultureInfo CI = CultureInfo.InvariantCulture;
|
|
114403
|
+
|
|
114404
|
+
static void Send(INPUT[] a){ SendInput((uint)a.Length, a, Marshal.SizeOf(typeof(INPUT))); }
|
|
114405
|
+
static INPUT Mouse(uint flags, int dx, int dy, uint data){ INPUT i=new INPUT(); i.type=INPUT_MOUSE; i.u.mi.dx=dx; i.u.mi.dy=dy; i.u.mi.mouseData=data; i.u.mi.dwFlags=flags; return i; }
|
|
114406
|
+
static INPUT Kbd(ushort vk, ushort scan, uint flags){ INPUT i=new INPUT(); i.type=INPUT_KEYBOARD; i.u.ki.wVk=vk; i.u.ki.wScan=scan; i.u.ki.dwFlags=flags; return i; }
|
|
114407
|
+
|
|
114408
|
+
static int Clamp(int v){ return v<0?0:(v>65535?65535:v); }
|
|
114409
|
+
static void MoveFraction(double fx, double fy){
|
|
114410
|
+
int nx=Clamp((int)Math.Round(fx*65535.0)); int ny=Clamp((int)Math.Round(fy*65535.0));
|
|
114411
|
+
Send(new INPUT[]{ Mouse(MOVE|ABSOLUTE|VIRTUALDESK, nx, ny, 0) });
|
|
114412
|
+
}
|
|
114413
|
+
static void MovePhysical(int px, int py){
|
|
114414
|
+
int vx=GetSystemMetrics(SM_XVIRTUALSCREEN), vy=GetSystemMetrics(SM_YVIRTUALSCREEN);
|
|
114415
|
+
int vw=GetSystemMetrics(SM_CXVIRTUALSCREEN), vh=GetSystemMetrics(SM_CYVIRTUALSCREEN);
|
|
114416
|
+
if(vw<1) vw=1; if(vh<1) vh=1;
|
|
114417
|
+
MoveFraction((px-vx)/(double)vw, (py-vy)/(double)vh);
|
|
114418
|
+
}
|
|
114419
|
+
static void Button(string b, bool down){
|
|
114420
|
+
switch(b){
|
|
114421
|
+
case "L": Send(new INPUT[]{ Mouse(down?LEFTDOWN:LEFTUP,0,0,0) }); break;
|
|
114422
|
+
case "R": Send(new INPUT[]{ Mouse(down?RIGHTDOWN:RIGHTUP,0,0,0) }); break;
|
|
114423
|
+
case "M": Send(new INPUT[]{ Mouse(down?MIDDLEDOWN:MIDDLEUP,0,0,0) }); break;
|
|
114424
|
+
case "1": Send(new INPUT[]{ Mouse(down?XDOWN:XUP,0,0,XBUTTON1) }); break;
|
|
114425
|
+
case "2": Send(new INPUT[]{ Mouse(down?XDOWN:XUP,0,0,XBUTTON2) }); break;
|
|
114426
|
+
}
|
|
114427
|
+
}
|
|
114428
|
+
static void Wheel(int dx, int dy){
|
|
114429
|
+
if(dy!=0) Send(new INPUT[]{ Mouse(WHEEL,0,0,(uint)dy) });
|
|
114430
|
+
if(dx!=0) Send(new INPUT[]{ Mouse(HWHEEL,0,0,(uint)dx) });
|
|
114431
|
+
}
|
|
114432
|
+
static bool IsExtended(ushort vk){
|
|
114433
|
+
switch(vk){
|
|
114434
|
+
case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28:
|
|
114435
|
+
case 0x2d: case 0x2e: case 0x5b: case 0x5c: case 0x5d: case 0xa3: case 0xa5: case 0x2c: return true;
|
|
114436
|
+
default: return false;
|
|
114437
|
+
}
|
|
114438
|
+
}
|
|
114439
|
+
static void Key(ushort vk, bool down){
|
|
114440
|
+
uint flags = down?0u:KEYUP;
|
|
114441
|
+
if(IsExtended(vk)) flags |= EXTENDED;
|
|
114442
|
+
Send(new INPUT[]{ Kbd(vk,0,flags) });
|
|
114443
|
+
}
|
|
114444
|
+
static void Unicode(string[] p, int start){
|
|
114445
|
+
for(int i=start;i<p.Length;i++){
|
|
114446
|
+
ushort u;
|
|
114447
|
+
if(!ushort.TryParse(p[i], NumberStyles.HexNumber, CI, out u)) continue;
|
|
114448
|
+
Send(new INPUT[]{ Kbd(0,u,UNICODE), Kbd(0,u,UNICODE|KEYUP) });
|
|
114449
|
+
}
|
|
114450
|
+
}
|
|
114451
|
+
static void Dispatch(string line){
|
|
114452
|
+
if(line.Length==0) return;
|
|
114453
|
+
string[] p = line.Split(' ');
|
|
114454
|
+
switch(p[0]){
|
|
114455
|
+
case "MF": MoveFraction(double.Parse(p[1],CI), double.Parse(p[2],CI)); break;
|
|
114456
|
+
case "M": MovePhysical(int.Parse(p[1],CI), int.Parse(p[2],CI)); break;
|
|
114457
|
+
case "B": Button(p[1], p[2]=="1"); break;
|
|
114458
|
+
case "W": Wheel(int.Parse(p[1],CI), int.Parse(p[2],CI)); break;
|
|
114459
|
+
case "K": Key(ushort.Parse(p[1],CI), p[2]=="1"); break;
|
|
114460
|
+
case "U": Unicode(p, 1); break;
|
|
114461
|
+
case "PING": Console.Out.WriteLine("PONG"); Console.Out.Flush(); break;
|
|
114462
|
+
}
|
|
114463
|
+
}
|
|
114464
|
+
public static void Pump(){
|
|
114465
|
+
try { SetProcessDpiAwarenessContext(new IntPtr(-4)); } catch {} // PER_MONITOR_AWARE_V2; best-effort
|
|
114466
|
+
Console.Out.WriteLine("READY"); Console.Out.Flush();
|
|
114467
|
+
string line;
|
|
114468
|
+
while((line = Console.In.ReadLine()) != null){
|
|
114469
|
+
try { Dispatch(line); } catch { /* one bad command must never kill the pump */ }
|
|
114470
|
+
}
|
|
114471
|
+
}
|
|
114472
|
+
}
|
|
114473
|
+
}
|
|
114474
|
+
`;
|
|
114475
|
+
var PS_SCRIPT = `$ErrorActionPreference='Stop'; Add-Type -TypeDefinition @'
|
|
114476
|
+
${CSHARP}
|
|
114477
|
+
'@ ; [Tess.Input]::Pump()`;
|
|
114478
|
+
var WinInputImpl = class {
|
|
114479
|
+
child = null;
|
|
114480
|
+
starting = null;
|
|
114481
|
+
queue = [];
|
|
114482
|
+
readyResolvers = [];
|
|
114483
|
+
stdoutBuf = "";
|
|
114484
|
+
stopped = false;
|
|
114485
|
+
log;
|
|
114486
|
+
constructor(log10) {
|
|
114487
|
+
this.log = log10 ?? (() => {
|
|
114488
|
+
});
|
|
114489
|
+
}
|
|
114490
|
+
/** Start the helper if it isn't running. Idempotent and lazy — a crashed helper is relaunched
|
|
114491
|
+
* the next time a command is sent (the queued command flushes once it's up). */
|
|
114492
|
+
ensure() {
|
|
114493
|
+
if (this.stopped || this.child)
|
|
114494
|
+
return;
|
|
114495
|
+
const b642 = Buffer.from(PS_SCRIPT, "utf16le").toString("base64");
|
|
114496
|
+
let child;
|
|
114497
|
+
try {
|
|
114498
|
+
child = spawn10("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-EncodedCommand", b642], {
|
|
114499
|
+
windowsHide: true,
|
|
114500
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
114501
|
+
});
|
|
114502
|
+
} catch (e) {
|
|
114503
|
+
this.log(`win-input: could not spawn powershell: ${e instanceof Error ? e.message : String(e)}`);
|
|
114504
|
+
return;
|
|
114505
|
+
}
|
|
114506
|
+
this.child = child;
|
|
114507
|
+
child.stdout.setEncoding("utf8");
|
|
114508
|
+
child.stdout.on("data", (d) => {
|
|
114509
|
+
this.stdoutBuf += d;
|
|
114510
|
+
if (this.stdoutBuf.includes("READY")) {
|
|
114511
|
+
this.stdoutBuf = "";
|
|
114512
|
+
const r = this.readyResolvers;
|
|
114513
|
+
this.readyResolvers = [];
|
|
114514
|
+
for (const res of r) {
|
|
114515
|
+
try {
|
|
114516
|
+
res();
|
|
114517
|
+
} catch {
|
|
114518
|
+
}
|
|
114519
|
+
}
|
|
114520
|
+
}
|
|
114521
|
+
});
|
|
114522
|
+
child.stderr.setEncoding("utf8");
|
|
114523
|
+
child.stderr.on("data", (d) => {
|
|
114524
|
+
const s = d.trim();
|
|
114525
|
+
if (s)
|
|
114526
|
+
this.log(`win-input stderr: ${s.slice(0, 300)}`);
|
|
114527
|
+
});
|
|
114528
|
+
child.on("error", (e) => {
|
|
114529
|
+
this.log(`win-input: helper error: ${e instanceof Error ? e.message : String(e)}`);
|
|
114530
|
+
this.child = null;
|
|
114531
|
+
});
|
|
114532
|
+
child.on("exit", (code, signal2) => {
|
|
114533
|
+
if (!this.stopped)
|
|
114534
|
+
this.log(`win-input: helper exited (code=${code ?? "?"} signal=${signal2 ?? "-"}) \u2014 will relaunch on next input`);
|
|
114535
|
+
this.child = null;
|
|
114536
|
+
});
|
|
114537
|
+
if (this.queue.length) {
|
|
114538
|
+
const q = this.queue;
|
|
114539
|
+
this.queue = [];
|
|
114540
|
+
for (const line of q)
|
|
114541
|
+
this.rawWrite(line);
|
|
114542
|
+
}
|
|
114543
|
+
}
|
|
114544
|
+
rawWrite(line) {
|
|
114545
|
+
const c = this.child;
|
|
114546
|
+
if (!c || !c.stdin.writable) {
|
|
114547
|
+
this.queue.push(line);
|
|
114548
|
+
if (this.queue.length > 512)
|
|
114549
|
+
this.queue.shift();
|
|
114550
|
+
return;
|
|
114551
|
+
}
|
|
114552
|
+
try {
|
|
114553
|
+
c.stdin.write(line + "\n");
|
|
114554
|
+
} catch (e) {
|
|
114555
|
+
this.log(`win-input: write failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
114556
|
+
}
|
|
114557
|
+
}
|
|
114558
|
+
send(line) {
|
|
114559
|
+
this.ensure();
|
|
114560
|
+
this.rawWrite(line);
|
|
114561
|
+
}
|
|
114562
|
+
async warm() {
|
|
114563
|
+
if (this.stopped)
|
|
114564
|
+
return;
|
|
114565
|
+
this.ensure();
|
|
114566
|
+
if (!this.child)
|
|
114567
|
+
return;
|
|
114568
|
+
await new Promise((resolve16) => {
|
|
114569
|
+
const t = setTimeout(() => {
|
|
114570
|
+
this.readyResolvers = this.readyResolvers.filter((r) => r !== resolve16);
|
|
114571
|
+
resolve16();
|
|
114572
|
+
}, 6e3);
|
|
114573
|
+
t.unref?.();
|
|
114574
|
+
this.readyResolvers.push(() => {
|
|
114575
|
+
clearTimeout(t);
|
|
114576
|
+
resolve16();
|
|
114577
|
+
});
|
|
114578
|
+
});
|
|
114579
|
+
}
|
|
114580
|
+
async moveAbsolute(fx, fy) {
|
|
114581
|
+
this.send(`MF ${clampFrac(fx)} ${clampFrac(fy)}`);
|
|
114582
|
+
}
|
|
114583
|
+
async moveAbsolutePhysical(px, py) {
|
|
114584
|
+
this.send(`M ${Math.round(px)} ${Math.round(py)}`);
|
|
114585
|
+
}
|
|
114586
|
+
async button(btn, down) {
|
|
114587
|
+
this.send(`B ${BTN_CODE[btn]} ${down ? 1 : 0}`);
|
|
114588
|
+
}
|
|
114589
|
+
async wheel(dx, dy) {
|
|
114590
|
+
this.send(`W ${Math.round(dx)} ${Math.round(dy)}`);
|
|
114591
|
+
}
|
|
114592
|
+
async keyByVk(vk, down) {
|
|
114593
|
+
if (vk > 0)
|
|
114594
|
+
this.send(`K ${vk | 0} ${down ? 1 : 0}`);
|
|
114595
|
+
}
|
|
114596
|
+
async typeUnicode(text13) {
|
|
114597
|
+
if (!text13)
|
|
114598
|
+
return;
|
|
114599
|
+
const units = [];
|
|
114600
|
+
for (let i = 0; i < text13.length; i++)
|
|
114601
|
+
units.push(text13.charCodeAt(i).toString(16));
|
|
114602
|
+
for (let i = 0; i < units.length; i += 64)
|
|
114603
|
+
this.send(`U ${units.slice(i, i + 64).join(" ")}`);
|
|
114604
|
+
}
|
|
114605
|
+
async stop() {
|
|
114606
|
+
this.stopped = true;
|
|
114607
|
+
const c = this.child;
|
|
114608
|
+
this.child = null;
|
|
114609
|
+
this.queue = [];
|
|
114610
|
+
if (!c)
|
|
114611
|
+
return;
|
|
114612
|
+
try {
|
|
114613
|
+
c.stdin.end();
|
|
114614
|
+
} catch {
|
|
114615
|
+
}
|
|
114616
|
+
await new Promise((resolve16) => {
|
|
114617
|
+
let done = false;
|
|
114618
|
+
const finish2 = () => {
|
|
114619
|
+
if (done)
|
|
114620
|
+
return;
|
|
114621
|
+
done = true;
|
|
114622
|
+
resolve16();
|
|
114623
|
+
};
|
|
114624
|
+
c.on("exit", finish2);
|
|
114625
|
+
const t = setTimeout(() => {
|
|
114626
|
+
try {
|
|
114627
|
+
c.kill();
|
|
114628
|
+
} catch {
|
|
114629
|
+
}
|
|
114630
|
+
finish2();
|
|
114631
|
+
}, 800);
|
|
114632
|
+
t.unref?.();
|
|
114633
|
+
});
|
|
114634
|
+
}
|
|
114635
|
+
};
|
|
114636
|
+
function clampFrac(v) {
|
|
114637
|
+
if (!Number.isFinite(v))
|
|
114638
|
+
return 0;
|
|
114639
|
+
return v < 0 ? 0 : v > 1 ? 1 : Math.round(v * 1e6) / 1e6;
|
|
114640
|
+
}
|
|
114641
|
+
function createWinInput(opts = {}) {
|
|
114642
|
+
if (process.platform !== "win32")
|
|
114643
|
+
return null;
|
|
114644
|
+
return new WinInputImpl(opts.log);
|
|
114645
|
+
}
|
|
114646
|
+
|
|
114086
114647
|
// ../../packages/host-core/dist/rdesk/input-injector.js
|
|
114087
114648
|
var BUTTON = { 0: "left", 1: "middle", 2: "right" };
|
|
114649
|
+
var WIN_BUTTON = { 0: "L", 1: "M", 2: "R", 3: "X1", 4: "X2" };
|
|
114088
114650
|
function normalizeKey(key2) {
|
|
114089
114651
|
const named = {
|
|
114090
114652
|
" ": "space",
|
|
@@ -114120,26 +114682,91 @@ function isComboKey(key2, modifiers = 0) {
|
|
|
114120
114682
|
const hasHardMod = (modifiers & (1 | 2 | 4)) !== 0;
|
|
114121
114683
|
return key2.length !== 1 || hasHardMod;
|
|
114122
114684
|
}
|
|
114685
|
+
var COMBO_TOKEN_VK = {
|
|
114686
|
+
ctrl: 17,
|
|
114687
|
+
control: 17,
|
|
114688
|
+
alt: 18,
|
|
114689
|
+
option: 18,
|
|
114690
|
+
opt: 18,
|
|
114691
|
+
shift: 16,
|
|
114692
|
+
cmd: 91,
|
|
114693
|
+
command: 91,
|
|
114694
|
+
meta: 91,
|
|
114695
|
+
win: 91,
|
|
114696
|
+
super: 91,
|
|
114697
|
+
enter: 13,
|
|
114698
|
+
return: 13,
|
|
114699
|
+
tab: 9,
|
|
114700
|
+
escape: 27,
|
|
114701
|
+
esc: 27,
|
|
114702
|
+
space: 32,
|
|
114703
|
+
backspace: 8,
|
|
114704
|
+
delete: 46,
|
|
114705
|
+
del: 46,
|
|
114706
|
+
insert: 45,
|
|
114707
|
+
up: 38,
|
|
114708
|
+
down: 40,
|
|
114709
|
+
left: 37,
|
|
114710
|
+
right: 39,
|
|
114711
|
+
home: 36,
|
|
114712
|
+
end: 35,
|
|
114713
|
+
pageup: 33,
|
|
114714
|
+
pgup: 33,
|
|
114715
|
+
pagedown: 34,
|
|
114716
|
+
pgdn: 34
|
|
114717
|
+
};
|
|
114718
|
+
function comboTokenVk(token4) {
|
|
114719
|
+
const t = token4.trim().toLowerCase();
|
|
114720
|
+
if (COMBO_TOKEN_VK[t] !== void 0)
|
|
114721
|
+
return COMBO_TOKEN_VK[t];
|
|
114722
|
+
if (/^f([1-9]|1[0-9]|2[0-4])$/.test(t))
|
|
114723
|
+
return 112 + (Number(t.slice(1)) - 1);
|
|
114724
|
+
if (t.length === 1) {
|
|
114725
|
+
const c = t.toUpperCase().charCodeAt(0);
|
|
114726
|
+
if (c >= 65 && c <= 90 || c >= 48 && c <= 57)
|
|
114727
|
+
return c;
|
|
114728
|
+
}
|
|
114729
|
+
return 0;
|
|
114730
|
+
}
|
|
114123
114731
|
var RdeskInputInjector = class {
|
|
114124
114732
|
runner;
|
|
114125
114733
|
origin;
|
|
114126
114734
|
access;
|
|
114127
|
-
|
|
114735
|
+
win;
|
|
114736
|
+
/** VKs currently held down on the Windows path — released on view/disconnect/blur so a
|
|
114737
|
+
* modifier or key never "sticks" on the remote machine after control ends. */
|
|
114738
|
+
heldVks = /* @__PURE__ */ new Set();
|
|
114739
|
+
constructor(runner2, origin, access, win3 = null) {
|
|
114128
114740
|
this.runner = runner2;
|
|
114129
114741
|
this.origin = origin;
|
|
114130
114742
|
this.access = access;
|
|
114743
|
+
this.win = win3;
|
|
114131
114744
|
}
|
|
114132
114745
|
setAccess(level) {
|
|
114746
|
+
if (this.access === "control" && level !== "control")
|
|
114747
|
+
this.releaseAll();
|
|
114133
114748
|
this.access = level;
|
|
114134
114749
|
}
|
|
114135
114750
|
setOrigin(origin) {
|
|
114136
114751
|
this.origin = origin;
|
|
114137
114752
|
}
|
|
114753
|
+
/** Release every held key on the Windows path. Called on access change, disconnect and blur. */
|
|
114754
|
+
releaseAll() {
|
|
114755
|
+
if (!this.win) {
|
|
114756
|
+
this.heldVks.clear();
|
|
114757
|
+
return;
|
|
114758
|
+
}
|
|
114759
|
+
for (const vk of this.heldVks)
|
|
114760
|
+
void this.win.api.keyByVk(vk, false);
|
|
114761
|
+
this.heldVks.clear();
|
|
114762
|
+
}
|
|
114138
114763
|
/** Apply one viewer input event, or drop it when the session is view-only. */
|
|
114139
114764
|
async apply(ev) {
|
|
114140
114765
|
if (this.access !== "control")
|
|
114141
114766
|
return;
|
|
114142
114767
|
const at = (x, y) => ({ x: this.origin.x + x, y: this.origin.y + y });
|
|
114768
|
+
if (this.win)
|
|
114769
|
+
return this.applyWin(ev, at);
|
|
114143
114770
|
if (ev.kind === "mouse") {
|
|
114144
114771
|
if (ev.action === "move")
|
|
114145
114772
|
return void this.runner.mouseMove(at(ev.x, ev.y));
|
|
@@ -114160,686 +114787,134 @@ var RdeskInputInjector = class {
|
|
|
114160
114787
|
return void this.runner.pressKeys(keyCombo(ev.key, ev.modifiers));
|
|
114161
114788
|
}
|
|
114162
114789
|
}
|
|
114163
|
-
|
|
114164
|
-
|
|
114165
|
-
|
|
114166
|
-
|
|
114167
|
-
|
|
114168
|
-
|
|
114169
|
-
|
|
114170
|
-
|
|
114171
|
-
|
|
114172
|
-
|
|
114173
|
-
|
|
114174
|
-
|
|
114175
|
-
|
|
114176
|
-
|
|
114177
|
-
|
|
114178
|
-
try {
|
|
114179
|
-
dbus = await import("dbus-next");
|
|
114180
|
-
} catch (e) {
|
|
114181
|
-
return fail4(`dbus-next could not be loaded: ${e instanceof Error ? e.message : String(e)}`);
|
|
114182
|
-
}
|
|
114183
|
-
try {
|
|
114184
|
-
const bus = dbus.sessionBus();
|
|
114185
|
-
const obj = await bus.getProxyObject("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop");
|
|
114186
|
-
const portal = obj.getInterface("org.freedesktop.portal.RemoteDesktop");
|
|
114187
|
-
const sessionHandle = await establishSession(portal);
|
|
114188
|
-
if (!sessionHandle)
|
|
114189
|
-
return fail4(`the RemoteDesktop portal did not grant a session${lastSessionError ? ` (${lastSessionError})` : " (Start was refused, or nobody answered its consent prompt on that machine)"}`);
|
|
114190
|
-
const opts = {};
|
|
114191
|
-
return {
|
|
114192
|
-
pointerMotionAbsolute: (x, y, w, h) => void portal.NotifyPointerMotionAbsolute(sessionHandle, opts, 0, x / Math.max(1, w), y / Math.max(1, h)).catch(() => {
|
|
114193
|
-
}),
|
|
114194
|
-
pointerButton: (b, down) => void portal.NotifyPointerButton(sessionHandle, opts, b, down ? 1 : 0).catch(() => {
|
|
114195
|
-
}),
|
|
114196
|
-
pointerAxis: (dx, dy) => void portal.NotifyPointerAxis(sessionHandle, opts, dx, dy).catch(() => {
|
|
114197
|
-
}),
|
|
114198
|
-
keyboardKeycode: (k, down) => void portal.NotifyKeyboardKeycode(sessionHandle, opts, k, down ? 1 : 0).catch(() => {
|
|
114199
|
-
}),
|
|
114200
|
-
keyboardKeysym: (ks, down) => void portal.NotifyKeyboardKeysym(sessionHandle, opts, ks, down ? 1 : 0).catch(() => {
|
|
114201
|
-
}),
|
|
114202
|
-
close: () => {
|
|
114790
|
+
/** The Windows input path (win32 only). Absolute virtual-desktop mouse, Unicode text, real
|
|
114791
|
+
* key down/up by virtual-key code. */
|
|
114792
|
+
applyWin(ev, at) {
|
|
114793
|
+
const api = this.win.api;
|
|
114794
|
+
if (ev.kind === "mouse") {
|
|
114795
|
+
const f = this.win.toFraction(at(ev.x, ev.y));
|
|
114796
|
+
void api.moveAbsolute(f.fx, f.fy);
|
|
114797
|
+
const btn = WIN_BUTTON[ev.button ?? 0] ?? "L";
|
|
114798
|
+
if (ev.action === "down")
|
|
114799
|
+
void api.button(btn, true);
|
|
114800
|
+
else if (ev.action === "up")
|
|
114801
|
+
void api.button(btn, false);
|
|
114802
|
+
else if (ev.action === "click") {
|
|
114803
|
+
void api.button(btn, true);
|
|
114804
|
+
void api.button(btn, false);
|
|
114203
114805
|
}
|
|
114204
|
-
|
|
114205
|
-
|
|
114206
|
-
|
|
114207
|
-
|
|
114208
|
-
|
|
114209
|
-
|
|
114210
|
-
|
|
114211
|
-
|
|
114212
|
-
|
|
114213
|
-
|
|
114214
|
-
|
|
114215
|
-
|
|
114216
|
-
await portal.Start(session, "", { handle_token: token4 + "g" });
|
|
114217
|
-
return session;
|
|
114218
|
-
} catch (e) {
|
|
114219
|
-
lastSessionError = e instanceof Error ? e.message : String(e);
|
|
114220
|
-
return null;
|
|
114221
|
-
}
|
|
114222
|
-
}
|
|
114223
|
-
var KEYSYM = {
|
|
114224
|
-
// Modifiers
|
|
114225
|
-
shift: 65505,
|
|
114226
|
-
ctrl: 65507,
|
|
114227
|
-
alt: 65513,
|
|
114228
|
-
cmd: 65515,
|
|
114229
|
-
// Shift_L, Control_L, Alt_L, Super_L
|
|
114230
|
-
// Named keys
|
|
114231
|
-
enter: 65293,
|
|
114232
|
-
tab: 65289,
|
|
114233
|
-
escape: 65307,
|
|
114234
|
-
backspace: 65288,
|
|
114235
|
-
delete: 65535,
|
|
114236
|
-
space: 32,
|
|
114237
|
-
up: 65362,
|
|
114238
|
-
down: 65364,
|
|
114239
|
-
left: 65361,
|
|
114240
|
-
right: 65363,
|
|
114241
|
-
home: 65360,
|
|
114242
|
-
end: 65367,
|
|
114243
|
-
pageup: 65365,
|
|
114244
|
-
pagedown: 65366,
|
|
114245
|
-
insert: 65379,
|
|
114246
|
-
f1: 65470,
|
|
114247
|
-
f2: 65471,
|
|
114248
|
-
f3: 65472,
|
|
114249
|
-
f4: 65473,
|
|
114250
|
-
f5: 65474,
|
|
114251
|
-
f6: 65475,
|
|
114252
|
-
f7: 65476,
|
|
114253
|
-
f8: 65477,
|
|
114254
|
-
f9: 65478,
|
|
114255
|
-
f10: 65479,
|
|
114256
|
-
f11: 65480,
|
|
114257
|
-
f12: 65481
|
|
114258
|
-
};
|
|
114259
|
-
function charKeysym(ch) {
|
|
114260
|
-
const cp2 = ch.codePointAt(0) ?? 0;
|
|
114261
|
-
if (cp2 === 0)
|
|
114262
|
-
return 0;
|
|
114263
|
-
return cp2 <= 255 ? cp2 : 16777216 + cp2;
|
|
114264
|
-
}
|
|
114265
|
-
function tokenKeysym(token4) {
|
|
114266
|
-
const t = token4.trim().toLowerCase();
|
|
114267
|
-
if (KEYSYM[t] !== void 0)
|
|
114268
|
-
return KEYSYM[t];
|
|
114269
|
-
return t.length === 1 ? charKeysym(t) : 0;
|
|
114270
|
-
}
|
|
114271
|
-
function waylandInputRunner(p, screen) {
|
|
114272
|
-
const w = () => screen.w, h = () => screen.h;
|
|
114273
|
-
return {
|
|
114274
|
-
async mouseMove(pt) {
|
|
114275
|
-
p.pointerMotionAbsolute(pt.x, pt.y, w(), h());
|
|
114276
|
-
},
|
|
114277
|
-
async mouseClick(o) {
|
|
114278
|
-
if (o.at)
|
|
114279
|
-
p.pointerMotionAbsolute(o.at.x, o.at.y, w(), h());
|
|
114280
|
-
const b = BTN[o.button ?? "left"];
|
|
114281
|
-
const click = () => {
|
|
114282
|
-
p.pointerButton(b, true);
|
|
114283
|
-
p.pointerButton(b, false);
|
|
114284
|
-
};
|
|
114285
|
-
click();
|
|
114286
|
-
if (o.double)
|
|
114287
|
-
click();
|
|
114288
|
-
},
|
|
114289
|
-
async scroll(o) {
|
|
114290
|
-
p.pointerAxis(o.dx ?? 0, o.dy ?? 0);
|
|
114291
|
-
},
|
|
114292
|
-
async typeText(text13) {
|
|
114293
|
-
for (const ch of text13) {
|
|
114294
|
-
const ks = charKeysym(ch);
|
|
114295
|
-
if (!ks)
|
|
114296
|
-
continue;
|
|
114297
|
-
p.keyboardKeysym(ks, true);
|
|
114298
|
-
p.keyboardKeysym(ks, false);
|
|
114806
|
+
return;
|
|
114807
|
+
}
|
|
114808
|
+
if (ev.kind === "wheel") {
|
|
114809
|
+
void api.moveAbsolute(...fracOf(this.win.toFraction(at(ev.x, ev.y))));
|
|
114810
|
+
void api.wheel(-Math.round(ev.deltaX), -Math.round(ev.deltaY));
|
|
114811
|
+
return;
|
|
114812
|
+
}
|
|
114813
|
+
if (ev.kind === "key") {
|
|
114814
|
+
if (ev.action === "char") {
|
|
114815
|
+
if (ev.text)
|
|
114816
|
+
void api.typeUnicode(ev.text);
|
|
114817
|
+
return;
|
|
114299
114818
|
}
|
|
114300
|
-
|
|
114301
|
-
|
|
114302
|
-
|
|
114303
|
-
if (!parts.length)
|
|
114819
|
+
if (ev.key.includes("+")) {
|
|
114820
|
+
if (ev.action === "down")
|
|
114821
|
+
this.pressWinCombo(ev.key);
|
|
114304
114822
|
return;
|
|
114305
|
-
|
|
114306
|
-
const
|
|
114307
|
-
if (!
|
|
114823
|
+
}
|
|
114824
|
+
const vk = vkForCode(ev.code, ev.key);
|
|
114825
|
+
if (!vk)
|
|
114308
114826
|
return;
|
|
114309
|
-
|
|
114310
|
-
|
|
114311
|
-
|
|
114312
|
-
|
|
114313
|
-
|
|
114314
|
-
|
|
114315
|
-
|
|
114316
|
-
|
|
114317
|
-
|
|
114318
|
-
|
|
114319
|
-
// ../../packages/host-core/dist/rdesk/host-clipboard.js
|
|
114320
|
-
init_dist2();
|
|
114321
|
-
import { statSync as statSync19 } from "node:fs";
|
|
114322
|
-
import { basename as basename13 } from "node:path";
|
|
114323
|
-
function readHostClipboardFilePaths() {
|
|
114324
|
-
const el2 = electronModule();
|
|
114325
|
-
const clip2 = el2?.clipboard;
|
|
114326
|
-
if (!clip2)
|
|
114327
|
-
return [];
|
|
114328
|
-
try {
|
|
114329
|
-
return readFilePathsWith((f) => clip2.read(f));
|
|
114330
|
-
} catch {
|
|
114331
|
-
return [];
|
|
114332
|
-
}
|
|
114333
|
-
}
|
|
114334
|
-
function describeClipFiles(paths) {
|
|
114335
|
-
const out = [];
|
|
114336
|
-
for (const p of paths) {
|
|
114337
|
-
try {
|
|
114338
|
-
const st = statSync19(p);
|
|
114339
|
-
out.push({ path: p, name: basename13(p), isDir: st.isDirectory(), size: st.isDirectory() ? 0 : st.size });
|
|
114340
|
-
} catch {
|
|
114827
|
+
const mods = ev.modifiers ?? 0;
|
|
114828
|
+
if (ev.action === "down") {
|
|
114829
|
+
if (ev.key.length === 1 && (mods & (2 | 4)) === 0)
|
|
114830
|
+
return;
|
|
114831
|
+
void api.keyByVk(vk, true);
|
|
114832
|
+
this.heldVks.add(vk);
|
|
114833
|
+
} else if (ev.action === "up") {
|
|
114834
|
+
void api.keyByVk(vk, false);
|
|
114835
|
+
this.heldVks.delete(vk);
|
|
114836
|
+
}
|
|
114341
114837
|
}
|
|
114342
114838
|
}
|
|
114343
|
-
|
|
114344
|
-
|
|
114345
|
-
|
|
114346
|
-
|
|
114347
|
-
|
|
114348
|
-
|
|
114349
|
-
if (stopped2)
|
|
114350
|
-
return;
|
|
114351
|
-
const paths = readHostClipboardFilePaths();
|
|
114352
|
-
const key2 = paths.join("\0");
|
|
114353
|
-
if (key2 === last)
|
|
114839
|
+
/** Inject a named chord ("ctrl+alt+delete") as a real down/up sequence on the Windows path. */
|
|
114840
|
+
pressWinCombo(combo) {
|
|
114841
|
+
const api = this.win.api;
|
|
114842
|
+
const toks = combo.split("+").map((t) => t.trim()).filter(Boolean);
|
|
114843
|
+
const vks = toks.map(comboTokenVk).filter((v) => v > 0);
|
|
114844
|
+
if (!vks.length)
|
|
114354
114845
|
return;
|
|
114355
|
-
|
|
114356
|
-
|
|
114357
|
-
|
|
114358
|
-
|
|
114359
|
-
|
|
114360
|
-
|
|
114361
|
-
|
|
114362
|
-
|
|
114363
|
-
};
|
|
114846
|
+
for (const vk of vks)
|
|
114847
|
+
void api.keyByVk(vk, true);
|
|
114848
|
+
for (const vk of [...vks].reverse())
|
|
114849
|
+
void api.keyByVk(vk, false);
|
|
114850
|
+
}
|
|
114851
|
+
};
|
|
114852
|
+
function fracOf(f) {
|
|
114853
|
+
return [f.fx, f.fy];
|
|
114364
114854
|
}
|
|
114365
114855
|
|
|
114366
|
-
// ../../packages/host-core/dist/
|
|
114367
|
-
|
|
114368
|
-
|
|
114369
|
-
|
|
114370
|
-
|
|
114371
|
-
|
|
114372
|
-
|
|
114373
|
-
|
|
114374
|
-
|
|
114375
|
-
|
|
114376
|
-
|
|
114377
|
-
|
|
114378
|
-
|
|
114379
|
-
|
|
114380
|
-
|
|
114381
|
-
|
|
114382
|
-
this.
|
|
114383
|
-
|
|
114384
|
-
|
|
114856
|
+
// ../../packages/host-core/dist/system-events.js
|
|
114857
|
+
import { execFile as execFile8 } from "node:child_process";
|
|
114858
|
+
import { networkInterfaces, platform as platform2 } from "node:os";
|
|
114859
|
+
|
|
114860
|
+
// ../../packages/host-core/dist/participants.js
|
|
114861
|
+
var DAEMON_PARTICIPANT = "daemon";
|
|
114862
|
+
var ParticipantRegistry = class {
|
|
114863
|
+
byId = /* @__PURE__ */ new Map();
|
|
114864
|
+
/** Declare a participant. Idempotent — re-joining an existing id updates its label and keeps its
|
|
114865
|
+
* state, which is what a reconnect is. */
|
|
114866
|
+
join(id, label, now2) {
|
|
114867
|
+
const existing = this.byId.get(id);
|
|
114868
|
+
if (existing) {
|
|
114869
|
+
existing.label = label;
|
|
114870
|
+
return null;
|
|
114871
|
+
}
|
|
114872
|
+
this.byId.set(id, { id, label, active: false, focused: false, joinedAt: now2 });
|
|
114873
|
+
return { kind: "joined", id };
|
|
114874
|
+
}
|
|
114875
|
+
/** It went away — closed, crashed, or its socket dropped. Anything it claimed goes with it: a
|
|
114876
|
+
* process that died is not frontmost, and leaving that behind would make the machine look
|
|
114877
|
+
* permanently attended. */
|
|
114878
|
+
leave(id) {
|
|
114879
|
+
return this.byId.delete(id) ? { kind: "left", id } : null;
|
|
114880
|
+
}
|
|
114881
|
+
/** Frontmost, as reported by the participant itself. */
|
|
114882
|
+
setActive(id, active2) {
|
|
114883
|
+
const p = this.byId.get(id);
|
|
114884
|
+
if (!p || p.active === active2)
|
|
114885
|
+
return null;
|
|
114886
|
+
p.active = active2;
|
|
114887
|
+
return { kind: active2 ? "activated" : "deactivated", id };
|
|
114888
|
+
}
|
|
114889
|
+
setFocused(id, focused) {
|
|
114890
|
+
const p = this.byId.get(id);
|
|
114891
|
+
if (!p || p.focused === focused)
|
|
114892
|
+
return null;
|
|
114893
|
+
p.focused = focused;
|
|
114894
|
+
return { kind: focused ? "window-focus" : "window-blur", id };
|
|
114385
114895
|
}
|
|
114386
114896
|
/**
|
|
114387
|
-
*
|
|
114897
|
+
* Is anybody at this machine?
|
|
114388
114898
|
*
|
|
114389
|
-
*
|
|
114390
|
-
*
|
|
114391
|
-
*
|
|
114392
|
-
* capability flag that doesn't track reality is worse than no flag: the viewer renders
|
|
114393
|
-
* a control that quietly does nothing.
|
|
114899
|
+
* The union, deliberately. With a single boolean, two participants taking and losing focus in
|
|
114900
|
+
* sequence leave the answer set by whichever spoke last — so the desktop app blurring would
|
|
114901
|
+
* report an empty machine while somebody sits in front of the Remote Desktop window.
|
|
114394
114902
|
*/
|
|
114395
|
-
|
|
114396
|
-
|
|
114397
|
-
|
|
114398
|
-
|
|
114399
|
-
|
|
114400
|
-
clipboard: true,
|
|
114401
|
-
// No drag/drop or upload surface exists inside an rdesk session today. It was
|
|
114402
|
-
// advertised as if it did; nothing in either viewer ever read it.
|
|
114403
|
-
fileTransfer: false,
|
|
114404
|
-
multiMonitor: true,
|
|
114405
|
-
windowCapture: true,
|
|
114406
|
-
// share a single window/app (privacy)
|
|
114407
|
-
// Voice now rides this session's own peer connection (setAv).
|
|
114408
|
-
bidiAudioVideo: true,
|
|
114409
|
-
// Windows/Linux capture desktop audio natively; macOS needs a loopback we don't
|
|
114410
|
-
// ship, so claiming it there just produces a toggle that yields silence.
|
|
114411
|
-
systemAudio: process.platform !== "darwin"
|
|
114412
|
-
};
|
|
114413
|
-
}
|
|
114414
|
-
listMonitors() {
|
|
114415
|
-
return listPublisherMonitors();
|
|
114416
|
-
}
|
|
114417
|
-
listSources() {
|
|
114418
|
-
return listPublisherSources();
|
|
114903
|
+
anyonePresent() {
|
|
114904
|
+
for (const p of this.byId.values())
|
|
114905
|
+
if (p.active || p.focused)
|
|
114906
|
+
return true;
|
|
114907
|
+
return false;
|
|
114419
114908
|
}
|
|
114420
|
-
|
|
114421
|
-
|
|
114909
|
+
/** Who is accounted for. Lets a caller tell "nobody is here" from "nobody has reported". */
|
|
114910
|
+
participants() {
|
|
114911
|
+
return [...this.byId.values()].map((p) => ({ ...p }));
|
|
114422
114912
|
}
|
|
114423
|
-
|
|
114424
|
-
this.
|
|
114425
|
-
const origin = sourceId.startsWith("screen:") ? this.originForActiveScreen() : { x: 0, y: 0 };
|
|
114426
|
-
this.injector?.setOrigin(origin);
|
|
114427
|
-
const iceServers = await this.deps.getIceServers().catch(() => []);
|
|
114428
|
-
await this.pub?.setSource(sourceId, this.startOpts(iceServers));
|
|
114913
|
+
has(id) {
|
|
114914
|
+
return this.byId.has(id);
|
|
114429
114915
|
}
|
|
114430
|
-
|
|
114431
|
-
|
|
114432
|
-
return b ? { x: b.x, y: b.y } : { x: 0, y: 0 };
|
|
114433
|
-
}
|
|
114434
|
-
async start(opts) {
|
|
114435
|
-
const monitors = await listPublisherMonitors();
|
|
114436
|
-
this.monitorId = opts.monitorId ?? monitors.find((m) => m.primary)?.id ?? monitors[0]?.id ?? "";
|
|
114437
|
-
this.quality = opts.quality ?? "auto";
|
|
114438
|
-
this.sourceId = await sourceIdForMonitor(this.monitorId).catch(() => "");
|
|
114439
|
-
const origin = displayBoundsById(this.monitorId) ?? { x: 0, y: 0, width: 0, height: 0 };
|
|
114440
|
-
let runner2;
|
|
114441
|
-
if (this.deps.makeRunner) {
|
|
114442
|
-
runner2 = this.deps.makeRunner();
|
|
114443
|
-
} else if (isWaylandSession()) {
|
|
114444
|
-
let waylandReason = "";
|
|
114445
|
-
const wp = await createWaylandInputProvider((r) => {
|
|
114446
|
-
waylandReason = r;
|
|
114447
|
-
});
|
|
114448
|
-
if (wp) {
|
|
114449
|
-
const m = monitors.find((x) => x.id === this.monitorId) ?? monitors[0];
|
|
114450
|
-
runner2 = waylandInputRunner(wp, { w: m?.width ?? origin.width, h: m?.height ?? origin.height });
|
|
114451
|
-
this.ctx.log("rdesk: Wayland input via XDG RemoteDesktop portal.");
|
|
114452
|
-
} else {
|
|
114453
|
-
this.ctx.log(`rdesk: Wayland \u2014 keyboard and mouse unavailable, streaming view-only. Reason: ${waylandReason || "unknown"}`);
|
|
114454
|
-
runner2 = createDaemonOsControlRunner();
|
|
114455
|
-
}
|
|
114456
|
-
} else {
|
|
114457
|
-
runner2 = createDaemonOsControlRunner();
|
|
114458
|
-
}
|
|
114459
|
-
this.injector = new RdeskInputInjector(runner2, { x: origin.x, y: origin.y }, this.ctx.accessLevel);
|
|
114460
|
-
const iceServers = await this.deps.getIceServers().catch(() => []);
|
|
114461
|
-
const pub = new PublisherWindow(this.ctx.sessionId, {
|
|
114462
|
-
onOffer: (sdp) => this.ctx.sendSignal({ type: "signal:offer", sdp }),
|
|
114463
|
-
onIce: (candidate) => this.ctx.sendSignal({ type: "signal:ice", candidate }),
|
|
114464
|
-
onInput: (ev) => void this.injector?.apply(ev),
|
|
114465
|
-
// Clipboard sync (Phase 6): write the viewer's clipboard onto the host so ⌘V pastes
|
|
114466
|
-
// into the remote machine. Gated by control access (view-only never writes).
|
|
114467
|
-
onClipboard: (text13) => {
|
|
114468
|
-
if (this.access === "control") {
|
|
114469
|
-
const el2 = electronModule();
|
|
114470
|
-
try {
|
|
114471
|
-
el2?.clipboard?.writeText(text13);
|
|
114472
|
-
} catch {
|
|
114473
|
-
}
|
|
114474
|
-
}
|
|
114475
|
-
},
|
|
114476
|
-
onReady: () => this.ctx.log("publisher connected"),
|
|
114477
|
-
onError: (err2) => {
|
|
114478
|
-
this.ctx.log(`publisher error: ${err2}`);
|
|
114479
|
-
this.ctx.sendControl({ type: "rdesk:closed", reason: "network" });
|
|
114480
|
-
},
|
|
114481
|
-
// Media is direct-first; log which path actually won so a regression to relay
|
|
114482
|
-
// (which costs real bandwidth) is visible instead of silent. Types only, no IPs.
|
|
114483
|
-
onCapture: (ok, message) => {
|
|
114484
|
-
this.ctx.log(`capture ${ok ? "ok" : `LOST \u2014 ${message}`}`);
|
|
114485
|
-
this.ctx.onCapture?.(ok, message);
|
|
114486
|
-
},
|
|
114487
|
-
onPath: (path6, d) => {
|
|
114488
|
-
this.ctx.log(`media path=${path6} (local=${d.localType ?? "?"} remote=${d.remoteType ?? "?"})`);
|
|
114489
|
-
this.ctx.onPath?.(path6, d);
|
|
114490
|
-
}
|
|
114491
|
-
});
|
|
114492
|
-
this.pub = pub;
|
|
114493
|
-
this.stopClipWatch = watchHostClipboardFiles((files) => {
|
|
114494
|
-
if (this.access !== "control")
|
|
114495
|
-
return;
|
|
114496
|
-
this.pub?.sendData({ t: "clipboard-files", files });
|
|
114497
|
-
});
|
|
114498
|
-
await pub.start({
|
|
114499
|
-
iceServers,
|
|
114500
|
-
monitorId: this.monitorId,
|
|
114501
|
-
quality: this.quality,
|
|
114502
|
-
systemAudio: this.systemAudio
|
|
114503
|
-
});
|
|
114504
|
-
}
|
|
114505
|
-
onSignal(msg) {
|
|
114506
|
-
if (!this.pub)
|
|
114507
|
-
return;
|
|
114508
|
-
if (msg.type === "signal:answer")
|
|
114509
|
-
this.pub.answer(msg.sdp);
|
|
114510
|
-
else if (msg.type === "signal:ice")
|
|
114511
|
-
this.pub.addIce(msg.candidate);
|
|
114512
|
-
}
|
|
114513
|
-
applyInput(ev) {
|
|
114514
|
-
void this.injector?.apply(ev);
|
|
114515
|
-
}
|
|
114516
|
-
setAccessLevel(level) {
|
|
114517
|
-
this.access = level;
|
|
114518
|
-
this.injector?.setAccess(level);
|
|
114519
|
-
}
|
|
114520
|
-
async setMonitor(monitorId) {
|
|
114521
|
-
this.monitorId = monitorId;
|
|
114522
|
-
this.sourceId = await sourceIdForMonitor(monitorId).catch(() => this.sourceId);
|
|
114523
|
-
const origin = displayBoundsById(monitorId);
|
|
114524
|
-
if (origin && this.injector)
|
|
114525
|
-
this.injector.setOrigin({ x: origin.x, y: origin.y });
|
|
114526
|
-
const iceServers = await this.deps.getIceServers().catch(() => []);
|
|
114527
|
-
await this.pub?.setMonitor(monitorId, this.startOpts(iceServers));
|
|
114528
|
-
}
|
|
114529
|
-
setAv(on) {
|
|
114530
|
-
this.pub?.setAv(on);
|
|
114531
|
-
}
|
|
114532
|
-
setQuality(preset) {
|
|
114533
|
-
this.quality = preset;
|
|
114534
|
-
this.pub?.setQuality(preset);
|
|
114535
|
-
}
|
|
114536
|
-
setMute(m) {
|
|
114537
|
-
if (typeof m.systemAudio === "boolean" && m.systemAudio !== this.systemAudio && this.sourceId) {
|
|
114538
|
-
this.systemAudio = m.systemAudio;
|
|
114539
|
-
void this.deps.getIceServers().catch(() => []).then((ice) => this.pub?.setSource(this.sourceId, this.startOpts(ice)));
|
|
114540
|
-
}
|
|
114541
|
-
}
|
|
114542
|
-
async stop() {
|
|
114543
|
-
this.stopClipWatch?.();
|
|
114544
|
-
this.stopClipWatch = null;
|
|
114545
|
-
await this.pub?.stop();
|
|
114546
|
-
this.pub = null;
|
|
114547
|
-
this.injector = null;
|
|
114548
|
-
}
|
|
114549
|
-
};
|
|
114550
|
-
function registerTesseractEngine(deps) {
|
|
114551
|
-
registerRdeskEngine("tesseract", (ctx2) => new TesseractEngine(ctx2, deps));
|
|
114552
|
-
}
|
|
114553
|
-
|
|
114554
|
-
// ../../packages/host-core/dist/local-server.js
|
|
114555
|
-
import { timingSafeEqual as timingSafeEqual5 } from "node:crypto";
|
|
114556
|
-
|
|
114557
|
-
// ../../packages/host-core/dist/in-process-link.js
|
|
114558
|
-
var OPEN = 1;
|
|
114559
|
-
var CLOSED = 3;
|
|
114560
|
-
var InProcSocket = class {
|
|
114561
|
-
OPEN = OPEN;
|
|
114562
|
-
CLOSED = CLOSED;
|
|
114563
|
-
readyState = OPEN;
|
|
114564
|
-
peer = null;
|
|
114565
|
-
listeners = /* @__PURE__ */ new Map();
|
|
114566
|
-
on(event, cb) {
|
|
114567
|
-
let s = this.listeners.get(event);
|
|
114568
|
-
if (!s) {
|
|
114569
|
-
s = /* @__PURE__ */ new Set();
|
|
114570
|
-
this.listeners.set(event, s);
|
|
114571
|
-
}
|
|
114572
|
-
s.add(cb);
|
|
114573
|
-
return this;
|
|
114574
|
-
}
|
|
114575
|
-
once(event, cb) {
|
|
114576
|
-
const wrap = (...a) => {
|
|
114577
|
-
this.off(event, wrap);
|
|
114578
|
-
cb(...a);
|
|
114579
|
-
};
|
|
114580
|
-
return this.on(event, wrap);
|
|
114581
|
-
}
|
|
114582
|
-
off(event, cb) {
|
|
114583
|
-
this.listeners.get(event)?.delete(cb);
|
|
114584
|
-
return this;
|
|
114585
|
-
}
|
|
114586
|
-
removeListener(event, cb) {
|
|
114587
|
-
return this.off(event, cb);
|
|
114588
|
-
}
|
|
114589
|
-
fire(event, ...args) {
|
|
114590
|
-
const s = this.listeners.get(event);
|
|
114591
|
-
if (!s)
|
|
114592
|
-
return;
|
|
114593
|
-
for (const cb of [...s]) {
|
|
114594
|
-
try {
|
|
114595
|
-
cb(...args);
|
|
114596
|
-
} catch {
|
|
114597
|
-
}
|
|
114598
|
-
}
|
|
114599
|
-
}
|
|
114600
|
-
/** Deliver `data` to the PEER's 'message' listeners on the next macrotask. */
|
|
114601
|
-
send(data) {
|
|
114602
|
-
const peer = this.peer;
|
|
114603
|
-
if (!peer || peer.readyState !== OPEN)
|
|
114604
|
-
return;
|
|
114605
|
-
setImmediate(() => {
|
|
114606
|
-
if (peer.readyState === OPEN)
|
|
114607
|
-
peer.fire("message", data);
|
|
114608
|
-
});
|
|
114609
|
-
}
|
|
114610
|
-
close(_code, _reason) {
|
|
114611
|
-
if (this.readyState === CLOSED)
|
|
114612
|
-
return;
|
|
114613
|
-
this.readyState = CLOSED;
|
|
114614
|
-
const peer = this.peer;
|
|
114615
|
-
setImmediate(() => this.fire("close"));
|
|
114616
|
-
if (peer && peer.readyState !== CLOSED) {
|
|
114617
|
-
peer.readyState = CLOSED;
|
|
114618
|
-
setImmediate(() => peer.fire("close"));
|
|
114619
|
-
}
|
|
114620
|
-
}
|
|
114621
|
-
/** @internal wire this end to its peer. */
|
|
114622
|
-
_link(peer) {
|
|
114623
|
-
this.peer = peer;
|
|
114624
|
-
}
|
|
114625
|
-
/** @internal fire 'open' on the next macrotask (client end only; the server never awaits it). */
|
|
114626
|
-
_fireOpen() {
|
|
114627
|
-
setImmediate(() => this.fire("open"));
|
|
114628
|
-
}
|
|
114629
|
-
};
|
|
114630
|
-
function makeInProcPair() {
|
|
114631
|
-
const serverEnd = new InProcSocket();
|
|
114632
|
-
const clientEnd = new InProcSocket();
|
|
114633
|
-
serverEnd._link(clientEnd);
|
|
114634
|
-
clientEnd._link(serverEnd);
|
|
114635
|
-
return { serverEnd, clientEnd };
|
|
114636
|
-
}
|
|
114637
|
-
var connector = null;
|
|
114638
|
-
function setInProcConnector(fn) {
|
|
114639
|
-
connector = fn;
|
|
114640
|
-
}
|
|
114641
|
-
|
|
114642
|
-
// ../../packages/host-core/dist/rdesk/server-prefs.js
|
|
114643
|
-
init_credentials();
|
|
114644
|
-
import { readFileSync as readFileSync44, writeFileSync as writeFileSync29 } from "node:fs";
|
|
114645
|
-
import { join as join72 } from "node:path";
|
|
114646
|
-
function rdeskServerPrefsPath() {
|
|
114647
|
-
return join72(daemonDir2(), "rdesk-server.json");
|
|
114648
|
-
}
|
|
114649
|
-
var DEFAULTS2 = {
|
|
114650
|
-
enabled: true,
|
|
114651
|
-
defaultAccess: "control",
|
|
114652
|
-
quality: "auto",
|
|
114653
|
-
autostart: true,
|
|
114654
|
-
hideTray: false,
|
|
114655
|
-
noticeShownAt: 0
|
|
114656
|
-
};
|
|
114657
|
-
function readRdeskServerPrefs() {
|
|
114658
|
-
try {
|
|
114659
|
-
const p = JSON.parse(readFileSync44(rdeskServerPrefsPath(), "utf8"));
|
|
114660
|
-
return {
|
|
114661
|
-
// `!== false` (not `?? true`): only an explicit opt-out disables sharing, so a
|
|
114662
|
-
// partially-written or older prefs file never silently turns the machine off.
|
|
114663
|
-
enabled: p.enabled !== false,
|
|
114664
|
-
defaultAccess: p.defaultAccess === "view" ? "view" : "control",
|
|
114665
|
-
quality: p.quality ?? DEFAULTS2.quality,
|
|
114666
|
-
autostart: p.autostart ?? DEFAULTS2.autostart,
|
|
114667
|
-
hideTray: p.hideTray ?? DEFAULTS2.hideTray,
|
|
114668
|
-
noticeShownAt: typeof p.noticeShownAt === "number" ? p.noticeShownAt : 0
|
|
114669
|
-
};
|
|
114670
|
-
} catch {
|
|
114671
|
-
return { ...DEFAULTS2 };
|
|
114672
|
-
}
|
|
114673
|
-
}
|
|
114674
|
-
function writeRdeskServerPrefs(patch) {
|
|
114675
|
-
try {
|
|
114676
|
-
const next = { ...readRdeskServerPrefs(), ...patch };
|
|
114677
|
-
writeFileSync29(rdeskServerPrefsPath(), JSON.stringify(next), { mode: 384 });
|
|
114678
|
-
} catch {
|
|
114679
|
-
}
|
|
114680
|
-
}
|
|
114681
|
-
function rdeskSharingEnabled() {
|
|
114682
|
-
if (process.env.CCB_RDESK_ENABLED === "0")
|
|
114683
|
-
return false;
|
|
114684
|
-
if (process.env.CCB_RDESK_ENABLED === "1")
|
|
114685
|
-
return true;
|
|
114686
|
-
return readRdeskServerPrefs().enabled;
|
|
114687
|
-
}
|
|
114688
|
-
|
|
114689
|
-
// ../../packages/host-core/dist/rdesk/password.js
|
|
114690
|
-
init_credentials();
|
|
114691
|
-
import { scryptSync, randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual4, randomInt } from "node:crypto";
|
|
114692
|
-
import { readFileSync as readFileSync45, writeFileSync as writeFileSync30, existsSync as existsSync32, unlinkSync as unlinkSync4 } from "node:fs";
|
|
114693
|
-
import { join as join73 } from "node:path";
|
|
114694
|
-
function pwFile() {
|
|
114695
|
-
return join73(daemonDir2(), "rdesk-password.json");
|
|
114696
|
-
}
|
|
114697
|
-
function pwPlainFile() {
|
|
114698
|
-
return join73(daemonDir2(), "rdesk-password.enc");
|
|
114699
|
-
}
|
|
114700
|
-
var PW_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
|
|
114701
|
-
var PW_LENGTH = 16;
|
|
114702
|
-
function generatePassword() {
|
|
114703
|
-
let out = "";
|
|
114704
|
-
for (let i = 0; i < PW_LENGTH; i++)
|
|
114705
|
-
out += PW_ALPHABET[randomInt(PW_ALPHABET.length)];
|
|
114706
|
-
return (out.match(/.{1,4}/g) ?? [out]).join("-");
|
|
114707
|
-
}
|
|
114708
|
-
function writePlain(password) {
|
|
114709
|
-
try {
|
|
114710
|
-
const safe2 = electronModule()?.safeStorage;
|
|
114711
|
-
if (!safe2?.isEncryptionAvailable())
|
|
114712
|
-
return;
|
|
114713
|
-
writeFileSync30(pwPlainFile(), safe2.encryptString(password), { mode: 384 });
|
|
114714
|
-
} catch {
|
|
114715
|
-
}
|
|
114716
|
-
}
|
|
114717
|
-
function canRevealRdeskPassword() {
|
|
114718
|
-
try {
|
|
114719
|
-
const safe2 = electronModule()?.safeStorage;
|
|
114720
|
-
return !!safe2?.isEncryptionAvailable() && existsSync32(pwPlainFile());
|
|
114721
|
-
} catch {
|
|
114722
|
-
return false;
|
|
114723
|
-
}
|
|
114724
|
-
}
|
|
114725
|
-
function readRdeskPasswordPlain() {
|
|
114726
|
-
try {
|
|
114727
|
-
const safe2 = electronModule()?.safeStorage;
|
|
114728
|
-
if (!safe2?.isEncryptionAvailable())
|
|
114729
|
-
return null;
|
|
114730
|
-
return safe2.decryptString(readFileSync45(pwPlainFile()));
|
|
114731
|
-
} catch {
|
|
114732
|
-
return null;
|
|
114733
|
-
}
|
|
114734
|
-
}
|
|
114735
|
-
function setRdeskPassword(password) {
|
|
114736
|
-
if (!password) {
|
|
114737
|
-
try {
|
|
114738
|
-
unlinkSync4(pwFile());
|
|
114739
|
-
} catch {
|
|
114740
|
-
}
|
|
114741
|
-
try {
|
|
114742
|
-
unlinkSync4(pwPlainFile());
|
|
114743
|
-
} catch {
|
|
114744
|
-
}
|
|
114745
|
-
return;
|
|
114746
|
-
}
|
|
114747
|
-
const salt = randomBytes5(16);
|
|
114748
|
-
const hash2 = scryptSync(password, salt, 32);
|
|
114749
|
-
writeFileSync30(pwFile(), JSON.stringify({ salt: salt.toString("hex"), hash: hash2.toString("hex") }), { mode: 384 });
|
|
114750
|
-
writePlain(password);
|
|
114751
|
-
}
|
|
114752
|
-
function regenerateRdeskPassword() {
|
|
114753
|
-
const pw = generatePassword();
|
|
114754
|
-
setRdeskPassword(pw);
|
|
114755
|
-
return pw;
|
|
114756
|
-
}
|
|
114757
|
-
function ensureRdeskPassword() {
|
|
114758
|
-
if (hasRdeskPassword())
|
|
114759
|
-
return null;
|
|
114760
|
-
return regenerateRdeskPassword();
|
|
114761
|
-
}
|
|
114762
|
-
function hasRdeskPassword() {
|
|
114763
|
-
return !!process.env.CCB_RDESK_PASSWORD || existsSync32(pwFile());
|
|
114764
|
-
}
|
|
114765
|
-
function verifyRdeskPassword(password) {
|
|
114766
|
-
if (!password)
|
|
114767
|
-
return false;
|
|
114768
|
-
const override3 = process.env.CCB_RDESK_PASSWORD;
|
|
114769
|
-
if (override3)
|
|
114770
|
-
return password.length === override3.length && timingSafeEqual4(Buffer.from(password), Buffer.from(override3));
|
|
114771
|
-
try {
|
|
114772
|
-
const { salt, hash: hash2 } = JSON.parse(readFileSync45(pwFile(), "utf8"));
|
|
114773
|
-
const got = scryptSync(password, Buffer.from(salt, "hex"), 32);
|
|
114774
|
-
const want = Buffer.from(hash2, "hex");
|
|
114775
|
-
return got.length === want.length && timingSafeEqual4(got, want);
|
|
114776
|
-
} catch {
|
|
114777
|
-
return false;
|
|
114778
|
-
}
|
|
114779
|
-
}
|
|
114780
|
-
|
|
114781
|
-
// ../../packages/host-core/dist/system-events.js
|
|
114782
|
-
import { execFile as execFile8 } from "node:child_process";
|
|
114783
|
-
import { networkInterfaces, platform as platform2 } from "node:os";
|
|
114784
|
-
|
|
114785
|
-
// ../../packages/host-core/dist/participants.js
|
|
114786
|
-
var DAEMON_PARTICIPANT = "daemon";
|
|
114787
|
-
var ParticipantRegistry = class {
|
|
114788
|
-
byId = /* @__PURE__ */ new Map();
|
|
114789
|
-
/** Declare a participant. Idempotent — re-joining an existing id updates its label and keeps its
|
|
114790
|
-
* state, which is what a reconnect is. */
|
|
114791
|
-
join(id, label, now2) {
|
|
114792
|
-
const existing = this.byId.get(id);
|
|
114793
|
-
if (existing) {
|
|
114794
|
-
existing.label = label;
|
|
114795
|
-
return null;
|
|
114796
|
-
}
|
|
114797
|
-
this.byId.set(id, { id, label, active: false, focused: false, joinedAt: now2 });
|
|
114798
|
-
return { kind: "joined", id };
|
|
114799
|
-
}
|
|
114800
|
-
/** It went away — closed, crashed, or its socket dropped. Anything it claimed goes with it: a
|
|
114801
|
-
* process that died is not frontmost, and leaving that behind would make the machine look
|
|
114802
|
-
* permanently attended. */
|
|
114803
|
-
leave(id) {
|
|
114804
|
-
return this.byId.delete(id) ? { kind: "left", id } : null;
|
|
114805
|
-
}
|
|
114806
|
-
/** Frontmost, as reported by the participant itself. */
|
|
114807
|
-
setActive(id, active2) {
|
|
114808
|
-
const p = this.byId.get(id);
|
|
114809
|
-
if (!p || p.active === active2)
|
|
114810
|
-
return null;
|
|
114811
|
-
p.active = active2;
|
|
114812
|
-
return { kind: active2 ? "activated" : "deactivated", id };
|
|
114813
|
-
}
|
|
114814
|
-
setFocused(id, focused) {
|
|
114815
|
-
const p = this.byId.get(id);
|
|
114816
|
-
if (!p || p.focused === focused)
|
|
114817
|
-
return null;
|
|
114818
|
-
p.focused = focused;
|
|
114819
|
-
return { kind: focused ? "window-focus" : "window-blur", id };
|
|
114820
|
-
}
|
|
114821
|
-
/**
|
|
114822
|
-
* Is anybody at this machine?
|
|
114823
|
-
*
|
|
114824
|
-
* The union, deliberately. With a single boolean, two participants taking and losing focus in
|
|
114825
|
-
* sequence leave the answer set by whichever spoke last — so the desktop app blurring would
|
|
114826
|
-
* report an empty machine while somebody sits in front of the Remote Desktop window.
|
|
114827
|
-
*/
|
|
114828
|
-
anyonePresent() {
|
|
114829
|
-
for (const p of this.byId.values())
|
|
114830
|
-
if (p.active || p.focused)
|
|
114831
|
-
return true;
|
|
114832
|
-
return false;
|
|
114833
|
-
}
|
|
114834
|
-
/** Who is accounted for. Lets a caller tell "nobody is here" from "nobody has reported". */
|
|
114835
|
-
participants() {
|
|
114836
|
-
return [...this.byId.values()].map((p) => ({ ...p }));
|
|
114837
|
-
}
|
|
114838
|
-
has(id) {
|
|
114839
|
-
return this.byId.has(id);
|
|
114840
|
-
}
|
|
114841
|
-
clear() {
|
|
114842
|
-
this.byId.clear();
|
|
114916
|
+
clear() {
|
|
114917
|
+
this.byId.clear();
|
|
114843
114918
|
}
|
|
114844
114919
|
};
|
|
114845
114920
|
|
|
@@ -114981,269 +115056,930 @@ async function probeOnce(url2) {
|
|
|
114981
115056
|
}
|
|
114982
115057
|
};
|
|
114983
115058
|
try {
|
|
114984
|
-
const req = en.request({ method: "HEAD", url: url2 });
|
|
114985
|
-
const t = setTimeout(() => {
|
|
114986
|
-
try {
|
|
114987
|
-
req.abort();
|
|
114988
|
-
} catch {
|
|
114989
|
-
}
|
|
114990
|
-
done(false);
|
|
114991
|
-
}, PROBE_TIMEOUT_MS2);
|
|
114992
|
-
t.unref?.();
|
|
114993
|
-
req.on("response", () => {
|
|
114994
|
-
clearTimeout(t);
|
|
114995
|
-
done(true);
|
|
114996
|
-
});
|
|
114997
|
-
req.on("error", () => {
|
|
114998
|
-
clearTimeout(t);
|
|
114999
|
-
done(false);
|
|
115000
|
-
});
|
|
115001
|
-
req.end();
|
|
115059
|
+
const req = en.request({ method: "HEAD", url: url2 });
|
|
115060
|
+
const t = setTimeout(() => {
|
|
115061
|
+
try {
|
|
115062
|
+
req.abort();
|
|
115063
|
+
} catch {
|
|
115064
|
+
}
|
|
115065
|
+
done(false);
|
|
115066
|
+
}, PROBE_TIMEOUT_MS2);
|
|
115067
|
+
t.unref?.();
|
|
115068
|
+
req.on("response", () => {
|
|
115069
|
+
clearTimeout(t);
|
|
115070
|
+
done(true);
|
|
115071
|
+
});
|
|
115072
|
+
req.on("error", () => {
|
|
115073
|
+
clearTimeout(t);
|
|
115074
|
+
done(false);
|
|
115075
|
+
});
|
|
115076
|
+
req.end();
|
|
115077
|
+
} catch {
|
|
115078
|
+
done(false);
|
|
115079
|
+
}
|
|
115080
|
+
});
|
|
115081
|
+
}
|
|
115082
|
+
try {
|
|
115083
|
+
await fetch(url2, { method: "HEAD", signal: AbortSignal.timeout(PROBE_TIMEOUT_MS2) });
|
|
115084
|
+
return true;
|
|
115085
|
+
} catch {
|
|
115086
|
+
return false;
|
|
115087
|
+
}
|
|
115088
|
+
}
|
|
115089
|
+
async function probeNetwork() {
|
|
115090
|
+
const started2 = Date.now();
|
|
115091
|
+
const results = await Promise.all(PROBE_URLS.map((u) => probeOnce(u)));
|
|
115092
|
+
const reachable = results.some(Boolean);
|
|
115093
|
+
emitSystemEvent({ type: "net:probe", reachable, ms: Date.now() - started2 });
|
|
115094
|
+
if (reachable !== state2.online)
|
|
115095
|
+
emitSystemEvent({ type: reachable ? "net:online" : "net:offline", evidence: "probed" });
|
|
115096
|
+
return reachable;
|
|
115097
|
+
}
|
|
115098
|
+
function startNetworkProbing(requester = "daemon") {
|
|
115099
|
+
wanted.add(requester);
|
|
115100
|
+
if (probing)
|
|
115101
|
+
return;
|
|
115102
|
+
probing = true;
|
|
115103
|
+
probeAttempt = 0;
|
|
115104
|
+
scheduleProbe(0);
|
|
115105
|
+
}
|
|
115106
|
+
function stopNetworkProbing(requester) {
|
|
115107
|
+
if (requester !== void 0) {
|
|
115108
|
+
wanted.delete(requester);
|
|
115109
|
+
if (wanted.size > 0)
|
|
115110
|
+
return;
|
|
115111
|
+
} else {
|
|
115112
|
+
wanted.clear();
|
|
115113
|
+
}
|
|
115114
|
+
probing = false;
|
|
115115
|
+
if (probeTimer) {
|
|
115116
|
+
clearTimeout(probeTimer);
|
|
115117
|
+
probeTimer = null;
|
|
115118
|
+
}
|
|
115119
|
+
probeAttempt = 0;
|
|
115120
|
+
}
|
|
115121
|
+
var probeTimer = null;
|
|
115122
|
+
var probeAttempt = 0;
|
|
115123
|
+
var probing = false;
|
|
115124
|
+
var wanted = /* @__PURE__ */ new Set();
|
|
115125
|
+
function scheduleProbe(delayMs) {
|
|
115126
|
+
if (probeTimer)
|
|
115127
|
+
clearTimeout(probeTimer);
|
|
115128
|
+
probeTimer = setTimeout(() => {
|
|
115129
|
+
void runProbeCycle();
|
|
115130
|
+
}, delayMs);
|
|
115131
|
+
probeTimer.unref?.();
|
|
115132
|
+
}
|
|
115133
|
+
async function runProbeCycle() {
|
|
115134
|
+
probeTimer = null;
|
|
115135
|
+
if (!probing)
|
|
115136
|
+
return;
|
|
115137
|
+
const ok = await probeNetwork();
|
|
115138
|
+
if (!probing)
|
|
115139
|
+
return;
|
|
115140
|
+
if (ok) {
|
|
115141
|
+
probing = false;
|
|
115142
|
+
probeAttempt = 0;
|
|
115143
|
+
wanted.clear();
|
|
115144
|
+
return;
|
|
115145
|
+
}
|
|
115146
|
+
scheduleProbe(probeDelayMs(probeAttempt++));
|
|
115147
|
+
}
|
|
115148
|
+
function addressFingerprint() {
|
|
115149
|
+
const out = [];
|
|
115150
|
+
for (const [name, addrs] of Object.entries(networkInterfaces())) {
|
|
115151
|
+
for (const a of addrs ?? [])
|
|
115152
|
+
if (!a.internal)
|
|
115153
|
+
out.push(`${name}:${a.address}`);
|
|
115154
|
+
}
|
|
115155
|
+
return out.sort();
|
|
115156
|
+
}
|
|
115157
|
+
function readLidClosed() {
|
|
115158
|
+
if (platform2() !== "darwin")
|
|
115159
|
+
return Promise.resolve(null);
|
|
115160
|
+
return new Promise((resolve16) => {
|
|
115161
|
+
execFile8("ioreg", ["-r", "-k", "AppleClamshellState", "-d", "4"], { timeout: 4e3 }, (err2, stdout) => {
|
|
115162
|
+
resolve16(err2 ? null : parseClamshell(stdout ?? ""));
|
|
115163
|
+
});
|
|
115164
|
+
});
|
|
115165
|
+
}
|
|
115166
|
+
function parseClamshell(stdout) {
|
|
115167
|
+
const m = /"AppleClamshellState"\s*=\s*(Yes|No)/.exec(stdout);
|
|
115168
|
+
return m ? m[1] === "Yes" : null;
|
|
115169
|
+
}
|
|
115170
|
+
var TICK_MS = 1e4;
|
|
115171
|
+
var CLOCK_JUMP_MS = TICK_MS * 3;
|
|
115172
|
+
var LID_EVERY_TICKS = 3;
|
|
115173
|
+
var stops = [];
|
|
115174
|
+
var started = false;
|
|
115175
|
+
function startSystemEvents(opts = {}) {
|
|
115176
|
+
if (started)
|
|
115177
|
+
return;
|
|
115178
|
+
started = true;
|
|
115179
|
+
const log10 = opts.log ?? (() => {
|
|
115180
|
+
});
|
|
115181
|
+
const el2 = electronModule();
|
|
115182
|
+
emitSystemEvent({ type: "process:started", pid: process.pid, version: opts.version ?? "dev", headless: !el2 });
|
|
115183
|
+
const pm = el2?.powerMonitor;
|
|
115184
|
+
if (pm?.on) {
|
|
115185
|
+
const wire = (name, make) => {
|
|
115186
|
+
const h = () => {
|
|
115187
|
+
emitSystemEvent(make());
|
|
115188
|
+
};
|
|
115189
|
+
try {
|
|
115190
|
+
pm.on(name, h);
|
|
115191
|
+
stops.push(() => {
|
|
115192
|
+
try {
|
|
115193
|
+
pm.removeListener(name, h);
|
|
115194
|
+
} catch {
|
|
115195
|
+
}
|
|
115196
|
+
});
|
|
115197
|
+
} catch {
|
|
115198
|
+
}
|
|
115199
|
+
};
|
|
115200
|
+
wire("suspend", () => ({ type: "power:suspend" }));
|
|
115201
|
+
wire("resume", () => ({ type: "power:resume" }));
|
|
115202
|
+
wire("on-ac", () => ({ type: "power:on-ac" }));
|
|
115203
|
+
wire("on-battery", () => ({ type: "power:on-battery" }));
|
|
115204
|
+
wire("lock-screen", () => ({ type: "power:lock-screen" }));
|
|
115205
|
+
wire("unlock-screen", () => ({ type: "power:unlock-screen" }));
|
|
115206
|
+
wire("user-did-become-active", () => ({ type: "power:user-active" }));
|
|
115207
|
+
wire("user-did-resign-active", () => ({ type: "power:user-idle", idleSeconds: pm.getSystemIdleTime?.() ?? 0 }));
|
|
115208
|
+
wire("shutdown", () => ({ type: "power:os-shutdown" }));
|
|
115209
|
+
wire("thermal-state-change", () => ({ type: "power:thermal", state: pm.getCurrentThermalState?.() ?? "unknown" }));
|
|
115210
|
+
if (typeof pm.onBatteryPower === "boolean")
|
|
115211
|
+
emitSystemEvent({ type: pm.onBatteryPower ? "power:on-battery" : "power:on-ac" });
|
|
115212
|
+
}
|
|
115213
|
+
if (el2?.screen?.on) {
|
|
115214
|
+
const count = () => {
|
|
115215
|
+
try {
|
|
115216
|
+
return el2.screen.getAllDisplays().length;
|
|
115217
|
+
} catch {
|
|
115218
|
+
return 0;
|
|
115219
|
+
}
|
|
115220
|
+
};
|
|
115221
|
+
const wireScreen = (name, type) => {
|
|
115222
|
+
const h = () => {
|
|
115223
|
+
emitSystemEvent({ type, count: count() });
|
|
115224
|
+
};
|
|
115225
|
+
try {
|
|
115226
|
+
el2.screen.on(name, h);
|
|
115227
|
+
stops.push(() => {
|
|
115228
|
+
try {
|
|
115229
|
+
el2.screen.removeListener?.(name, h);
|
|
115230
|
+
} catch {
|
|
115231
|
+
}
|
|
115232
|
+
});
|
|
115233
|
+
} catch {
|
|
115234
|
+
}
|
|
115235
|
+
};
|
|
115236
|
+
wireScreen("display-added", "display:added");
|
|
115237
|
+
wireScreen("display-removed", "display:removed");
|
|
115238
|
+
wireScreen("display-metrics-changed", "display:changed");
|
|
115239
|
+
state2.displays = count();
|
|
115240
|
+
}
|
|
115241
|
+
if (el2?.app?.on) {
|
|
115242
|
+
const wireApp = (name, fire) => {
|
|
115243
|
+
try {
|
|
115244
|
+
el2.app.on(name, fire);
|
|
115245
|
+
stops.push(() => {
|
|
115246
|
+
try {
|
|
115247
|
+
el2.app.removeListener?.(name, fire);
|
|
115248
|
+
} catch {
|
|
115249
|
+
}
|
|
115250
|
+
});
|
|
115251
|
+
} catch {
|
|
115252
|
+
}
|
|
115253
|
+
};
|
|
115254
|
+
joinParticipant(DAEMON_PARTICIPANT, "Tesseract daemon");
|
|
115255
|
+
wireApp("activate", () => reportParticipantActive(DAEMON_PARTICIPANT, true));
|
|
115256
|
+
wireApp("did-become-active", () => reportParticipantActive(DAEMON_PARTICIPANT, true));
|
|
115257
|
+
wireApp("did-resign-active", () => reportParticipantActive(DAEMON_PARTICIPANT, false));
|
|
115258
|
+
wireApp("browser-window-focus", () => reportParticipantFocus(DAEMON_PARTICIPANT, true));
|
|
115259
|
+
wireApp("browser-window-blur", () => reportParticipantFocus(DAEMON_PARTICIPANT, false));
|
|
115260
|
+
}
|
|
115261
|
+
if (el2?.nativeTheme?.on) {
|
|
115262
|
+
const h = () => {
|
|
115263
|
+
emitSystemEvent({ type: "theme:changed", dark: !!el2.nativeTheme?.shouldUseDarkColors });
|
|
115264
|
+
};
|
|
115265
|
+
try {
|
|
115266
|
+
el2.nativeTheme.on("updated", h);
|
|
115267
|
+
stops.push(() => {
|
|
115268
|
+
try {
|
|
115269
|
+
el2.nativeTheme?.removeListener?.("updated", h);
|
|
115270
|
+
} catch {
|
|
115271
|
+
}
|
|
115272
|
+
});
|
|
115273
|
+
} catch {
|
|
115274
|
+
}
|
|
115275
|
+
}
|
|
115276
|
+
let lastTick = Date.now();
|
|
115277
|
+
let addresses = addressFingerprint();
|
|
115278
|
+
let ticks = 0;
|
|
115279
|
+
const tick = setInterval(() => {
|
|
115280
|
+
const now2 = Date.now();
|
|
115281
|
+
const drift = now2 - lastTick;
|
|
115282
|
+
lastTick = now2;
|
|
115283
|
+
if (drift > CLOCK_JUMP_MS)
|
|
115284
|
+
emitSystemEvent({ type: "power:clock-jump", missedMs: drift - TICK_MS });
|
|
115285
|
+
const next = addressFingerprint();
|
|
115286
|
+
if (next.join("|") !== addresses.join("|")) {
|
|
115287
|
+
addresses = next;
|
|
115288
|
+
emitSystemEvent({ type: "net:interfaces-changed", addresses: next });
|
|
115289
|
+
void probeNetwork().catch(() => {
|
|
115290
|
+
});
|
|
115291
|
+
}
|
|
115292
|
+
if (++ticks % LID_EVERY_TICKS === 0) {
|
|
115293
|
+
void readLidClosed().then((closed) => {
|
|
115294
|
+
if (closed === null || closed === state2.lidClosed)
|
|
115295
|
+
return;
|
|
115296
|
+
emitSystemEvent({ type: closed ? "lid:closed" : "lid:opened" });
|
|
115297
|
+
}).catch(() => {
|
|
115298
|
+
});
|
|
115299
|
+
}
|
|
115300
|
+
}, TICK_MS);
|
|
115301
|
+
tick.unref?.();
|
|
115302
|
+
stops.push(() => clearInterval(tick));
|
|
115303
|
+
try {
|
|
115304
|
+
const osOnline = el2?.net?.isOnline?.();
|
|
115305
|
+
if (typeof osOnline === "boolean")
|
|
115306
|
+
reportOsOnline(osOnline);
|
|
115307
|
+
} catch {
|
|
115308
|
+
}
|
|
115309
|
+
log10(`system events: watching (${el2 ? "electron" : "headless"})`);
|
|
115310
|
+
}
|
|
115311
|
+
function stopSystemEvents() {
|
|
115312
|
+
for (const s of stops) {
|
|
115313
|
+
try {
|
|
115314
|
+
s();
|
|
115315
|
+
} catch {
|
|
115316
|
+
}
|
|
115317
|
+
}
|
|
115318
|
+
stops = [];
|
|
115319
|
+
stopNetworkProbing();
|
|
115320
|
+
leaveParticipant(DAEMON_PARTICIPANT);
|
|
115321
|
+
started = false;
|
|
115322
|
+
}
|
|
115323
|
+
|
|
115324
|
+
// ../../packages/host-core/dist/rdesk/wayland-input.js
|
|
115325
|
+
function isWaylandSession() {
|
|
115326
|
+
return process.platform === "linux" && (process.env.XDG_SESSION_TYPE === "wayland" || !!process.env.WAYLAND_DISPLAY);
|
|
115327
|
+
}
|
|
115328
|
+
var BTN = { left: 272, right: 273, middle: 274 };
|
|
115329
|
+
async function createWaylandInputProvider(onUnavailable) {
|
|
115330
|
+
const fail4 = (reason) => {
|
|
115331
|
+
onUnavailable?.(reason);
|
|
115332
|
+
return null;
|
|
115333
|
+
};
|
|
115334
|
+
if (!isWaylandSession())
|
|
115335
|
+
return fail4("not a Wayland session");
|
|
115336
|
+
let dbus = null;
|
|
115337
|
+
try {
|
|
115338
|
+
dbus = await import("dbus-next");
|
|
115339
|
+
} catch (e) {
|
|
115340
|
+
return fail4(`dbus-next could not be loaded: ${e instanceof Error ? e.message : String(e)}`);
|
|
115341
|
+
}
|
|
115342
|
+
try {
|
|
115343
|
+
const bus = dbus.sessionBus();
|
|
115344
|
+
const obj = await bus.getProxyObject("org.freedesktop.portal.Desktop", "/org/freedesktop/portal/desktop");
|
|
115345
|
+
const portal = obj.getInterface("org.freedesktop.portal.RemoteDesktop");
|
|
115346
|
+
const sessionHandle = await establishSession(portal);
|
|
115347
|
+
if (!sessionHandle)
|
|
115348
|
+
return fail4(`the RemoteDesktop portal did not grant a session${lastSessionError ? ` (${lastSessionError})` : " (Start was refused, or nobody answered its consent prompt on that machine)"}`);
|
|
115349
|
+
const opts = {};
|
|
115350
|
+
return {
|
|
115351
|
+
pointerMotionAbsolute: (x, y, w, h) => void portal.NotifyPointerMotionAbsolute(sessionHandle, opts, 0, x / Math.max(1, w), y / Math.max(1, h)).catch(() => {
|
|
115352
|
+
}),
|
|
115353
|
+
pointerButton: (b, down) => void portal.NotifyPointerButton(sessionHandle, opts, b, down ? 1 : 0).catch(() => {
|
|
115354
|
+
}),
|
|
115355
|
+
pointerAxis: (dx, dy) => void portal.NotifyPointerAxis(sessionHandle, opts, dx, dy).catch(() => {
|
|
115356
|
+
}),
|
|
115357
|
+
keyboardKeycode: (k, down) => void portal.NotifyKeyboardKeycode(sessionHandle, opts, k, down ? 1 : 0).catch(() => {
|
|
115358
|
+
}),
|
|
115359
|
+
keyboardKeysym: (ks, down) => void portal.NotifyKeyboardKeysym(sessionHandle, opts, ks, down ? 1 : 0).catch(() => {
|
|
115360
|
+
}),
|
|
115361
|
+
close: () => {
|
|
115362
|
+
}
|
|
115363
|
+
};
|
|
115364
|
+
} catch (e) {
|
|
115365
|
+
return fail4(`the RemoteDesktop portal is not reachable: ${e instanceof Error ? e.message : String(e)}`);
|
|
115366
|
+
}
|
|
115367
|
+
}
|
|
115368
|
+
var lastSessionError = "";
|
|
115369
|
+
async function establishSession(portal) {
|
|
115370
|
+
try {
|
|
115371
|
+
const token4 = "tesseract" + Math.floor(Date.now() % 1e6);
|
|
115372
|
+
await portal.CreateSession({ session_handle_token: token4, handle_token: token4 });
|
|
115373
|
+
const session = `/org/freedesktop/portal/desktop/session/${token4}`;
|
|
115374
|
+
await portal.SelectDevices(session, { types: 3, handle_token: token4 + "s" });
|
|
115375
|
+
await portal.Start(session, "", { handle_token: token4 + "g" });
|
|
115376
|
+
return session;
|
|
115377
|
+
} catch (e) {
|
|
115378
|
+
lastSessionError = e instanceof Error ? e.message : String(e);
|
|
115379
|
+
return null;
|
|
115380
|
+
}
|
|
115381
|
+
}
|
|
115382
|
+
var KEYSYM = {
|
|
115383
|
+
// Modifiers
|
|
115384
|
+
shift: 65505,
|
|
115385
|
+
ctrl: 65507,
|
|
115386
|
+
alt: 65513,
|
|
115387
|
+
cmd: 65515,
|
|
115388
|
+
// Shift_L, Control_L, Alt_L, Super_L
|
|
115389
|
+
// Named keys
|
|
115390
|
+
enter: 65293,
|
|
115391
|
+
tab: 65289,
|
|
115392
|
+
escape: 65307,
|
|
115393
|
+
backspace: 65288,
|
|
115394
|
+
delete: 65535,
|
|
115395
|
+
space: 32,
|
|
115396
|
+
up: 65362,
|
|
115397
|
+
down: 65364,
|
|
115398
|
+
left: 65361,
|
|
115399
|
+
right: 65363,
|
|
115400
|
+
home: 65360,
|
|
115401
|
+
end: 65367,
|
|
115402
|
+
pageup: 65365,
|
|
115403
|
+
pagedown: 65366,
|
|
115404
|
+
insert: 65379,
|
|
115405
|
+
f1: 65470,
|
|
115406
|
+
f2: 65471,
|
|
115407
|
+
f3: 65472,
|
|
115408
|
+
f4: 65473,
|
|
115409
|
+
f5: 65474,
|
|
115410
|
+
f6: 65475,
|
|
115411
|
+
f7: 65476,
|
|
115412
|
+
f8: 65477,
|
|
115413
|
+
f9: 65478,
|
|
115414
|
+
f10: 65479,
|
|
115415
|
+
f11: 65480,
|
|
115416
|
+
f12: 65481
|
|
115417
|
+
};
|
|
115418
|
+
function charKeysym(ch) {
|
|
115419
|
+
const cp2 = ch.codePointAt(0) ?? 0;
|
|
115420
|
+
if (cp2 === 0)
|
|
115421
|
+
return 0;
|
|
115422
|
+
return cp2 <= 255 ? cp2 : 16777216 + cp2;
|
|
115423
|
+
}
|
|
115424
|
+
function tokenKeysym(token4) {
|
|
115425
|
+
const t = token4.trim().toLowerCase();
|
|
115426
|
+
if (KEYSYM[t] !== void 0)
|
|
115427
|
+
return KEYSYM[t];
|
|
115428
|
+
return t.length === 1 ? charKeysym(t) : 0;
|
|
115429
|
+
}
|
|
115430
|
+
function waylandInputRunner(p, screen) {
|
|
115431
|
+
const w = () => screen.w, h = () => screen.h;
|
|
115432
|
+
return {
|
|
115433
|
+
async mouseMove(pt) {
|
|
115434
|
+
p.pointerMotionAbsolute(pt.x, pt.y, w(), h());
|
|
115435
|
+
},
|
|
115436
|
+
async mouseClick(o) {
|
|
115437
|
+
if (o.at)
|
|
115438
|
+
p.pointerMotionAbsolute(o.at.x, o.at.y, w(), h());
|
|
115439
|
+
const b = BTN[o.button ?? "left"];
|
|
115440
|
+
const click = () => {
|
|
115441
|
+
p.pointerButton(b, true);
|
|
115442
|
+
p.pointerButton(b, false);
|
|
115443
|
+
};
|
|
115444
|
+
click();
|
|
115445
|
+
if (o.double)
|
|
115446
|
+
click();
|
|
115447
|
+
},
|
|
115448
|
+
async scroll(o) {
|
|
115449
|
+
p.pointerAxis(o.dx ?? 0, o.dy ?? 0);
|
|
115450
|
+
},
|
|
115451
|
+
async typeText(text13) {
|
|
115452
|
+
for (const ch of text13) {
|
|
115453
|
+
const ks = charKeysym(ch);
|
|
115454
|
+
if (!ks)
|
|
115455
|
+
continue;
|
|
115456
|
+
p.keyboardKeysym(ks, true);
|
|
115457
|
+
p.keyboardKeysym(ks, false);
|
|
115458
|
+
}
|
|
115459
|
+
},
|
|
115460
|
+
async pressKeys(combo) {
|
|
115461
|
+
const parts = combo.split("+").map((x) => x.trim()).filter(Boolean);
|
|
115462
|
+
if (!parts.length)
|
|
115463
|
+
return;
|
|
115464
|
+
const key2 = tokenKeysym(parts[parts.length - 1]);
|
|
115465
|
+
const mods = parts.slice(0, -1).map(tokenKeysym).filter((k) => k > 0);
|
|
115466
|
+
if (!key2)
|
|
115467
|
+
return;
|
|
115468
|
+
for (const m of mods)
|
|
115469
|
+
p.keyboardKeysym(m, true);
|
|
115470
|
+
p.keyboardKeysym(key2, true);
|
|
115471
|
+
p.keyboardKeysym(key2, false);
|
|
115472
|
+
for (const m of [...mods].reverse())
|
|
115473
|
+
p.keyboardKeysym(m, false);
|
|
115474
|
+
}
|
|
115475
|
+
};
|
|
115476
|
+
}
|
|
115477
|
+
|
|
115478
|
+
// ../../packages/host-core/dist/rdesk/host-clipboard.js
|
|
115479
|
+
init_dist2();
|
|
115480
|
+
import { statSync as statSync19 } from "node:fs";
|
|
115481
|
+
import { basename as basename13 } from "node:path";
|
|
115482
|
+
function readHostClipboardFilePaths() {
|
|
115483
|
+
const el2 = electronModule();
|
|
115484
|
+
const clip2 = el2?.clipboard;
|
|
115485
|
+
if (!clip2)
|
|
115486
|
+
return [];
|
|
115487
|
+
try {
|
|
115488
|
+
return readFilePathsWith((f) => clip2.read(f));
|
|
115489
|
+
} catch {
|
|
115490
|
+
return [];
|
|
115491
|
+
}
|
|
115492
|
+
}
|
|
115493
|
+
function describeClipFiles(paths) {
|
|
115494
|
+
const out = [];
|
|
115495
|
+
for (const p of paths) {
|
|
115496
|
+
try {
|
|
115497
|
+
const st = statSync19(p);
|
|
115498
|
+
out.push({ path: p, name: basename13(p), isDir: st.isDirectory(), size: st.isDirectory() ? 0 : st.size });
|
|
115499
|
+
} catch {
|
|
115500
|
+
}
|
|
115501
|
+
}
|
|
115502
|
+
return out;
|
|
115503
|
+
}
|
|
115504
|
+
function watchHostClipboardFiles(onChange, intervalMs = 1500) {
|
|
115505
|
+
let last = "";
|
|
115506
|
+
let stopped2 = false;
|
|
115507
|
+
const tick = () => {
|
|
115508
|
+
if (stopped2)
|
|
115509
|
+
return;
|
|
115510
|
+
const paths = readHostClipboardFilePaths();
|
|
115511
|
+
const key2 = paths.join("\0");
|
|
115512
|
+
if (key2 === last)
|
|
115513
|
+
return;
|
|
115514
|
+
last = key2;
|
|
115515
|
+
onChange(paths.length ? describeClipFiles(paths) : []);
|
|
115516
|
+
};
|
|
115517
|
+
const timer2 = setInterval(tick, intervalMs);
|
|
115518
|
+
timer2.unref?.();
|
|
115519
|
+
return () => {
|
|
115520
|
+
stopped2 = true;
|
|
115521
|
+
clearInterval(timer2);
|
|
115522
|
+
};
|
|
115523
|
+
}
|
|
115524
|
+
|
|
115525
|
+
// ../../packages/host-core/dist/rdesk/tesseract-engine.js
|
|
115526
|
+
var TesseractEngine = class {
|
|
115527
|
+
id = "tesseract";
|
|
115528
|
+
ctx;
|
|
115529
|
+
deps;
|
|
115530
|
+
pub = null;
|
|
115531
|
+
injector = null;
|
|
115532
|
+
/** Windows SendInput backend (win32 only) — the injector routes through it instead of libnut. */
|
|
115533
|
+
win = null;
|
|
115534
|
+
/** Unsubscribe from the machine lock/unlock watch (see start()). */
|
|
115535
|
+
stopLockWatch = null;
|
|
115536
|
+
/** Stops the host-clipboard file watcher for this session (see host-clipboard.ts). */
|
|
115537
|
+
stopClipWatch = null;
|
|
115538
|
+
monitorId = "";
|
|
115539
|
+
sourceId = "";
|
|
115540
|
+
// active capture source (screen or window)
|
|
115541
|
+
quality = "auto";
|
|
115542
|
+
systemAudio = false;
|
|
115543
|
+
access;
|
|
115544
|
+
constructor(ctx2, deps) {
|
|
115545
|
+
this.ctx = ctx2;
|
|
115546
|
+
this.deps = deps;
|
|
115547
|
+
this.access = ctx2.accessLevel;
|
|
115548
|
+
}
|
|
115549
|
+
/**
|
|
115550
|
+
* What this session can ACTUALLY do, derived at runtime.
|
|
115551
|
+
*
|
|
115552
|
+
* This used to be a hardcoded constant returned identically on every platform, so a
|
|
115553
|
+
* viewer was told `input: true` by a Wayland host that cannot inject a keystroke, and
|
|
115554
|
+
* offered a system-audio toggle on macOS where the capture is known to be silent. A
|
|
115555
|
+
* capability flag that doesn't track reality is worse than no flag: the viewer renders
|
|
115556
|
+
* a control that quietly does nothing.
|
|
115557
|
+
*/
|
|
115558
|
+
capabilities() {
|
|
115559
|
+
return {
|
|
115560
|
+
// False until an input runner was actually obtained (see start()): on Wayland
|
|
115561
|
+
// without the portal, control is impossible and the session is view-only.
|
|
115562
|
+
input: !!this.injector,
|
|
115563
|
+
clipboard: true,
|
|
115564
|
+
// No drag/drop or upload surface exists inside an rdesk session today. It was
|
|
115565
|
+
// advertised as if it did; nothing in either viewer ever read it.
|
|
115566
|
+
fileTransfer: false,
|
|
115567
|
+
multiMonitor: true,
|
|
115568
|
+
windowCapture: true,
|
|
115569
|
+
// share a single window/app (privacy)
|
|
115570
|
+
// Voice now rides this session's own peer connection (setAv).
|
|
115571
|
+
bidiAudioVideo: true,
|
|
115572
|
+
// Windows/Linux capture desktop audio natively; macOS needs a loopback we don't
|
|
115573
|
+
// ship, so claiming it there just produces a toggle that yields silence.
|
|
115574
|
+
systemAudio: process.platform !== "darwin"
|
|
115575
|
+
};
|
|
115576
|
+
}
|
|
115577
|
+
listMonitors() {
|
|
115578
|
+
return listPublisherMonitors();
|
|
115579
|
+
}
|
|
115580
|
+
listSources() {
|
|
115581
|
+
return listPublisherSources();
|
|
115582
|
+
}
|
|
115583
|
+
startOpts(iceServers) {
|
|
115584
|
+
return { iceServers, quality: this.quality, systemAudio: this.systemAudio };
|
|
115585
|
+
}
|
|
115586
|
+
async setSource(sourceId) {
|
|
115587
|
+
this.sourceId = sourceId;
|
|
115588
|
+
const origin = sourceId.startsWith("screen:") ? this.originForActiveScreen() : { x: 0, y: 0 };
|
|
115589
|
+
this.injector?.setOrigin(origin);
|
|
115590
|
+
const iceServers = await this.deps.getIceServers().catch(() => []);
|
|
115591
|
+
await this.pub?.setSource(sourceId, this.startOpts(iceServers));
|
|
115592
|
+
}
|
|
115593
|
+
originForActiveScreen() {
|
|
115594
|
+
const b = displayBoundsById(this.monitorId);
|
|
115595
|
+
return b ? { x: b.x, y: b.y } : { x: 0, y: 0 };
|
|
115596
|
+
}
|
|
115597
|
+
/**
|
|
115598
|
+
* Tell the viewer the machine is LOCKED, using the daemon's existing lock/unlock watch.
|
|
115599
|
+
*
|
|
115600
|
+
* When Windows locks (or shows a UAC / Ctrl-Alt-Del secure desktop) capture goes black and any
|
|
115601
|
+
* injected input is silently dropped — a user process cannot reach the secure desktop. The viewer
|
|
115602
|
+
* would otherwise see a black rectangle and a "the display may be asleep" guess. Reporting an
|
|
115603
|
+
* accurate capture-lost with the real cause replaces that guess; on unlock we clear it. Held keys
|
|
115604
|
+
* are also released, so a modifier down when the lock hit doesn't stick. Enabled on every platform
|
|
115605
|
+
* (the events are cross-platform); the wording names the Windows secure desktop where it applies.
|
|
115606
|
+
*/
|
|
115607
|
+
startLockWatch() {
|
|
115608
|
+
const lockedMsg = process.platform === "win32" ? "This computer is locked (Windows secure desktop). Its screen can\u2019t be shared and input is paused until someone signs in." : "This computer is locked. Its screen can\u2019t be shared until someone signs in.";
|
|
115609
|
+
try {
|
|
115610
|
+
if (systemState().screenLocked === true)
|
|
115611
|
+
this.ctx.onCapture?.(false, lockedMsg);
|
|
115612
|
+
} catch {
|
|
115613
|
+
}
|
|
115614
|
+
this.stopLockWatch = onSystemEvent((e) => {
|
|
115615
|
+
if (e.event.type === "power:lock-screen") {
|
|
115616
|
+
this.injector?.releaseAll();
|
|
115617
|
+
this.ctx.onCapture?.(false, lockedMsg);
|
|
115618
|
+
} else if (e.event.type === "power:unlock-screen") {
|
|
115619
|
+
this.ctx.onCapture?.(true, "");
|
|
115620
|
+
}
|
|
115621
|
+
});
|
|
115622
|
+
}
|
|
115623
|
+
async start(opts) {
|
|
115624
|
+
const monitors = await listPublisherMonitors();
|
|
115625
|
+
this.monitorId = opts.monitorId ?? monitors.find((m) => m.primary)?.id ?? monitors[0]?.id ?? "";
|
|
115626
|
+
this.quality = opts.quality ?? "auto";
|
|
115627
|
+
this.sourceId = await sourceIdForMonitor(this.monitorId).catch(() => "");
|
|
115628
|
+
const origin = displayBoundsById(this.monitorId) ?? { x: 0, y: 0, width: 0, height: 0 };
|
|
115629
|
+
let runner2;
|
|
115630
|
+
if (this.deps.makeRunner) {
|
|
115631
|
+
runner2 = this.deps.makeRunner();
|
|
115632
|
+
} else if (isWaylandSession()) {
|
|
115633
|
+
let waylandReason = "";
|
|
115634
|
+
const wp = await createWaylandInputProvider((r) => {
|
|
115635
|
+
waylandReason = r;
|
|
115636
|
+
});
|
|
115637
|
+
if (wp) {
|
|
115638
|
+
const m = monitors.find((x) => x.id === this.monitorId) ?? monitors[0];
|
|
115639
|
+
runner2 = waylandInputRunner(wp, { w: m?.width ?? origin.width, h: m?.height ?? origin.height });
|
|
115640
|
+
this.ctx.log("rdesk: Wayland input via XDG RemoteDesktop portal.");
|
|
115641
|
+
} else {
|
|
115642
|
+
this.ctx.log(`rdesk: Wayland \u2014 keyboard and mouse unavailable, streaming view-only. Reason: ${waylandReason || "unknown"}`);
|
|
115643
|
+
runner2 = createDaemonOsControlRunner();
|
|
115644
|
+
}
|
|
115645
|
+
} else {
|
|
115646
|
+
runner2 = createDaemonOsControlRunner();
|
|
115647
|
+
}
|
|
115648
|
+
let winBinding = null;
|
|
115649
|
+
if (!this.deps.makeRunner && process.platform === "win32") {
|
|
115650
|
+
const win3 = createWinInput({ log: (m) => this.ctx.log(m) });
|
|
115651
|
+
if (win3) {
|
|
115652
|
+
this.win = win3;
|
|
115653
|
+
void win3.warm();
|
|
115654
|
+
winBinding = { api: win3, toFraction: (p) => virtualDesktopFraction(p) };
|
|
115655
|
+
}
|
|
115656
|
+
}
|
|
115657
|
+
this.injector = new RdeskInputInjector(runner2, { x: origin.x, y: origin.y }, this.ctx.accessLevel, winBinding);
|
|
115658
|
+
this.startLockWatch();
|
|
115659
|
+
const iceServers = await this.deps.getIceServers().catch(() => []);
|
|
115660
|
+
const pub = new PublisherWindow(this.ctx.sessionId, {
|
|
115661
|
+
onOffer: (sdp) => this.ctx.sendSignal({ type: "signal:offer", sdp }),
|
|
115662
|
+
onIce: (candidate) => this.ctx.sendSignal({ type: "signal:ice", candidate }),
|
|
115663
|
+
onInput: (ev) => void this.injector?.apply(ev),
|
|
115664
|
+
// Clipboard sync (Phase 6): write the viewer's clipboard onto the host so ⌘V pastes
|
|
115665
|
+
// into the remote machine. Gated by control access (view-only never writes).
|
|
115666
|
+
onClipboard: (text13) => {
|
|
115667
|
+
if (this.access === "control") {
|
|
115668
|
+
const el2 = electronModule();
|
|
115669
|
+
try {
|
|
115670
|
+
el2?.clipboard?.writeText(text13);
|
|
115671
|
+
} catch {
|
|
115672
|
+
}
|
|
115673
|
+
}
|
|
115674
|
+
},
|
|
115675
|
+
onReady: () => this.ctx.log("publisher connected"),
|
|
115676
|
+
onError: (err2) => {
|
|
115677
|
+
this.ctx.log(`publisher error: ${err2}`);
|
|
115678
|
+
this.ctx.sendControl({ type: "rdesk:closed", reason: "network" });
|
|
115679
|
+
},
|
|
115680
|
+
// Media is direct-first; log which path actually won so a regression to relay
|
|
115681
|
+
// (which costs real bandwidth) is visible instead of silent. Types only, no IPs.
|
|
115682
|
+
onCapture: (ok, message) => {
|
|
115683
|
+
this.ctx.log(`capture ${ok ? "ok" : `LOST \u2014 ${message}`}`);
|
|
115684
|
+
this.ctx.onCapture?.(ok, message);
|
|
115685
|
+
},
|
|
115686
|
+
onPath: (path6, d) => {
|
|
115687
|
+
this.ctx.log(`media path=${path6} (local=${d.localType ?? "?"} remote=${d.remoteType ?? "?"})`);
|
|
115688
|
+
this.ctx.onPath?.(path6, d);
|
|
115689
|
+
}
|
|
115690
|
+
});
|
|
115691
|
+
this.pub = pub;
|
|
115692
|
+
this.stopClipWatch = watchHostClipboardFiles((files) => {
|
|
115693
|
+
if (this.access !== "control")
|
|
115694
|
+
return;
|
|
115695
|
+
this.pub?.sendData({ t: "clipboard-files", files });
|
|
115696
|
+
});
|
|
115697
|
+
await pub.start({
|
|
115698
|
+
iceServers,
|
|
115699
|
+
monitorId: this.monitorId,
|
|
115700
|
+
quality: this.quality,
|
|
115701
|
+
systemAudio: this.systemAudio
|
|
115702
|
+
});
|
|
115703
|
+
}
|
|
115704
|
+
onSignal(msg) {
|
|
115705
|
+
if (!this.pub)
|
|
115706
|
+
return;
|
|
115707
|
+
if (msg.type === "signal:answer")
|
|
115708
|
+
this.pub.answer(msg.sdp);
|
|
115709
|
+
else if (msg.type === "signal:ice")
|
|
115710
|
+
this.pub.addIce(msg.candidate);
|
|
115711
|
+
}
|
|
115712
|
+
applyInput(ev) {
|
|
115713
|
+
void this.injector?.apply(ev);
|
|
115714
|
+
}
|
|
115715
|
+
setAccessLevel(level) {
|
|
115716
|
+
this.access = level;
|
|
115717
|
+
this.injector?.setAccess(level);
|
|
115718
|
+
}
|
|
115719
|
+
async setMonitor(monitorId) {
|
|
115720
|
+
this.monitorId = monitorId;
|
|
115721
|
+
this.sourceId = await sourceIdForMonitor(monitorId).catch(() => this.sourceId);
|
|
115722
|
+
const origin = displayBoundsById(monitorId);
|
|
115723
|
+
if (origin && this.injector)
|
|
115724
|
+
this.injector.setOrigin({ x: origin.x, y: origin.y });
|
|
115725
|
+
const iceServers = await this.deps.getIceServers().catch(() => []);
|
|
115726
|
+
await this.pub?.setMonitor(monitorId, this.startOpts(iceServers));
|
|
115727
|
+
}
|
|
115728
|
+
setAv(on) {
|
|
115729
|
+
this.pub?.setAv(on);
|
|
115730
|
+
}
|
|
115731
|
+
setQuality(preset) {
|
|
115732
|
+
this.quality = preset;
|
|
115733
|
+
this.pub?.setQuality(preset);
|
|
115734
|
+
}
|
|
115735
|
+
setMute(m) {
|
|
115736
|
+
if (typeof m.systemAudio === "boolean" && m.systemAudio !== this.systemAudio && this.sourceId) {
|
|
115737
|
+
this.systemAudio = m.systemAudio;
|
|
115738
|
+
void this.deps.getIceServers().catch(() => []).then((ice) => this.pub?.setSource(this.sourceId, this.startOpts(ice)));
|
|
115739
|
+
}
|
|
115740
|
+
}
|
|
115741
|
+
async stop() {
|
|
115742
|
+
this.stopClipWatch?.();
|
|
115743
|
+
this.stopClipWatch = null;
|
|
115744
|
+
this.stopLockWatch?.();
|
|
115745
|
+
this.stopLockWatch = null;
|
|
115746
|
+
this.injector?.releaseAll();
|
|
115747
|
+
await this.pub?.stop();
|
|
115748
|
+
this.pub = null;
|
|
115749
|
+
this.injector = null;
|
|
115750
|
+
await this.win?.stop();
|
|
115751
|
+
this.win = null;
|
|
115752
|
+
}
|
|
115753
|
+
};
|
|
115754
|
+
function registerTesseractEngine(deps) {
|
|
115755
|
+
registerRdeskEngine("tesseract", (ctx2) => new TesseractEngine(ctx2, deps));
|
|
115756
|
+
}
|
|
115757
|
+
|
|
115758
|
+
// ../../packages/host-core/dist/local-server.js
|
|
115759
|
+
import { timingSafeEqual as timingSafeEqual5 } from "node:crypto";
|
|
115760
|
+
|
|
115761
|
+
// ../../packages/host-core/dist/in-process-link.js
|
|
115762
|
+
var OPEN = 1;
|
|
115763
|
+
var CLOSED = 3;
|
|
115764
|
+
var InProcSocket = class {
|
|
115765
|
+
OPEN = OPEN;
|
|
115766
|
+
CLOSED = CLOSED;
|
|
115767
|
+
readyState = OPEN;
|
|
115768
|
+
peer = null;
|
|
115769
|
+
listeners = /* @__PURE__ */ new Map();
|
|
115770
|
+
on(event, cb) {
|
|
115771
|
+
let s = this.listeners.get(event);
|
|
115772
|
+
if (!s) {
|
|
115773
|
+
s = /* @__PURE__ */ new Set();
|
|
115774
|
+
this.listeners.set(event, s);
|
|
115775
|
+
}
|
|
115776
|
+
s.add(cb);
|
|
115777
|
+
return this;
|
|
115778
|
+
}
|
|
115779
|
+
once(event, cb) {
|
|
115780
|
+
const wrap = (...a) => {
|
|
115781
|
+
this.off(event, wrap);
|
|
115782
|
+
cb(...a);
|
|
115783
|
+
};
|
|
115784
|
+
return this.on(event, wrap);
|
|
115785
|
+
}
|
|
115786
|
+
off(event, cb) {
|
|
115787
|
+
this.listeners.get(event)?.delete(cb);
|
|
115788
|
+
return this;
|
|
115789
|
+
}
|
|
115790
|
+
removeListener(event, cb) {
|
|
115791
|
+
return this.off(event, cb);
|
|
115792
|
+
}
|
|
115793
|
+
fire(event, ...args) {
|
|
115794
|
+
const s = this.listeners.get(event);
|
|
115795
|
+
if (!s)
|
|
115796
|
+
return;
|
|
115797
|
+
for (const cb of [...s]) {
|
|
115798
|
+
try {
|
|
115799
|
+
cb(...args);
|
|
115002
115800
|
} catch {
|
|
115003
|
-
done(false);
|
|
115004
115801
|
}
|
|
115005
|
-
}
|
|
115802
|
+
}
|
|
115006
115803
|
}
|
|
115007
|
-
|
|
115008
|
-
|
|
115009
|
-
|
|
115010
|
-
|
|
115011
|
-
|
|
115804
|
+
/** Deliver `data` to the PEER's 'message' listeners on the next macrotask. */
|
|
115805
|
+
send(data) {
|
|
115806
|
+
const peer = this.peer;
|
|
115807
|
+
if (!peer || peer.readyState !== OPEN)
|
|
115808
|
+
return;
|
|
115809
|
+
setImmediate(() => {
|
|
115810
|
+
if (peer.readyState === OPEN)
|
|
115811
|
+
peer.fire("message", data);
|
|
115812
|
+
});
|
|
115012
115813
|
}
|
|
115013
|
-
|
|
115014
|
-
|
|
115015
|
-
const started2 = Date.now();
|
|
115016
|
-
const results = await Promise.all(PROBE_URLS.map((u) => probeOnce(u)));
|
|
115017
|
-
const reachable = results.some(Boolean);
|
|
115018
|
-
emitSystemEvent({ type: "net:probe", reachable, ms: Date.now() - started2 });
|
|
115019
|
-
if (reachable !== state2.online)
|
|
115020
|
-
emitSystemEvent({ type: reachable ? "net:online" : "net:offline", evidence: "probed" });
|
|
115021
|
-
return reachable;
|
|
115022
|
-
}
|
|
115023
|
-
function startNetworkProbing(requester = "daemon") {
|
|
115024
|
-
wanted.add(requester);
|
|
115025
|
-
if (probing)
|
|
115026
|
-
return;
|
|
115027
|
-
probing = true;
|
|
115028
|
-
probeAttempt = 0;
|
|
115029
|
-
scheduleProbe(0);
|
|
115030
|
-
}
|
|
115031
|
-
function stopNetworkProbing(requester) {
|
|
115032
|
-
if (requester !== void 0) {
|
|
115033
|
-
wanted.delete(requester);
|
|
115034
|
-
if (wanted.size > 0)
|
|
115814
|
+
close(_code, _reason) {
|
|
115815
|
+
if (this.readyState === CLOSED)
|
|
115035
115816
|
return;
|
|
115036
|
-
|
|
115037
|
-
|
|
115817
|
+
this.readyState = CLOSED;
|
|
115818
|
+
const peer = this.peer;
|
|
115819
|
+
setImmediate(() => this.fire("close"));
|
|
115820
|
+
if (peer && peer.readyState !== CLOSED) {
|
|
115821
|
+
peer.readyState = CLOSED;
|
|
115822
|
+
setImmediate(() => peer.fire("close"));
|
|
115823
|
+
}
|
|
115038
115824
|
}
|
|
115039
|
-
|
|
115040
|
-
|
|
115041
|
-
|
|
115042
|
-
probeTimer = null;
|
|
115825
|
+
/** @internal wire this end to its peer. */
|
|
115826
|
+
_link(peer) {
|
|
115827
|
+
this.peer = peer;
|
|
115043
115828
|
}
|
|
115044
|
-
|
|
115829
|
+
/** @internal fire 'open' on the next macrotask (client end only; the server never awaits it). */
|
|
115830
|
+
_fireOpen() {
|
|
115831
|
+
setImmediate(() => this.fire("open"));
|
|
115832
|
+
}
|
|
115833
|
+
};
|
|
115834
|
+
function makeInProcPair() {
|
|
115835
|
+
const serverEnd = new InProcSocket();
|
|
115836
|
+
const clientEnd = new InProcSocket();
|
|
115837
|
+
serverEnd._link(clientEnd);
|
|
115838
|
+
clientEnd._link(serverEnd);
|
|
115839
|
+
return { serverEnd, clientEnd };
|
|
115045
115840
|
}
|
|
115046
|
-
var
|
|
115047
|
-
|
|
115048
|
-
|
|
115049
|
-
var wanted = /* @__PURE__ */ new Set();
|
|
115050
|
-
function scheduleProbe(delayMs) {
|
|
115051
|
-
if (probeTimer)
|
|
115052
|
-
clearTimeout(probeTimer);
|
|
115053
|
-
probeTimer = setTimeout(() => {
|
|
115054
|
-
void runProbeCycle();
|
|
115055
|
-
}, delayMs);
|
|
115056
|
-
probeTimer.unref?.();
|
|
115841
|
+
var connector = null;
|
|
115842
|
+
function setInProcConnector(fn) {
|
|
115843
|
+
connector = fn;
|
|
115057
115844
|
}
|
|
115058
|
-
|
|
115059
|
-
|
|
115060
|
-
|
|
115061
|
-
|
|
115062
|
-
|
|
115063
|
-
|
|
115064
|
-
|
|
115065
|
-
|
|
115066
|
-
|
|
115067
|
-
|
|
115068
|
-
|
|
115069
|
-
|
|
115845
|
+
|
|
115846
|
+
// ../../packages/host-core/dist/rdesk/server-prefs.js
|
|
115847
|
+
init_credentials();
|
|
115848
|
+
import { readFileSync as readFileSync44, writeFileSync as writeFileSync29 } from "node:fs";
|
|
115849
|
+
import { join as join72 } from "node:path";
|
|
115850
|
+
function rdeskServerPrefsPath() {
|
|
115851
|
+
return join72(daemonDir2(), "rdesk-server.json");
|
|
115852
|
+
}
|
|
115853
|
+
var DEFAULTS2 = {
|
|
115854
|
+
enabled: true,
|
|
115855
|
+
defaultAccess: "control",
|
|
115856
|
+
quality: "auto",
|
|
115857
|
+
autostart: true,
|
|
115858
|
+
hideTray: false,
|
|
115859
|
+
noticeShownAt: 0
|
|
115860
|
+
};
|
|
115861
|
+
function readRdeskServerPrefs() {
|
|
115862
|
+
try {
|
|
115863
|
+
const p = JSON.parse(readFileSync44(rdeskServerPrefsPath(), "utf8"));
|
|
115864
|
+
return {
|
|
115865
|
+
// `!== false` (not `?? true`): only an explicit opt-out disables sharing, so a
|
|
115866
|
+
// partially-written or older prefs file never silently turns the machine off.
|
|
115867
|
+
enabled: p.enabled !== false,
|
|
115868
|
+
defaultAccess: p.defaultAccess === "view" ? "view" : "control",
|
|
115869
|
+
quality: p.quality ?? DEFAULTS2.quality,
|
|
115870
|
+
autostart: p.autostart ?? DEFAULTS2.autostart,
|
|
115871
|
+
hideTray: p.hideTray ?? DEFAULTS2.hideTray,
|
|
115872
|
+
noticeShownAt: typeof p.noticeShownAt === "number" ? p.noticeShownAt : 0
|
|
115873
|
+
};
|
|
115874
|
+
} catch {
|
|
115875
|
+
return { ...DEFAULTS2 };
|
|
115070
115876
|
}
|
|
115071
|
-
scheduleProbe(probeDelayMs(probeAttempt++));
|
|
115072
115877
|
}
|
|
115073
|
-
function
|
|
115074
|
-
|
|
115075
|
-
|
|
115076
|
-
|
|
115077
|
-
|
|
115078
|
-
out.push(`${name}:${a.address}`);
|
|
115878
|
+
function writeRdeskServerPrefs(patch) {
|
|
115879
|
+
try {
|
|
115880
|
+
const next = { ...readRdeskServerPrefs(), ...patch };
|
|
115881
|
+
writeFileSync29(rdeskServerPrefsPath(), JSON.stringify(next), { mode: 384 });
|
|
115882
|
+
} catch {
|
|
115079
115883
|
}
|
|
115080
|
-
return out.sort();
|
|
115081
115884
|
}
|
|
115082
|
-
function
|
|
115083
|
-
if (
|
|
115084
|
-
return
|
|
115085
|
-
|
|
115086
|
-
|
|
115087
|
-
|
|
115088
|
-
});
|
|
115089
|
-
});
|
|
115885
|
+
function rdeskSharingEnabled() {
|
|
115886
|
+
if (process.env.CCB_RDESK_ENABLED === "0")
|
|
115887
|
+
return false;
|
|
115888
|
+
if (process.env.CCB_RDESK_ENABLED === "1")
|
|
115889
|
+
return true;
|
|
115890
|
+
return readRdeskServerPrefs().enabled;
|
|
115090
115891
|
}
|
|
115091
|
-
|
|
115092
|
-
|
|
115093
|
-
|
|
115892
|
+
|
|
115893
|
+
// ../../packages/host-core/dist/rdesk/password.js
|
|
115894
|
+
init_credentials();
|
|
115895
|
+
import { scryptSync, randomBytes as randomBytes5, timingSafeEqual as timingSafeEqual4, randomInt } from "node:crypto";
|
|
115896
|
+
import { readFileSync as readFileSync45, writeFileSync as writeFileSync30, existsSync as existsSync32, unlinkSync as unlinkSync4 } from "node:fs";
|
|
115897
|
+
import { join as join73 } from "node:path";
|
|
115898
|
+
function pwFile() {
|
|
115899
|
+
return join73(daemonDir2(), "rdesk-password.json");
|
|
115094
115900
|
}
|
|
115095
|
-
|
|
115096
|
-
|
|
115097
|
-
|
|
115098
|
-
var
|
|
115099
|
-
var
|
|
115100
|
-
function
|
|
115101
|
-
|
|
115102
|
-
|
|
115103
|
-
|
|
115104
|
-
|
|
115105
|
-
|
|
115106
|
-
|
|
115107
|
-
|
|
115108
|
-
|
|
115109
|
-
|
|
115110
|
-
|
|
115111
|
-
|
|
115112
|
-
|
|
115113
|
-
};
|
|
115114
|
-
try {
|
|
115115
|
-
pm.on(name, h);
|
|
115116
|
-
stops.push(() => {
|
|
115117
|
-
try {
|
|
115118
|
-
pm.removeListener(name, h);
|
|
115119
|
-
} catch {
|
|
115120
|
-
}
|
|
115121
|
-
});
|
|
115122
|
-
} catch {
|
|
115123
|
-
}
|
|
115124
|
-
};
|
|
115125
|
-
wire("suspend", () => ({ type: "power:suspend" }));
|
|
115126
|
-
wire("resume", () => ({ type: "power:resume" }));
|
|
115127
|
-
wire("on-ac", () => ({ type: "power:on-ac" }));
|
|
115128
|
-
wire("on-battery", () => ({ type: "power:on-battery" }));
|
|
115129
|
-
wire("lock-screen", () => ({ type: "power:lock-screen" }));
|
|
115130
|
-
wire("unlock-screen", () => ({ type: "power:unlock-screen" }));
|
|
115131
|
-
wire("user-did-become-active", () => ({ type: "power:user-active" }));
|
|
115132
|
-
wire("user-did-resign-active", () => ({ type: "power:user-idle", idleSeconds: pm.getSystemIdleTime?.() ?? 0 }));
|
|
115133
|
-
wire("shutdown", () => ({ type: "power:os-shutdown" }));
|
|
115134
|
-
wire("thermal-state-change", () => ({ type: "power:thermal", state: pm.getCurrentThermalState?.() ?? "unknown" }));
|
|
115135
|
-
if (typeof pm.onBatteryPower === "boolean")
|
|
115136
|
-
emitSystemEvent({ type: pm.onBatteryPower ? "power:on-battery" : "power:on-ac" });
|
|
115137
|
-
}
|
|
115138
|
-
if (el2?.screen?.on) {
|
|
115139
|
-
const count = () => {
|
|
115140
|
-
try {
|
|
115141
|
-
return el2.screen.getAllDisplays().length;
|
|
115142
|
-
} catch {
|
|
115143
|
-
return 0;
|
|
115144
|
-
}
|
|
115145
|
-
};
|
|
115146
|
-
const wireScreen = (name, type) => {
|
|
115147
|
-
const h = () => {
|
|
115148
|
-
emitSystemEvent({ type, count: count() });
|
|
115149
|
-
};
|
|
115150
|
-
try {
|
|
115151
|
-
el2.screen.on(name, h);
|
|
115152
|
-
stops.push(() => {
|
|
115153
|
-
try {
|
|
115154
|
-
el2.screen.removeListener?.(name, h);
|
|
115155
|
-
} catch {
|
|
115156
|
-
}
|
|
115157
|
-
});
|
|
115158
|
-
} catch {
|
|
115159
|
-
}
|
|
115160
|
-
};
|
|
115161
|
-
wireScreen("display-added", "display:added");
|
|
115162
|
-
wireScreen("display-removed", "display:removed");
|
|
115163
|
-
wireScreen("display-metrics-changed", "display:changed");
|
|
115164
|
-
state2.displays = count();
|
|
115165
|
-
}
|
|
115166
|
-
if (el2?.app?.on) {
|
|
115167
|
-
const wireApp = (name, fire) => {
|
|
115168
|
-
try {
|
|
115169
|
-
el2.app.on(name, fire);
|
|
115170
|
-
stops.push(() => {
|
|
115171
|
-
try {
|
|
115172
|
-
el2.app.removeListener?.(name, fire);
|
|
115173
|
-
} catch {
|
|
115174
|
-
}
|
|
115175
|
-
});
|
|
115176
|
-
} catch {
|
|
115177
|
-
}
|
|
115178
|
-
};
|
|
115179
|
-
joinParticipant(DAEMON_PARTICIPANT, "Tesseract daemon");
|
|
115180
|
-
wireApp("activate", () => reportParticipantActive(DAEMON_PARTICIPANT, true));
|
|
115181
|
-
wireApp("did-become-active", () => reportParticipantActive(DAEMON_PARTICIPANT, true));
|
|
115182
|
-
wireApp("did-resign-active", () => reportParticipantActive(DAEMON_PARTICIPANT, false));
|
|
115183
|
-
wireApp("browser-window-focus", () => reportParticipantFocus(DAEMON_PARTICIPANT, true));
|
|
115184
|
-
wireApp("browser-window-blur", () => reportParticipantFocus(DAEMON_PARTICIPANT, false));
|
|
115901
|
+
function pwPlainFile() {
|
|
115902
|
+
return join73(daemonDir2(), "rdesk-password.enc");
|
|
115903
|
+
}
|
|
115904
|
+
var PW_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
|
|
115905
|
+
var PW_LENGTH = 16;
|
|
115906
|
+
function generatePassword() {
|
|
115907
|
+
let out = "";
|
|
115908
|
+
for (let i = 0; i < PW_LENGTH; i++)
|
|
115909
|
+
out += PW_ALPHABET[randomInt(PW_ALPHABET.length)];
|
|
115910
|
+
return (out.match(/.{1,4}/g) ?? [out]).join("-");
|
|
115911
|
+
}
|
|
115912
|
+
function writePlain(password) {
|
|
115913
|
+
try {
|
|
115914
|
+
const safe2 = electronModule()?.safeStorage;
|
|
115915
|
+
if (!safe2?.isEncryptionAvailable())
|
|
115916
|
+
return;
|
|
115917
|
+
writeFileSync30(pwPlainFile(), safe2.encryptString(password), { mode: 384 });
|
|
115918
|
+
} catch {
|
|
115185
115919
|
}
|
|
115186
|
-
|
|
115187
|
-
|
|
115188
|
-
|
|
115189
|
-
|
|
115190
|
-
|
|
115191
|
-
|
|
115192
|
-
|
|
115193
|
-
try {
|
|
115194
|
-
el2.nativeTheme?.removeListener?.("updated", h);
|
|
115195
|
-
} catch {
|
|
115196
|
-
}
|
|
115197
|
-
});
|
|
115198
|
-
} catch {
|
|
115199
|
-
}
|
|
115920
|
+
}
|
|
115921
|
+
function canRevealRdeskPassword() {
|
|
115922
|
+
try {
|
|
115923
|
+
const safe2 = electronModule()?.safeStorage;
|
|
115924
|
+
return !!safe2?.isEncryptionAvailable() && existsSync32(pwPlainFile());
|
|
115925
|
+
} catch {
|
|
115926
|
+
return false;
|
|
115200
115927
|
}
|
|
115201
|
-
|
|
115202
|
-
|
|
115203
|
-
let ticks = 0;
|
|
115204
|
-
const tick = setInterval(() => {
|
|
115205
|
-
const now2 = Date.now();
|
|
115206
|
-
const drift = now2 - lastTick;
|
|
115207
|
-
lastTick = now2;
|
|
115208
|
-
if (drift > CLOCK_JUMP_MS)
|
|
115209
|
-
emitSystemEvent({ type: "power:clock-jump", missedMs: drift - TICK_MS });
|
|
115210
|
-
const next = addressFingerprint();
|
|
115211
|
-
if (next.join("|") !== addresses.join("|")) {
|
|
115212
|
-
addresses = next;
|
|
115213
|
-
emitSystemEvent({ type: "net:interfaces-changed", addresses: next });
|
|
115214
|
-
void probeNetwork().catch(() => {
|
|
115215
|
-
});
|
|
115216
|
-
}
|
|
115217
|
-
if (++ticks % LID_EVERY_TICKS === 0) {
|
|
115218
|
-
void readLidClosed().then((closed) => {
|
|
115219
|
-
if (closed === null || closed === state2.lidClosed)
|
|
115220
|
-
return;
|
|
115221
|
-
emitSystemEvent({ type: closed ? "lid:closed" : "lid:opened" });
|
|
115222
|
-
}).catch(() => {
|
|
115223
|
-
});
|
|
115224
|
-
}
|
|
115225
|
-
}, TICK_MS);
|
|
115226
|
-
tick.unref?.();
|
|
115227
|
-
stops.push(() => clearInterval(tick));
|
|
115928
|
+
}
|
|
115929
|
+
function readRdeskPasswordPlain() {
|
|
115228
115930
|
try {
|
|
115229
|
-
const
|
|
115230
|
-
if (
|
|
115231
|
-
|
|
115931
|
+
const safe2 = electronModule()?.safeStorage;
|
|
115932
|
+
if (!safe2?.isEncryptionAvailable())
|
|
115933
|
+
return null;
|
|
115934
|
+
return safe2.decryptString(readFileSync45(pwPlainFile()));
|
|
115232
115935
|
} catch {
|
|
115936
|
+
return null;
|
|
115233
115937
|
}
|
|
115234
|
-
log10(`system events: watching (${el2 ? "electron" : "headless"})`);
|
|
115235
115938
|
}
|
|
115236
|
-
function
|
|
115237
|
-
|
|
115939
|
+
function setRdeskPassword(password) {
|
|
115940
|
+
if (!password) {
|
|
115238
115941
|
try {
|
|
115239
|
-
|
|
115942
|
+
unlinkSync4(pwFile());
|
|
115943
|
+
} catch {
|
|
115944
|
+
}
|
|
115945
|
+
try {
|
|
115946
|
+
unlinkSync4(pwPlainFile());
|
|
115240
115947
|
} catch {
|
|
115241
115948
|
}
|
|
115949
|
+
return;
|
|
115950
|
+
}
|
|
115951
|
+
const salt = randomBytes5(16);
|
|
115952
|
+
const hash2 = scryptSync(password, salt, 32);
|
|
115953
|
+
writeFileSync30(pwFile(), JSON.stringify({ salt: salt.toString("hex"), hash: hash2.toString("hex") }), { mode: 384 });
|
|
115954
|
+
writePlain(password);
|
|
115955
|
+
}
|
|
115956
|
+
function regenerateRdeskPassword() {
|
|
115957
|
+
const pw = generatePassword();
|
|
115958
|
+
setRdeskPassword(pw);
|
|
115959
|
+
return pw;
|
|
115960
|
+
}
|
|
115961
|
+
function ensureRdeskPassword() {
|
|
115962
|
+
if (hasRdeskPassword())
|
|
115963
|
+
return null;
|
|
115964
|
+
return regenerateRdeskPassword();
|
|
115965
|
+
}
|
|
115966
|
+
function hasRdeskPassword() {
|
|
115967
|
+
return !!process.env.CCB_RDESK_PASSWORD || existsSync32(pwFile());
|
|
115968
|
+
}
|
|
115969
|
+
function verifyRdeskPassword(password) {
|
|
115970
|
+
if (!password)
|
|
115971
|
+
return false;
|
|
115972
|
+
const override3 = process.env.CCB_RDESK_PASSWORD;
|
|
115973
|
+
if (override3)
|
|
115974
|
+
return password.length === override3.length && timingSafeEqual4(Buffer.from(password), Buffer.from(override3));
|
|
115975
|
+
try {
|
|
115976
|
+
const { salt, hash: hash2 } = JSON.parse(readFileSync45(pwFile(), "utf8"));
|
|
115977
|
+
const got = scryptSync(password, Buffer.from(salt, "hex"), 32);
|
|
115978
|
+
const want = Buffer.from(hash2, "hex");
|
|
115979
|
+
return got.length === want.length && timingSafeEqual4(got, want);
|
|
115980
|
+
} catch {
|
|
115981
|
+
return false;
|
|
115242
115982
|
}
|
|
115243
|
-
stops = [];
|
|
115244
|
-
stopNetworkProbing();
|
|
115245
|
-
leaveParticipant(DAEMON_PARTICIPANT);
|
|
115246
|
-
started = false;
|
|
115247
115983
|
}
|
|
115248
115984
|
|
|
115249
115985
|
// ../../packages/host-core/dist/local-server.js
|
|
@@ -117859,7 +118595,9 @@ function uninstallCliAgent(log10 = console.log) {
|
|
|
117859
118595
|
log10("[cli-agent] uninstalling the tesseract-agent npm package");
|
|
117860
118596
|
execFileSync4(process.platform === "win32" ? "npm.cmd" : "npm", ["uninstall", "-g", "tesseract-agent"], {
|
|
117861
118597
|
stdio: ["ignore", "pipe", "pipe"],
|
|
117862
|
-
timeout: 12e4
|
|
118598
|
+
timeout: 12e4,
|
|
118599
|
+
shell: process.platform === "win32",
|
|
118600
|
+
windowsHide: true
|
|
117863
118601
|
});
|
|
117864
118602
|
log10("[cli-agent] uninstalled");
|
|
117865
118603
|
return true;
|
|
@@ -120295,6 +121033,7 @@ var realHost = () => hostname5();
|
|
|
120295
121033
|
|
|
120296
121034
|
// ../../packages/host-core/dist/cli-link/install.js
|
|
120297
121035
|
import { chmodSync as chmodSync6, existsSync as existsSync40, mkdirSync as mkdirSync39, readFileSync as readFileSync53, writeFileSync as writeFileSync37 } from "node:fs";
|
|
121036
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
120298
121037
|
import { dirname as dirname33 } from "node:path";
|
|
120299
121038
|
|
|
120300
121039
|
// ../../packages/host-core/dist/cli-link/plan.js
|
|
@@ -120363,6 +121102,42 @@ function planCliLink(args) {
|
|
|
120363
121102
|
|
|
120364
121103
|
// ../../packages/host-core/dist/cli-link/install.js
|
|
120365
121104
|
init_patched_resources();
|
|
121105
|
+
function readUserPath() {
|
|
121106
|
+
const r = spawnSync3("reg", ["query", "HKCU\\Environment", "/v", "Path"], { encoding: "utf8", windowsHide: true });
|
|
121107
|
+
if (r.status !== 0 || !r.stdout)
|
|
121108
|
+
return "";
|
|
121109
|
+
const m = r.stdout.match(/^\s*Path\s+REG_(?:EXPAND_)?SZ\s+(.*)$/im);
|
|
121110
|
+
return m ? m[1].trim() : "";
|
|
121111
|
+
}
|
|
121112
|
+
function pathHasEntry(pathValue, dir2) {
|
|
121113
|
+
const norm = (s) => s.trim().replace(/\\+$/, "").toLowerCase();
|
|
121114
|
+
const want = norm(dir2);
|
|
121115
|
+
return pathValue.split(";").some((e) => e.trim() !== "" && norm(e) === want);
|
|
121116
|
+
}
|
|
121117
|
+
function broadcastEnvChange() {
|
|
121118
|
+
const ps2 = [
|
|
121119
|
+
`$s = '[DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)] public static extern IntPtr SendMessageTimeout(IntPtr hWnd, uint Msg, UIntPtr wParam, string lParam, uint fuFlags, uint uTimeout, out UIntPtr lpdwResult);';`,
|
|
121120
|
+
"$t = Add-Type -MemberDefinition $s -Name Win32Env -Namespace Tess -PassThru;",
|
|
121121
|
+
"$r = [UIntPtr]::Zero;",
|
|
121122
|
+
// HWND_BROADCAST=0xffff, WM_SETTINGCHANGE=0x1A, SMTO_ABORTIFHUNG=0x2, 5s timeout.
|
|
121123
|
+
"[void]$t::SendMessageTimeout([IntPtr]0xffff, 0x1A, [UIntPtr]::Zero, 'Environment', 0x2, 5000, [ref]$r);"
|
|
121124
|
+
].join(" ");
|
|
121125
|
+
try {
|
|
121126
|
+
spawnSync3("powershell", ["-NoProfile", "-NonInteractive", "-Command", ps2], { windowsHide: true, timeout: 15e3 });
|
|
121127
|
+
} catch {
|
|
121128
|
+
}
|
|
121129
|
+
}
|
|
121130
|
+
function ensureWindowsPathEntry(dir2) {
|
|
121131
|
+
const current = readUserPath();
|
|
121132
|
+
if (pathHasEntry(current, dir2))
|
|
121133
|
+
return { ok: true, added: false };
|
|
121134
|
+
const next = current ? `${current.replace(/;+$/, "")};${dir2}` : dir2;
|
|
121135
|
+
const w = spawnSync3("reg", ["add", "HKCU\\Environment", "/v", "Path", "/t", "REG_EXPAND_SZ", "/d", next, "/f"], { encoding: "utf8", windowsHide: true });
|
|
121136
|
+
if (w.status !== 0)
|
|
121137
|
+
return { ok: false, added: false, reason: (w.stderr || w.stdout || "reg add failed").trim() };
|
|
121138
|
+
broadcastEnvChange();
|
|
121139
|
+
return { ok: true, added: true };
|
|
121140
|
+
}
|
|
120366
121141
|
function installCliShims(args) {
|
|
120367
121142
|
const plan = planCliLink(args);
|
|
120368
121143
|
if (!plan)
|
|
@@ -120401,6 +121176,12 @@ function installCliShims(args) {
|
|
|
120401
121176
|
}
|
|
120402
121177
|
}
|
|
120403
121178
|
}
|
|
121179
|
+
if (plan.needsPathEntry && process.platform === "win32" && installed.length > 0) {
|
|
121180
|
+
const pathRes = ensureWindowsPathEntry(plan.dir);
|
|
121181
|
+
if (!pathRes.ok) {
|
|
121182
|
+
return { ok: false, dir: plan.dir, installed, skipped, reason: `shims written but PATH update failed: ${pathRes.reason ?? "unknown"}` };
|
|
121183
|
+
}
|
|
121184
|
+
}
|
|
120404
121185
|
return { ok: installed.length > 0, dir: plan.dir, installed, skipped };
|
|
120405
121186
|
}
|
|
120406
121187
|
function ensureCliOnPath(log10) {
|
|
@@ -121577,7 +122358,7 @@ function startRdeskChannelClient(deps) {
|
|
|
121577
122358
|
if (sock?.readyState === wrapper_default.OPEN)
|
|
121578
122359
|
sock.send(JSON.stringify({ channel: "rdeskhost", payload }));
|
|
121579
122360
|
};
|
|
121580
|
-
const
|
|
122361
|
+
const scheduleRetry2 = () => {
|
|
121581
122362
|
if (stopped2 || retry)
|
|
121582
122363
|
return;
|
|
121583
122364
|
retry = setTimeout(() => {
|
|
@@ -121591,7 +122372,7 @@ function startRdeskChannelClient(deps) {
|
|
|
121591
122372
|
return;
|
|
121592
122373
|
const url2 = readEndpoint(deps.daemonDir(), deps.selfPid);
|
|
121593
122374
|
if (!url2) {
|
|
121594
|
-
|
|
122375
|
+
scheduleRetry2();
|
|
121595
122376
|
return;
|
|
121596
122377
|
}
|
|
121597
122378
|
const ws = new wrapper_default(url2);
|
|
@@ -121646,7 +122427,7 @@ function startRdeskChannelClient(deps) {
|
|
|
121646
122427
|
return;
|
|
121647
122428
|
sock = null;
|
|
121648
122429
|
setHolding(false, "disconnected");
|
|
121649
|
-
|
|
122430
|
+
scheduleRetry2();
|
|
121650
122431
|
};
|
|
121651
122432
|
ws.on("close", drop);
|
|
121652
122433
|
ws.on("error", () => {
|
|
@@ -122281,11 +123062,28 @@ async function homeFor(hostId) {
|
|
|
122281
123062
|
}
|
|
122282
123063
|
return null;
|
|
122283
123064
|
}
|
|
122284
|
-
|
|
122285
|
-
|
|
122286
|
-
|
|
123065
|
+
function safeDecode3(s) {
|
|
123066
|
+
try {
|
|
123067
|
+
return decodeURIComponent(s);
|
|
123068
|
+
} catch {
|
|
122287
123069
|
return null;
|
|
122288
|
-
|
|
123070
|
+
}
|
|
123071
|
+
}
|
|
123072
|
+
function decodeSegs(urlPath) {
|
|
123073
|
+
const segs = [];
|
|
123074
|
+
for (const raw of urlPath.split("/").filter(Boolean)) {
|
|
123075
|
+
const d = safeDecode3(raw);
|
|
123076
|
+
if (d === null)
|
|
123077
|
+
return null;
|
|
123078
|
+
if (d === ".." || d === "." || /[\\/:\0]/.test(d))
|
|
123079
|
+
return null;
|
|
123080
|
+
segs.push(d);
|
|
123081
|
+
}
|
|
123082
|
+
return segs;
|
|
123083
|
+
}
|
|
123084
|
+
async function resolveTarget(urlPath) {
|
|
123085
|
+
const segs = decodeSegs(urlPath);
|
|
123086
|
+
if (!segs || segs.length === 0 || segs[0] !== token2)
|
|
122289
123087
|
return null;
|
|
122290
123088
|
const rest = segs.slice(1);
|
|
122291
123089
|
if (rest.length === 0)
|
|
@@ -122300,7 +123098,11 @@ async function resolveTarget(urlPath) {
|
|
|
122300
123098
|
if (!home)
|
|
122301
123099
|
return null;
|
|
122302
123100
|
const rel = rest.slice(1);
|
|
122303
|
-
|
|
123101
|
+
const base = home.replace(/\/+$/, "");
|
|
123102
|
+
const remote = `${base}/${rel.join("/")}`;
|
|
123103
|
+
if (remote !== base && !remote.startsWith(`${base}/`))
|
|
123104
|
+
return null;
|
|
123105
|
+
return { kind: "path", hostId, label, remote, rel };
|
|
122304
123106
|
}
|
|
122305
123107
|
async function remotePathOf(t) {
|
|
122306
123108
|
if (t.kind === "machine")
|
|
@@ -122322,10 +123124,8 @@ var href = (segs) => "/" + segs.map((s) => encodeURIComponent(s)).join("/");
|
|
|
122322
123124
|
function propfindXml(entries) {
|
|
122323
123125
|
const body = entries.map((e) => {
|
|
122324
123126
|
const mtime = new Date(e.mtimeMs ?? Date.now()).toUTCString();
|
|
122325
|
-
const iso = new Date(e.mtimeMs ?? Date.now()).toISOString();
|
|
122326
|
-
const name = e.segs[e.segs.length - 1] ?? "Tesseract";
|
|
122327
123127
|
const props = e.isDir ? `<D:resourcetype><D:collection/></D:resourcetype>` : `<D:resourcetype/><D:getcontentlength>${e.size ?? 0}</D:getcontentlength>`;
|
|
122328
|
-
return `<D:response><D:href>${xmlEscape(href(e.segs))}${e.isDir ? "/" : ""}</D:href><D:propstat><D:prop><D:
|
|
123128
|
+
return `<D:response><D:href>${xmlEscape(href(e.segs))}${e.isDir ? "/" : ""}</D:href><D:propstat><D:prop><D:getlastmodified>${mtime}</D:getlastmodified>` + props + `</D:prop><D:status>HTTP/1.1 200 OK</D:status></D:propstat></D:response>`;
|
|
122329
123129
|
}).join("");
|
|
122330
123130
|
return `<?xml version="1.0" encoding="utf-8"?><D:multistatus xmlns:D="DAV:">${body}</D:multistatus>`;
|
|
122331
123131
|
}
|
|
@@ -122517,7 +123317,7 @@ async function route2(req, res) {
|
|
|
122517
123317
|
send4(res, 404, "not found", DAV_HEADERS);
|
|
122518
123318
|
return;
|
|
122519
123319
|
}
|
|
122520
|
-
const urlSegs = urlPath
|
|
123320
|
+
const urlSegs = decodeSegs(urlPath) ?? [];
|
|
122521
123321
|
switch (method) {
|
|
122522
123322
|
case "PROPFIND":
|
|
122523
123323
|
await handlePropfind(req, res, t, urlSegs);
|
|
@@ -122595,7 +123395,7 @@ function bind(i) {
|
|
|
122595
123395
|
s.on("listening", () => {
|
|
122596
123396
|
server = s;
|
|
122597
123397
|
boundPort = port;
|
|
122598
|
-
log8(`file-share (WebDAV) listening on http://127.0.0.1:${port}
|
|
123398
|
+
log8(`file-share (WebDAV) listening on http://127.0.0.1:${port}/ (token redacted)`);
|
|
122599
123399
|
});
|
|
122600
123400
|
s.on("error", (e) => {
|
|
122601
123401
|
if (e.code === "EADDRINUSE") {
|
|
@@ -122648,6 +123448,15 @@ var run3 = promisify6(execFile10);
|
|
|
122648
123448
|
var OPTS = { timeout: 2e4, windowsHide: true };
|
|
122649
123449
|
var mounted = false;
|
|
122650
123450
|
var mountedAt = null;
|
|
123451
|
+
var RETRY_MS = 6e4;
|
|
123452
|
+
var MAX_ATTEMPTS = 5;
|
|
123453
|
+
var retryTimer = null;
|
|
123454
|
+
var attempts = 0;
|
|
123455
|
+
function redact(s) {
|
|
123456
|
+
const tok = fileShareToken();
|
|
123457
|
+
const out = tok ? s.split(tok).join("<token>") : s;
|
|
123458
|
+
return out.replace(/([/\\])[0-9a-f]{32}(?=[/\\"'\s]|$)/gi, "$1<token>");
|
|
123459
|
+
}
|
|
122651
123460
|
function davUri() {
|
|
122652
123461
|
const port = fileSharePort();
|
|
122653
123462
|
const token4 = fileShareToken();
|
|
@@ -122675,44 +123484,135 @@ async function mountLinux(log10) {
|
|
|
122675
123484
|
const msg = String(e.message || "");
|
|
122676
123485
|
if (/already mounted/i.test(msg)) {
|
|
122677
123486
|
} else if (/not found|ENOENT/i.test(msg)) {
|
|
122678
|
-
log10(`file-share: gio not available \u2014 install gvfs-backends to mount ${uri} in Files`);
|
|
123487
|
+
log10(`file-share: gio not available \u2014 install gvfs-backends to mount ${redact(uri)} in Files`);
|
|
122679
123488
|
return false;
|
|
122680
123489
|
} else if (/No such interface|not supported|dav/i.test(msg)) {
|
|
122681
|
-
log10(`file-share: GVfs has no dav backend \u2014 install gvfs-backends (${msg.split("\n")[0]})`);
|
|
123490
|
+
log10(`file-share: GVfs has no dav backend \u2014 install gvfs-backends (${redact(msg.split("\n")[0])})`);
|
|
122682
123491
|
return false;
|
|
122683
123492
|
} else {
|
|
122684
|
-
log10(`file-share: gio mount failed: ${msg.split("\n")[0]}`);
|
|
123493
|
+
log10(`file-share: gio mount failed: ${redact(msg.split("\n")[0])}`);
|
|
122685
123494
|
return false;
|
|
122686
123495
|
}
|
|
122687
123496
|
}
|
|
122688
123497
|
addGtkBookmark(uri, "Tesseract");
|
|
122689
123498
|
mountedAt = uri;
|
|
122690
|
-
log10(`file-share: mounted in Files (GVfs) \u2192 ${uri}`);
|
|
123499
|
+
log10(`file-share: mounted in Files (GVfs) \u2192 ${redact(uri)}`);
|
|
122691
123500
|
return true;
|
|
122692
123501
|
}
|
|
123502
|
+
function runWin(cmd, args) {
|
|
123503
|
+
return new Promise((resolve16) => {
|
|
123504
|
+
execFile10(cmd, args, { ...OPTS, encoding: "buffer", maxBuffer: 8 * 1024 * 1024 }, (err2, stdout, stderr) => {
|
|
123505
|
+
const e = err2;
|
|
123506
|
+
const combined = Buffer.concat([stderr ?? Buffer.alloc(0), stdout ?? Buffer.alloc(0)]).toString("latin1").trim();
|
|
123507
|
+
const code = e && typeof e.code === "number" ? e.code : null;
|
|
123508
|
+
const out = combined || (e && typeof e.code === "string" ? e.code : "");
|
|
123509
|
+
resolve16({ ok: !e, code, out });
|
|
123510
|
+
});
|
|
123511
|
+
});
|
|
123512
|
+
}
|
|
123513
|
+
function ourUnc(port) {
|
|
123514
|
+
return `\\\\127.0.0.1@${port}\\${fileShareToken()}`.toLowerCase();
|
|
123515
|
+
}
|
|
123516
|
+
async function mappedDisks() {
|
|
123517
|
+
const r = await runWin("powershell.exe", [
|
|
123518
|
+
"-NoProfile",
|
|
123519
|
+
"-NonInteractive",
|
|
123520
|
+
"-Command",
|
|
123521
|
+
'Get-CimInstance Win32_MappedLogicalDisk | ForEach-Object { "$($_.DeviceID)`t$($_.ProviderName)" }'
|
|
123522
|
+
]);
|
|
123523
|
+
if (!r.ok)
|
|
123524
|
+
return [];
|
|
123525
|
+
const out = [];
|
|
123526
|
+
for (const line of r.out.split(/\r?\n/)) {
|
|
123527
|
+
const tab = line.indexOf(" ");
|
|
123528
|
+
if (tab < 0)
|
|
123529
|
+
continue;
|
|
123530
|
+
const device = line.slice(0, tab).trim();
|
|
123531
|
+
const provider = line.slice(tab + 1).trim();
|
|
123532
|
+
if (/^[A-Za-z]:$/.test(device))
|
|
123533
|
+
out.push({ device, provider });
|
|
123534
|
+
}
|
|
123535
|
+
return out;
|
|
123536
|
+
}
|
|
123537
|
+
async function usedDriveLetters() {
|
|
123538
|
+
const used = /* @__PURE__ */ new Set();
|
|
123539
|
+
const r = await runWin("powershell.exe", [
|
|
123540
|
+
"-NoProfile",
|
|
123541
|
+
"-NonInteractive",
|
|
123542
|
+
"-Command",
|
|
123543
|
+
'(Get-PSDrive -PSProvider FileSystem | Select-Object -ExpandProperty Name) -join ","'
|
|
123544
|
+
]);
|
|
123545
|
+
if (r.ok)
|
|
123546
|
+
for (const n of r.out.split(",")) {
|
|
123547
|
+
const L = n.trim().toUpperCase();
|
|
123548
|
+
if (/^[A-Z]$/.test(L))
|
|
123549
|
+
used.add(L);
|
|
123550
|
+
}
|
|
123551
|
+
return used;
|
|
123552
|
+
}
|
|
123553
|
+
function pickFreeLetter(used) {
|
|
123554
|
+
for (let c = "Z".charCodeAt(0); c >= "D".charCodeAt(0); c--) {
|
|
123555
|
+
const L = String.fromCharCode(c);
|
|
123556
|
+
if (!used.has(L))
|
|
123557
|
+
return L;
|
|
123558
|
+
}
|
|
123559
|
+
return null;
|
|
123560
|
+
}
|
|
122693
123561
|
async function mountWindows(log10) {
|
|
122694
123562
|
const url2 = fileShareUrl();
|
|
122695
|
-
|
|
123563
|
+
const port = fileSharePort();
|
|
123564
|
+
if (!url2 || !port)
|
|
122696
123565
|
return false;
|
|
122697
|
-
|
|
122698
|
-
|
|
122699
|
-
|
|
122700
|
-
|
|
122701
|
-
|
|
122702
|
-
|
|
122703
|
-
|
|
123566
|
+
const disks = await mappedDisks();
|
|
123567
|
+
const want = ourUnc(port);
|
|
123568
|
+
const tokenLc = fileShareToken().toLowerCase();
|
|
123569
|
+
let reuse = null;
|
|
123570
|
+
for (const d of disks) {
|
|
123571
|
+
const prov = d.provider.toLowerCase();
|
|
123572
|
+
const isOurs2 = prov.startsWith("\\\\127.0.0.1@") && prov.includes(`\\${tokenLc}`);
|
|
123573
|
+
if (!isOurs2)
|
|
123574
|
+
continue;
|
|
123575
|
+
if (prov === want || prov === `${want}\\`) {
|
|
123576
|
+
reuse = d.device;
|
|
123577
|
+
continue;
|
|
123578
|
+
}
|
|
123579
|
+
const del = await runWin("net", ["use", d.device, "/delete", "/y"]);
|
|
123580
|
+
log10(del.ok ? `file-share: removed a stale mapping on ${d.device}` : `file-share: could not remove stale mapping on ${d.device} (${redact(del.out) || `exit ${del.code}`})`);
|
|
122704
123581
|
}
|
|
122705
|
-
|
|
122706
|
-
|
|
122707
|
-
|
|
122708
|
-
mountedAt = drive ?? url2;
|
|
122709
|
-
log10(`file-share: mapped in Explorer \u2192 ${mountedAt}`);
|
|
123582
|
+
if (reuse) {
|
|
123583
|
+
mountedAt = reuse;
|
|
123584
|
+
log10(`file-share: reusing existing mapping on ${reuse}`);
|
|
122710
123585
|
return true;
|
|
122711
|
-
}
|
|
122712
|
-
|
|
122713
|
-
|
|
123586
|
+
}
|
|
123587
|
+
const used = await usedDriveLetters();
|
|
123588
|
+
for (const d of disks)
|
|
123589
|
+
used.add(d.device[0].toUpperCase());
|
|
123590
|
+
const letter = pickFreeLetter(used);
|
|
123591
|
+
if (!letter) {
|
|
123592
|
+
log10("file-share: no free drive letter to map the share");
|
|
122714
123593
|
return false;
|
|
122715
123594
|
}
|
|
123595
|
+
const res = await runWin("net", ["use", `${letter}:`, url2, "/persistent:no"]);
|
|
123596
|
+
if (res.ok) {
|
|
123597
|
+
mountedAt = `${letter}:`;
|
|
123598
|
+
log10(`file-share: mapped in Explorer \u2192 ${letter}:`);
|
|
123599
|
+
return true;
|
|
123600
|
+
}
|
|
123601
|
+
const detail = redact(res.out).split(/\r?\n/).map((l) => l.trim()).filter(Boolean).join(" ");
|
|
123602
|
+
const sysErr = /System error (\d+)/i.exec(res.out)?.[1] ?? null;
|
|
123603
|
+
const hint = sysErr === "1058" ? " \u2014 the WebClient service is disabled; enable it (services.msc \u2192 WebClient, Manual/Automatic) so `net use` can trigger-start it" : sysErr === "53" || sysErr === "67" || sysErr === "1231" || sysErr === "3" ? " \u2014 WebClient could not be reached; it may be stopped or absent (net use normally trigger-starts it)" : "";
|
|
123604
|
+
log10(`file-share: net use failed (exit ${res.code}${sysErr ? `, System error ${sysErr}` : ""}: ${detail || "no output"})${hint}`);
|
|
123605
|
+
return false;
|
|
123606
|
+
}
|
|
123607
|
+
function scheduleRetry(log10) {
|
|
123608
|
+
if (mounted || retryTimer || attempts >= MAX_ATTEMPTS)
|
|
123609
|
+
return;
|
|
123610
|
+
attempts++;
|
|
123611
|
+
retryTimer = setTimeout(() => {
|
|
123612
|
+
retryTimer = null;
|
|
123613
|
+
void mountFileShare(log10);
|
|
123614
|
+
}, RETRY_MS);
|
|
123615
|
+
retryTimer.unref?.();
|
|
122716
123616
|
}
|
|
122717
123617
|
async function mountFileShare(log10 = () => {
|
|
122718
123618
|
}) {
|
|
@@ -122724,18 +123624,34 @@ async function mountFileShare(log10 = () => {
|
|
|
122724
123624
|
return false;
|
|
122725
123625
|
const ok = process.platform === "linux" ? await mountLinux(log10) : process.platform === "win32" ? await mountWindows(log10) : false;
|
|
122726
123626
|
mounted = ok;
|
|
123627
|
+
if (ok) {
|
|
123628
|
+
attempts = 0;
|
|
123629
|
+
if (retryTimer) {
|
|
123630
|
+
clearTimeout(retryTimer);
|
|
123631
|
+
retryTimer = null;
|
|
123632
|
+
}
|
|
123633
|
+
} else
|
|
123634
|
+
scheduleRetry(log10);
|
|
122727
123635
|
return ok;
|
|
122728
123636
|
}
|
|
122729
123637
|
async function unmountFileShare(log10 = () => {
|
|
122730
123638
|
}) {
|
|
122731
|
-
if (
|
|
123639
|
+
if (retryTimer) {
|
|
123640
|
+
clearTimeout(retryTimer);
|
|
123641
|
+
retryTimer = null;
|
|
123642
|
+
}
|
|
123643
|
+
attempts = 0;
|
|
123644
|
+
if (!mounted || !mountedAt) {
|
|
123645
|
+
mounted = false;
|
|
122732
123646
|
return;
|
|
123647
|
+
}
|
|
123648
|
+
const at = mountedAt;
|
|
122733
123649
|
try {
|
|
122734
123650
|
if (process.platform === "linux")
|
|
122735
|
-
await run3("gio", ["mount", "-u",
|
|
123651
|
+
await run3("gio", ["mount", "-u", at], OPTS);
|
|
122736
123652
|
else if (process.platform === "win32")
|
|
122737
|
-
await run3("net", ["use",
|
|
122738
|
-
log10(`file-share: unmounted ${
|
|
123653
|
+
await run3("net", ["use", at, "/delete", "/y"], OPTS);
|
|
123654
|
+
log10(`file-share: unmounted ${redact(at)}`);
|
|
122739
123655
|
} catch {
|
|
122740
123656
|
}
|
|
122741
123657
|
mounted = false;
|
|
@@ -123115,9 +124031,10 @@ function writeLauncher(daemonDir3, bridge, runtime) {
|
|
|
123115
124031
|
const launcher = join89(daemonDir3, isWin ? "companion-host.cmd" : "companion-host.sh");
|
|
123116
124032
|
if (isWin) {
|
|
123117
124033
|
writeFileSync41(launcher, `@echo off\r
|
|
124034
|
+
chcp 65001>nul\r
|
|
123118
124035
|
set ELECTRON_RUN_AS_NODE=1\r
|
|
123119
124036
|
"${runtime}" "${bridge}" %*\r
|
|
123120
|
-
|
|
124037
|
+
`, { encoding: "utf8" });
|
|
123121
124038
|
} else {
|
|
123122
124039
|
writeFileSync41(launcher, `#!/bin/sh
|
|
123123
124040
|
# Tesseract Companion native-messaging launcher (auto-written by the daemon).
|
|
@@ -123170,7 +124087,7 @@ function registerWindows(daemonDir3, manifestJson, log10) {
|
|
|
123170
124087
|
writeFileSync41(manifestPath, manifestJson);
|
|
123171
124088
|
for (const root of ["Software\\Google\\Chrome", "Software\\Microsoft\\Edge", "Software\\BraveSoftware\\Brave-Browser"]) {
|
|
123172
124089
|
try {
|
|
123173
|
-
execFileSync5("reg", ["add", `HKCU\\${root}\\NativeMessagingHosts\\${HOST_NAME}`, "/ve", "/t", "REG_SZ", "/d", manifestPath, "/f"], { stdio: "ignore" });
|
|
124090
|
+
execFileSync5("reg", ["add", `HKCU\\${root}\\NativeMessagingHosts\\${HOST_NAME}`, "/ve", "/t", "REG_SZ", "/d", manifestPath, "/f"], { stdio: "ignore", windowsHide: true });
|
|
123174
124091
|
} catch {
|
|
123175
124092
|
}
|
|
123176
124093
|
}
|
|
@@ -123228,6 +124145,37 @@ function loadMacAx2() {
|
|
|
123228
124145
|
})();
|
|
123229
124146
|
return macAxPromise2;
|
|
123230
124147
|
}
|
|
124148
|
+
var windowingPromise;
|
|
124149
|
+
function loadWindowing() {
|
|
124150
|
+
if (!windowingPromise)
|
|
124151
|
+
windowingPromise = loadLibnutWindowing();
|
|
124152
|
+
return windowingPromise;
|
|
124153
|
+
}
|
|
124154
|
+
function appNameFromWindowTitle(title) {
|
|
124155
|
+
const parts = title.split(/\s+[-—|]\s+/).map((s) => s.trim()).filter(Boolean);
|
|
124156
|
+
return parts.length ? parts[parts.length - 1] : title.trim();
|
|
124157
|
+
}
|
|
124158
|
+
async function detectFrontmost() {
|
|
124159
|
+
if (process.platform === "darwin") {
|
|
124160
|
+
const ax = await loadMacAx2();
|
|
124161
|
+
return ax?.frontmostApp() ?? null;
|
|
124162
|
+
}
|
|
124163
|
+
const win3 = await loadWindowing();
|
|
124164
|
+
if (!win3)
|
|
124165
|
+
return null;
|
|
124166
|
+
try {
|
|
124167
|
+
const handle3 = win3.getActiveWindow();
|
|
124168
|
+
if (!handle3 || handle3 <= 0)
|
|
124169
|
+
return null;
|
|
124170
|
+
const title = (win3.getWindowTitle(handle3) ?? "").trim();
|
|
124171
|
+
if (!title)
|
|
124172
|
+
return null;
|
|
124173
|
+
const name = appNameFromWindowTitle(title);
|
|
124174
|
+
return name ? { name, bundleId: "", pid: handle3 } : null;
|
|
124175
|
+
} catch {
|
|
124176
|
+
return null;
|
|
124177
|
+
}
|
|
124178
|
+
}
|
|
123231
124179
|
function createActivityTracker(opts = {}) {
|
|
123232
124180
|
const intervalMs = opts.intervalMs ?? 5e3;
|
|
123233
124181
|
const flushEvery = opts.flushEvery ?? 12;
|
|
@@ -123257,8 +124205,7 @@ function createActivityTracker(opts = {}) {
|
|
|
123257
124205
|
}
|
|
123258
124206
|
async function sample() {
|
|
123259
124207
|
try {
|
|
123260
|
-
const
|
|
123261
|
-
const front = ax?.frontmostApp() ?? null;
|
|
124208
|
+
const front = await detectFrontmost();
|
|
123262
124209
|
if (!front || !front.name)
|
|
123263
124210
|
return;
|
|
123264
124211
|
const key2 = front.bundleId || front.name;
|
|
@@ -123430,7 +124377,7 @@ function writeJsonId(file2, deviceId3) {
|
|
|
123430
124377
|
}
|
|
123431
124378
|
function haveTool(bin) {
|
|
123432
124379
|
try {
|
|
123433
|
-
execFileSync6(process.platform === "win32" ? "where" : "which", [bin], { timeout: TOOL_TIMEOUT_MS, stdio: "pipe" });
|
|
124380
|
+
execFileSync6(process.platform === "win32" ? "where" : "which", [bin], { timeout: TOOL_TIMEOUT_MS, stdio: "pipe", windowsHide: true });
|
|
123434
124381
|
return true;
|
|
123435
124382
|
} catch {
|
|
123436
124383
|
return false;
|
|
@@ -123672,6 +124619,14 @@ function backoff2() {
|
|
|
123672
124619
|
// ../../packages/host-core/dist/daemon-runtime.js
|
|
123673
124620
|
var daemonClipboardSync = null;
|
|
123674
124621
|
var REFRESH_INTERVAL_MS2 = 12 * 60 * 60 * 1e3;
|
|
124622
|
+
function releaseDaemonLock() {
|
|
124623
|
+
try {
|
|
124624
|
+
const f = join93(daemonDir2(), "daemon.lock");
|
|
124625
|
+
if (existsSync50(f) && Number(readFileSync60(f, "utf-8").trim()) === process.pid)
|
|
124626
|
+
unlinkSync8(f);
|
|
124627
|
+
} catch {
|
|
124628
|
+
}
|
|
124629
|
+
}
|
|
123675
124630
|
async function runDaemon(opts = {}) {
|
|
123676
124631
|
const VERSION2 = opts.version ?? process.env.CCB_DAEMON_VERSION ?? "dev";
|
|
123677
124632
|
const TAG = opts.tag ?? "host-daemon";
|
|
@@ -123692,10 +124647,31 @@ async function runDaemon(opts = {}) {
|
|
|
123692
124647
|
return false;
|
|
123693
124648
|
}
|
|
123694
124649
|
};
|
|
124650
|
+
const readOwnerStatus = () => {
|
|
124651
|
+
try {
|
|
124652
|
+
const s = JSON.parse(readFileSync60(statusFile2(), "utf-8"));
|
|
124653
|
+
const pid = Number(s.pid);
|
|
124654
|
+
if (!Number.isInteger(pid) || pid <= 0 || s.stopped === true)
|
|
124655
|
+
return null;
|
|
124656
|
+
return { pid, startedAt: typeof s.startedAt === "string" ? s.startedAt : void 0 };
|
|
124657
|
+
} catch {
|
|
124658
|
+
return null;
|
|
124659
|
+
}
|
|
124660
|
+
};
|
|
124661
|
+
const lockOwnerIsLive = (pid) => {
|
|
124662
|
+
if (!isAlive2(pid))
|
|
124663
|
+
return false;
|
|
124664
|
+
if (pid === process.pid)
|
|
124665
|
+
return true;
|
|
124666
|
+
const owner = readOwnerStatus();
|
|
124667
|
+
if (owner && owner.pid === pid && owner.startedAt)
|
|
124668
|
+
return pidStillMatches(pid, { startedAt: owner.startedAt });
|
|
124669
|
+
return true;
|
|
124670
|
+
};
|
|
123695
124671
|
const lockHeldByAnother = () => {
|
|
123696
124672
|
try {
|
|
123697
124673
|
const pid = Number(readFileSync60(pidfile2(), "utf-8").trim());
|
|
123698
|
-
return Number.isFinite(pid) && pid > 0 && pid !== process.pid &&
|
|
124674
|
+
return Number.isFinite(pid) && pid > 0 && pid !== process.pid && lockOwnerIsLive(pid);
|
|
123699
124675
|
} catch {
|
|
123700
124676
|
return false;
|
|
123701
124677
|
}
|
|
@@ -123703,7 +124679,7 @@ async function runDaemon(opts = {}) {
|
|
|
123703
124679
|
const lockHeldByYieldingSubset = () => {
|
|
123704
124680
|
try {
|
|
123705
124681
|
const pid = Number(readFileSync60(pidfile2(), "utf-8").trim());
|
|
123706
|
-
if (!Number.isFinite(pid) || pid <= 0 || pid === process.pid || !
|
|
124682
|
+
if (!Number.isFinite(pid) || pid <= 0 || pid === process.pid || !lockOwnerIsLive(pid))
|
|
123707
124683
|
return false;
|
|
123708
124684
|
return liveDaemons().some((d) => d.pid === pid && d.rdeskOnly === true);
|
|
123709
124685
|
} catch {
|
|
@@ -123726,7 +124702,7 @@ async function runDaemon(opts = {}) {
|
|
|
123726
124702
|
const prev = Number(readFileSync60(f, "utf-8").trim());
|
|
123727
124703
|
if (!prev)
|
|
123728
124704
|
return false;
|
|
123729
|
-
if (prev !== process.pid &&
|
|
124705
|
+
if (prev !== process.pid && lockOwnerIsLive(prev))
|
|
123730
124706
|
return false;
|
|
123731
124707
|
unlinkSync8(f);
|
|
123732
124708
|
} catch {
|
|
@@ -123736,14 +124712,7 @@ async function runDaemon(opts = {}) {
|
|
|
123736
124712
|
}
|
|
123737
124713
|
return false;
|
|
123738
124714
|
}
|
|
123739
|
-
|
|
123740
|
-
try {
|
|
123741
|
-
const f = pidfile2();
|
|
123742
|
-
if (existsSync50(f) && Number(readFileSync60(f, "utf-8").trim()) === process.pid)
|
|
123743
|
-
unlinkSync8(f);
|
|
123744
|
-
} catch {
|
|
123745
|
-
}
|
|
123746
|
-
}
|
|
124715
|
+
const releaseLock = () => releaseDaemonLock();
|
|
123747
124716
|
const WE_ARE_RDESK_ONLY = process.env.CCB_RDESK_ONLY === "1" || process.env.CCB_MESH_ONLY === "1";
|
|
123748
124717
|
function writeStatus2(s) {
|
|
123749
124718
|
if (!ownsMachine)
|
|
@@ -124024,6 +124993,10 @@ async function runDaemon(opts = {}) {
|
|
|
124024
124993
|
}
|
|
124025
124994
|
if (mergedProcess) {
|
|
124026
124995
|
if (deliberateMergedQuit()) {
|
|
124996
|
+
try {
|
|
124997
|
+
releaseLock();
|
|
124998
|
+
} catch {
|
|
124999
|
+
}
|
|
124027
125000
|
try {
|
|
124028
125001
|
log10("APP before-quit \u2014 allowed (merged: deliberate quit)");
|
|
124029
125002
|
} catch {
|
|
@@ -124647,7 +125620,7 @@ async function runDaemon(opts = {}) {
|
|
|
124647
125620
|
}
|
|
124648
125621
|
const holder = lockHolder();
|
|
124649
125622
|
const fullDaemonAlive = liveDaemons().some((d) => d.pid !== process.pid && d.rdeskOnly !== true);
|
|
124650
|
-
if (holder > 0 &&
|
|
125623
|
+
if (holder > 0 && lockOwnerIsLive(holder) || fullDaemonAlive) {
|
|
124651
125624
|
freeTicks = 0;
|
|
124652
125625
|
return;
|
|
124653
125626
|
}
|
|
@@ -125081,7 +126054,7 @@ import { dirname as dirname43, join as join97 } from "node:path";
|
|
|
125081
126054
|
import { promisify as promisify12 } from "node:util";
|
|
125082
126055
|
|
|
125083
126056
|
// ../../packages/host-core/dist/net-service/launchd-system.js
|
|
125084
|
-
import { spawn as
|
|
126057
|
+
import { spawn as spawn11 } from "node:child_process";
|
|
125085
126058
|
import { existsSync as existsSync52, mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync46, rmSync as rmSync11, readFileSync as readFileSync61 } from "node:fs";
|
|
125086
126059
|
import { tmpdir as tmpdir6 } from "node:os";
|
|
125087
126060
|
import { join as join95 } from "node:path";
|
|
@@ -125249,7 +126222,7 @@ ${args}
|
|
|
125249
126222
|
}
|
|
125250
126223
|
function runForCode(cmd, args) {
|
|
125251
126224
|
return new Promise((resolve16) => {
|
|
125252
|
-
const p =
|
|
126225
|
+
const p = spawn11(cmd, args, { stdio: "inherit" });
|
|
125253
126226
|
p.on("error", () => resolve16(127));
|
|
125254
126227
|
p.on("close", (code) => resolve16(code ?? 1));
|
|
125255
126228
|
});
|
|
@@ -125352,16 +126325,16 @@ ${plist}TESSERACT_PLIST_EOF`,
|
|
|
125352
126325
|
}
|
|
125353
126326
|
|
|
125354
126327
|
// ../../packages/host-core/dist/net-service/systemd-system.js
|
|
125355
|
-
import { spawn as
|
|
126328
|
+
import { spawn as spawn13 } from "node:child_process";
|
|
125356
126329
|
import { existsSync as existsSync53, readFileSync as readFileSync62 } from "node:fs";
|
|
125357
126330
|
|
|
125358
126331
|
// ../../packages/host-core/dist/service/exec.js
|
|
125359
|
-
import { execFile as execFile13, spawn as
|
|
126332
|
+
import { execFile as execFile13, spawn as spawn12 } from "node:child_process";
|
|
125360
126333
|
import { promisify as promisify9 } from "node:util";
|
|
125361
126334
|
var run6 = promisify9(execFile13);
|
|
125362
126335
|
function runInherit(cmd, args) {
|
|
125363
126336
|
return new Promise((resolve16) => {
|
|
125364
|
-
const p =
|
|
126337
|
+
const p = spawn12(cmd, args, { stdio: "inherit" });
|
|
125365
126338
|
p.on("error", () => resolve16());
|
|
125366
126339
|
p.on("close", () => resolve16());
|
|
125367
126340
|
});
|
|
@@ -125370,7 +126343,7 @@ function runInherit(cmd, args) {
|
|
|
125370
126343
|
// ../../packages/host-core/dist/net-service/systemd-system.js
|
|
125371
126344
|
function runForCode2(cmd, args) {
|
|
125372
126345
|
return new Promise((resolve16) => {
|
|
125373
|
-
const p =
|
|
126346
|
+
const p = spawn13(cmd, args, { stdio: "inherit" });
|
|
125374
126347
|
p.on("error", () => resolve16(127));
|
|
125375
126348
|
p.on("close", (code) => resolve16(code ?? 1));
|
|
125376
126349
|
});
|
|
@@ -125556,7 +126529,7 @@ function sh2(s) {
|
|
|
125556
126529
|
}
|
|
125557
126530
|
|
|
125558
126531
|
// ../../packages/host-core/dist/net-service/windows-service.js
|
|
125559
|
-
import { execFile as execFile14, spawn as
|
|
126532
|
+
import { execFile as execFile14, spawn as spawn14 } from "node:child_process";
|
|
125560
126533
|
import { mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync47, rmSync as rmSync12 } from "node:fs";
|
|
125561
126534
|
import { tmpdir as tmpdir7 } from "node:os";
|
|
125562
126535
|
import { join as join96, win32 } from "node:path";
|
|
@@ -125599,7 +126572,7 @@ async function elevatePowerShell(script) {
|
|
|
125599
126572
|
writeFileSync47(inner, script, { encoding: "utf8" });
|
|
125600
126573
|
if (await alreadyElevated()) {
|
|
125601
126574
|
return new Promise((resolve16) => {
|
|
125602
|
-
const p =
|
|
126575
|
+
const p = spawn14("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", inner], { stdio: "inherit" });
|
|
125603
126576
|
const finish2 = (code) => {
|
|
125604
126577
|
try {
|
|
125605
126578
|
rmSync12(dir2, { recursive: true, force: true });
|
|
@@ -125622,7 +126595,7 @@ async function elevatePowerShell(script) {
|
|
|
125622
126595
|
"}"
|
|
125623
126596
|
].join("\n"), { encoding: "utf8" });
|
|
125624
126597
|
return new Promise((resolve16) => {
|
|
125625
|
-
const p =
|
|
126598
|
+
const p = spawn14("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", outer], { stdio: "inherit" });
|
|
125626
126599
|
const finish2 = (code) => {
|
|
125627
126600
|
try {
|
|
125628
126601
|
rmSync12(dir2, { recursive: true, force: true });
|
|
@@ -126746,7 +127719,7 @@ function whoami() {
|
|
|
126746
127719
|
}
|
|
126747
127720
|
|
|
126748
127721
|
// src/self-update.ts
|
|
126749
|
-
import { spawn as
|
|
127722
|
+
import { spawn as spawn15 } from "node:child_process";
|
|
126750
127723
|
import { createPublicKey, verify as cryptoVerify } from "node:crypto";
|
|
126751
127724
|
var RESTART_EXIT_CODE = 75;
|
|
126752
127725
|
var REGISTRY_BASE = "https://registry.npmjs.org/tesseract-agent";
|
|
@@ -126821,7 +127794,7 @@ function installVersion(target) {
|
|
|
126821
127794
|
log9(`installing tesseract-agent@${target} \u2026`);
|
|
126822
127795
|
let child;
|
|
126823
127796
|
try {
|
|
126824
|
-
child =
|
|
127797
|
+
child = spawn15(npm, ["install", "-g", `tesseract-agent@${target}`], { stdio: ["ignore", "inherit", "inherit"], shell: process.platform === "win32", windowsHide: true });
|
|
126825
127798
|
} catch (e) {
|
|
126826
127799
|
log9("npm spawn threw:", e instanceof Error ? e.message : String(e));
|
|
126827
127800
|
resolve16(false);
|
|
@@ -127909,7 +128882,7 @@ async function superviseDaemon(cliEntry) {
|
|
|
127909
128882
|
let stopping3 = false;
|
|
127910
128883
|
let child = null;
|
|
127911
128884
|
const spawnWorker = () => {
|
|
127912
|
-
const c =
|
|
128885
|
+
const c = spawn16(process.execPath, [cliEntry, "__worker"], { stdio: "inherit", env: process.env });
|
|
127913
128886
|
child = c;
|
|
127914
128887
|
let settled = false;
|
|
127915
128888
|
const settle2 = (fn) => {
|
|
@@ -128090,9 +129063,11 @@ async function captureClaudeSubscription(flags) {
|
|
|
128090
129063
|
console.log("Open the authorization URL in any browser; paste the returned code here if Claude asks for it.");
|
|
128091
129064
|
try {
|
|
128092
129065
|
const code = await new Promise((resolveCode, reject) => {
|
|
128093
|
-
const child =
|
|
129066
|
+
const child = spawn16(binary, args, {
|
|
128094
129067
|
env: { ...process.env, CLAUDE_CONFIG_DIR: home },
|
|
128095
|
-
stdio: "inherit"
|
|
129068
|
+
stdio: "inherit",
|
|
129069
|
+
// An npm-installed engine CLI is a .cmd shim on Windows — spawn via a shell or Node >=18.20.2 throws EINVAL.
|
|
129070
|
+
shell: process.platform === "win32" && /\.(cmd|bat)$/i.test(binary)
|
|
128096
129071
|
});
|
|
128097
129072
|
child.once("error", reject);
|
|
128098
129073
|
child.once("close", (exitCode, signal2) => resolveCode(signal2 ? 1 : exitCode ?? 1));
|